Layout adaptation
Mapping framework layouts onto React Router without an Outlet.
The mismatch
React Router renders nested routes using the <Outlet /> component. A layout component must call <Outlet /> where its children should appear. But BionicJS developers write standard React layouts that accept children:
1export default function Layout({ children }) {
2 return (
3 <div className="layout">
4 <nav>...</nav>
5 {children}
6 </div>
7 );
8}
This is the idiomatic React pattern - every component library, every design system, every documentation page teaches layouts this way. But React Router expects <Outlet />, not children. If BionicJS required developers to use <Outlet />, it would break the mental model that layouts are just regular React components.
BionicJSLayoutAdapter
To bridge this gap, the BionicJS generator wraps every layout in a higher-order component called BionicJSLayoutAdapter. This adapter takes the user's layout and automatically injects an <Outlet /> as its child, completely hiding the React Router implementation detail:
1export function BionicJSLayoutAdapter({ Layout }) {
2 return (
3 <Layout>
4 <Outlet />
5 </Layout>
6 );
7}
When the generator encounters a layout file, it wraps the import in this adapter. The developer's layout component receives <Outlet /> as the children prop, so it renders nested routes exactly where it placed {children}.
1lazy: async () => {
2 const mod = await import("/app/dashboard/layout.tsx");
3 return {
4 Component: () => React.createElement(BionicJSLayoutAdapter, { Layout: mod.default })
5 };
6}
Why not require Outlet?
Requiring developers to import <Outlet /> from react-router would leak the routing implementation into every layout. The adapter eliminates this coupling entirely.
This is a core principle of the BionicJS design: React Router is an internal detail. The developer writes <BionicJSRouter /> at the root and standard {children} layouts everywhere else. The framework adapts between the two conventions so the developer never has to think about it.
<Outlet /> as its only child. There is no extra re-rendering, no context providers, no performance overhead. It exists purely to translate between two conventions.07 / 25