The routing engine/Virtual modules

Virtual modules

Generated entry points and lazy route loading.

The Generator

Once the RouteManifestNode tree is built, BionicJS's generator steps in. It traverses the AST and produces a JavaScript module string - not a binary bundle, not a compiled artifact, but a string of source code that Vite consumes as a virtual module.

This string is served through a virtual module called virtual:bionicjs-routes. Virtual modules exist only in Vite's module graph - they are never written to disk and never appear in the project directory. Vite resolves them through the plugin's resolveId and load hooks.

Virtual module resolution
1resolveId(id) {
2  if (id === virtualModuleId) return resolvedVirtualModuleId;
3},
4
5load(id) {
6  if (id === resolvedVirtualModuleId) {
7    const files = getAppFiles();
8    const manifest = parseRoutes(files);
9    return generateRouteModule(manifest);
10  }
11}

Every time the virtual module is requested - either on initial load or after an invalidation - the generator re-runs from scratch. The manifest is computed fresh, and the resulting code string replaces whatever was there before. There is no cached state to drift out of sync.

Code Generation

The generator outputs an array of React Router route objects. Each route uses the lazy property with a dynamic import, returning either a page component or a layout wrapped in BionicJSLayoutAdapter. The output is clean, readable JavaScript that looks hand-written.

Generated route structure
1{
2  path: "users",
3  lazy: async () => {
4    const mod = await import("/app/users/layout.tsx");
5    return {
6      Component: () => React.createElement(BionicJSLayoutAdapter, { Layout: mod.default })
7    };
8  },
9  children: [
10    {
11      index: true,
12      lazy: async () => {
13        const mod = await import("/app/users/page.tsx");
14        return { Component: mod.default };
15      }
16    },
17    {
18      path: ":id",
19      lazy: async () => {
20        const mod = await import("/app/users/[id]/page.tsx");
21        return { Component: mod.default };
22      }
23    }
24  ]
25}

Layout nodes use BionicJSLayoutAdapter to wrap the user's layout component. Page nodes return the default export directly. The generator handles nesting automatically - child routes appear in the children array of their parent layout.

Lazy Loading

Every route in the generated module uses dynamic imports inside React Router's lazy property. This guarantees optimal code splitting: only the JavaScript needed for the current page is sent to the client.

ⓘ
When a user navigates from /about to /dashboard, the browser fetches the dashboard/page.tsx chunk - not the entire application. Layouts are loaded once and cached by the browser; only their child pages trigger additional fetches.

This pattern is identical to what you would write by hand with React Router - BionicJS generates it automatically from the filesystem structure. The developer gets the same performance characteristics without thinking about code splitting at all.