Each framework generator produces a small set of foundation files that wire up routing, navigation, and application bootstrap. The structure is consistent across frameworks: a root App component, a router configuration, and a client entry point.
π¨ Root Application Component β
The generator creates a minimal root component as your application shell. Extend it with global layouts, error boundaries, authentication providers, or other application-wide concerns.
import { Outlet } from "react-router";
export default function App() {
return <Outlet />;
}import type { ParentComponent } from "solid-js";
const App: ParentComponent = (props) => {
return props.children;
};
export default App;<template>
<RouterView />
</template>{props.children}π£οΈ Router Configuration β
The routerFactory function connects your root App component and generated routes to the framework's native router. It accepts a callback receiving auto-generated route definitions from KosmoJS.
The callback must return two functions:
clientRouter()- browser-based routing for client-side navigationserverRouter(url)- server-side routing for SSR, receiving the requested URL
import {
createBrowserRouter,
createStaticHandler,
createStaticRouter,
RouterProvider,
StaticRouterProvider,
} from "react-router";
import { baseurl } from "~/config";
import routerFactory from "_/router";
import App from "./App";
export default routerFactory((routes) => {
const routeStack = [
{
path: "/",
Component: App,
children: routes,
},
];
const handler = createStaticHandler(routeStack, { basename: baseurl });
return {
async clientRouter() {
const router = createBrowserRouter(routeStack, { basename: baseurl });
return <RouterProvider router={router} />;
},
async serverRouter(url) {
const context = await handler.query(new Request(url.href));
if (context instanceof Response) {
// handled by SSR server
throw context;
}
const router = createStaticRouter(routeStack, context);
return <StaticRouterProvider router={router} context={context} />;
},
};
});import { Router } from "@solidjs/router";
import { baseurl } from "~/config";
import routerFactory from "_/router";
import App from "./App";
export default routerFactory((routes) => {
return {
async clientRouter() {
return <Router root={App} base={baseurl}>{routes}</Router>;
},
async serverRouter(url) {
return <Router root={App} base={baseurl} url={url.pathname}>
{routes}
</Router>;
},
}
});import { createApp, createSSRApp } from "vue";
import {
createMemoryHistory,
createRouter,
createWebHistory,
} from "vue-router";
import { baseurl } from "~/config";
import routerFactory from "_/router";
import App from "./App.vue";
export default routerFactory((routes) => {
return {
async clientRouter() {
const app = createApp(App);
const router = createRouter({
history: createWebHistory(baseurl),
routes,
strict: true,
});
app.use(router);
return app;
},
async serverRouter(url) {
const app = createSSRApp(App);
const router = createRouter({
history: createMemoryHistory(baseurl),
routes,
strict: true,
});
await router.push(url.pathname.replace(baseurl, ""));
await router.isReady();
app.use(router);
return app;
},
};
});import { createRouter } from "_/mdx";
import routerFactory from "_/router";
import App from "./App.mdx";
import { components } from "./components/mdx"
export default routerFactory((routes) => {
const router = createRouter(routes, App, { components });
return {
async clientRouter() {
return router.resolve();
},
async serverRouter(url) {
return router.resolve(url);
},
};
});All use your source folder's baseurl config for correct path-based routing. The generated routes are always wrapped inside your App component, establishing the layout hierarchy.
π― Application Entry β
The entry/client.tsx file is your application's DOM rendering entry point, referenced from index.html:
<script type="module" src="./entry/client.tsx"></script>Vite begins from this HTML file, follows the import to entry/client, and constructs the complete application dependency graph from there.
The renderFactory function orchestrates two rendering modes via a callback that must return:
mount()- mounts the application fresh in the browserhydrate()- hydrates pre-rendered server HTML for interactivity
On page load, renderFactory reads Vite's import.meta.env.SSR flag to select the correct method: hydrate() for SSR hydration, mount() for a fresh client-only mount.
import { createRoot, hydrateRoot } from "react-dom/client";
import renderFactory, { createRoutes } from "_/entry/client";
import routerFactory from "../router";
const routes = createRoutes({ withPreload: true });
const { clientRouter } = routerFactory(routes);
const root = document.getElementById("app");
if (root) {
renderFactory(() => {
return {
async mount() {
const page = await clientRouter();
createRoot(root).render(page);
},
async hydrate() {
const page = await clientRouter();
hydrateRoot(root, page);
},
};
});
} else {
console.error("β Root element not found!");
}import { hydrate, render } from "solid-js/web";
import renderFactory, { createRoutes } from "_/entry/client";
import routerFactory from "../router";
const routes = createRoutes({ withPreload: true });
const { clientRouter } = routerFactory(routes);
const root = document.getElementById("app");
if (root) {
renderFactory(() => {
return {
async mount() {
const page = await clientRouter();
render(() => page, root);
},
async hydrate() {
const page = await clientRouter();
hydrate(() => page, root)
},
}
});
} else {
console.error("β Root element not found!");
}import renderFactory, { createRoutes } from "_/entry/client";
import routerFactory from "../router";
const routes = createRoutes();
const { clientRouter } = routerFactory(routes);
const root = document.getElementById("app");
if (root) {
renderFactory(() => {
return {
async mount() {
const page = await clientRouter();
page.mount(root);
},
async hydrate() {
const page = await clientRouter();
page.mount(root, true);
},
};
});
} else {
console.error("β Root element not found!");
}import { hydrate, render } from "preact";
import renderFactory, { createRoutes } from "_/entry/client";
import routerFactory from "../router";
const routes = createRoutes();
const { clientRouter } = routerFactory(routes);
const root = document.getElementById("app");
if (root) {
renderFactory(() => {
return {
async mount() {
const page = await clientRouter();
render(page.component, root);
},
async hydrate() {
const page = await clientRouter();
hydrate(page.component, root);
},
};
});
} else {
console.error("β Root element not found!");
}- React uses
createRoot/hydrateRootfromreact-dom/client. - SolidJS uses
render/hydratefromsolid-js/web. - Vue constructs separate app instances via
createAppandcreateSSRApp. - MDX uses
render/hydratefrompreact.