Manic Internals
Deep dive into how Manic loads configuration, discovers routes, and generates the route manifest.
Manic Internals
TL;DR
Understanding how Manic processes configuration, discovers routes, and generates the ~routes.generated.ts manifest that powers client-side routing.
What It Is
Manic's internals consist of three main systems:
| System | Purpose | Key File |
|---|---|---|
| Config Loading | Load and validate manic.config.ts | packages/manic/src/config/ |
| Route Discovery | Scan app/routes/ for routes | packages/manic/src/server/lib/discovery.ts |
| Manifest Generation | Create ~routes.generated.ts | packages/manic/src/cli/ |
Prerequisites
- Getting Started - Basic setup
- Config Reference - Configuration options
- Routing Guide - Route concepts
How It Works
Config Loading Flow
Route Discovery Flow
Config Loading
How manic.config.ts Loads
// Simplified loadConfig() implementation
// (see packages/manic/src/config/index.ts)
async function loadConfig(cwd = process.cwd()): Promise<ManicConfig> {
for (const file of ['manic.config.ts', 'manic.config.js']) {
const path = `${cwd}/${file}`;
if (await Bun.file(path).exists()) {
const mod = await import(path);
const userConfig = mod.default ?? mod;
return {
mode: userConfig.mode ?? DEFAULT_CONFIG.mode,
app: { ...DEFAULT_CONFIG.app, ...userConfig.app },
server: { ...DEFAULT_CONFIG.server, ...userConfig.server },
router: { ...DEFAULT_CONFIG.router, ...userConfig.router },
build: { ...DEFAULT_CONFIG.build, ...userConfig.build },
sitemap: userConfig.sitemap === false ? false : userConfig.sitemap,
oxc: { ...DEFAULT_CONFIG.oxc, ...userConfig.oxc },
providers: userConfig.providers,
plugins: userConfig.plugins,
};
}
}
return DEFAULT_CONFIG;
}Defaults & Merging
There is no Zod or runtime schema validator — Manic relies on TypeScript for validation at the call site of defineConfig. At load time, the user object is shallow-merged field by field with DEFAULT_CONFIG. Nested objects (app, server, router, build, oxc) are spread so user values override defaults without losing siblings.
The loaded config is cached. Subsequent loadConfig() calls within the same process return the cached value — important because every plugin context reads from the same instance.
Route Discovery
Process Overview
Scoring Algorithm
Routes are scored based on segment type:
| Segment Type | Score | Example |
|---|---|---|
| Static | +100 | about.tsx |
| Dynamic | +10 | [id].tsx |
| Catch-all | +1 | [...slug].tsx |
Example: /posts/new
posts/new.tsx [200] ← wins (static)
posts/[id].tsx [110]
posts/[...slug].tsx [101]Manifest Generation
~routes.generated.ts Structure
// DO NOT EDIT — regenerated on every dev start and build.
export const routes = {
'/': () => import('./routes/index.tsx'),
'/about': () => import('./routes/about.tsx'),
'/posts': () => import('./routes/posts/index.tsx'),
'/posts/:id': () => import('./routes/posts/[id].tsx'),
'/docs/:...slug': () => import('./routes/docs/[...slug].tsx'),
};
export const notFoundPage = () => import('./routes/~404.tsx');
export const errorPage = () => import('./routes/~500.tsx');The client <Router> reads these from window.__MANIC_ROUTES__ and window.__MANIC_ERROR_PAGES__, which are populated by the entry script during application boot.
Generation Process
Type Definitions
Discovered Route
import type { PageRoute, ApiRoute } from 'manicjs/config';
interface PageRoute {
path: string; // URL pattern, e.g. "/posts/:id"
filePath: string; // Source file path
dynamic: boolean; // True when the path contains parameters
}
interface ApiRoute {
mountPath: string; // Mount point, e.g. "/api/users"
filePath: string;
}Config Type (Excerpt)
The full ManicConfig shape is exported from manicjs/config. The excerpt below covers the fields you most commonly customize — see the Configuration Reference for everything.
interface ManicConfig {
mode?: 'fullstack' | 'frontend';
app?: { name?: string };
server?: { port?: number; hmr?: boolean };
router?: {
viewTransitions?: boolean;
preserveScroll?: boolean;
scrollBehavior?: 'auto' | 'smooth';
};
build?: {
minify?: boolean;
sourcemap?: boolean | 'inline' | 'external';
splitting?: boolean;
outdir?: string;
};
oxc?: {
target?: string;
rewriteImportExtensions?: boolean;
refresh?: boolean;
};
providers?: ManicProvider[];
plugins?: ManicPlugin[];
}Debugging
Enable Debug Logging
DEBUG=manic:* bun devCheck Generated Routes
The generated routes file is in your app directory:
cat app/~routes.generated.tsConfig Validation Errors
// manic.config.ts must export default
export default defineConfig({ ... });Common Issues
Issue 1: Config Not Found
manic.config.ts not found at project root.
Solution:
# Ensure manic.config.ts is in project root
ls -la manic.config.tsIssue 2: Route Not Discovered
Solution: Check file location:
✓ app/routes/index.tsx → discovered
✗ app/pages/index.tsx → not in routes folderIssue 3: Manifest Outdated
Solution: Restart dev server to regenerate:
# Stop server and restart
bun devBest Practices
Don't edit ~routes.generated.ts - it's auto-generated.
Keep config at project root for discovery.
Use debug logging to troubleshoot discovery issues.
See also: