Filesystem routing
The folder convention and the route tree it produces.
The convention
BionicJS routes map directly from the filesystem. The rule is simple:
1app/page.tsx → /
2app/about/page.tsx → /about
3app/dashboard/settings/page.tsx → /dashboard/settings
Layouts wrap every route below them, so nesting folders is nesting layouts:
1app/
2|-- page.tsx -> /
3|-- layout.tsx -> wraps all pages
4|-- about/
5| `-- page.tsx -> /about
6`-- blog/
7 |-- layout.tsx -> wraps /blog/*
8 |-- page.tsx -> /blog
9 `-- [slug]/
10 `-- page.tsx -> /blog/:slug
Every route is a file named page.tsx. Its path relative to app/ is the URL. No manual registration, no import list, no router config.
app/layout.tsx and app/page.tsx. When you create app/about/page.tsx, the route appears automatically. No restart required during development.Build-time discovery
Filesystem scanning is strictly a build-time and dev-server concern, owned by bionicjsRoutesPlugin. The browser runtime never scans the filesystem or calls import.meta.glob.
1const relativeFiles = fg.sync("**/{page,layout}.tsx", { cwd: appDir });
2const manifest = parseRoutes(relativeFiles);
3return generateRouteModule(manifest);
During development, Vite's file watcher monitors app/. When a route file is created or deleted, bionicjsRoutesPlugin invalidates the virtual module graph entry, triggering instant HMR update.
virtual:bionicjs-routes & React Router
BionicJS's generator transforms the manifest tree into a virtual module string, virtual:bionicjs-routes, exported directly to React Router. Each route uses lazy dynamic imports for optimal code splitting.
1import React from "react";
2import { BionicJSLayoutAdapter } from "@bionicjs/core/router";
3
4export const routes = [
5 {
6 lazy: async () => {
7 const mod = await import("/app/layout.tsx");
8 return {
9 Component: () => React.createElement(BionicJSLayoutAdapter, { Layout: mod.default })
10 };
11 },
12 children: [
13 {
14 index: true,
15 lazy: async () => {
16 const mod = await import("/app/page.tsx");
17 return { Component: mod.default };
18 }
19 },
20 {
21 path: "about",
22 lazy: async () => {
23 const mod = await import("/app/about/page.tsx");
24 return { Component: mod.default };
25 }
26 }
27 ]
28 }
29];
Layouts use standard React { children } syntax. The BionicJSLayoutAdapter injects React Router's <Outlet /> transparently behind the scenes.
The developer mounts <BionicJSRouter /> at their application root. That is the entire router API surface exposed to them. React Router is an internal framework detail.
04 / 25