Learn
SSR with Loaders
When you have a server runtime that can execute loaders at request time, set runLoaders: true in the ssr config to produce fully rendered HTML — including loader data — on the server.
How runLoaders Works
As described in the Static Site Generation guide, the ssr prop enables path-based route matching during SSR. By default, routes with loaders are skipped. Setting runLoaders: true changes this: those routes are matched and their loaders execute during SSR.
// Without runLoaders (default): loaders are skipped
<Router routes={routes} ssr={{ path: "/dashboard" }} />
// With runLoaders: loaders execute during SSR
<Router routes={routes} ssr={{ path: "/dashboard", runLoaders: true }} />When loaders run during SSR, their results are passed to components as the data prop. The server-rendered HTML includes the loader content, so users see the full page immediately.
Example
Consider a dashboard route with a loader that fetches user data:
const routes = [
route({
component: AppShell,
children: [
route({ path: "/", component: HomePage }),
route({
path: "/dashboard",
component: DashboardPage,
loader: dashboardLoader,
}),
],
}),
];
// Without runLoaders: DashboardPage is skipped during SSR.
// Users see only the app shell; dashboard content fills in after hydration.
<Router routes={routes} ssr={{ path: "/dashboard" }} />
// With runLoaders: dashboardLoader executes during SSR.
// DashboardPage renders with data — users see the full page immediately.
<Router routes={routes} ssr={{ path: "/dashboard", runLoaders: true }} />Server Setup
Unlike static site generation, SSR with loaders requires a server runtime that can handle requests and render pages dynamically. Your server needs to know the requested pathname and pass it to the router:
// App.tsx — receives the pathname from the server
export default function App({ pathname }: { pathname: string }) {
return (
<Router
routes={routes}
ssr={{ path: pathname, runLoaders: true }}
/>
);
}Because loaders run during SSR, they must be able to execute in the server environment. If your loaders call browser-only APIs, you may need to adjust them or use environment checks.
Comparison with ssr (No Loaders)
The choice between ssr alone and ssr + runLoaders depends on your deployment model:
ssr={{ path }}— Ideal for static site generation. Pages without loaders are fully pre-rendered. Pages with loaders show the app shell during SSR and fill in data after hydration. No server runtime needed.ssr={{ path, runLoaders: true }}— Ideal for dynamic SSR with a server runtime. All matched routes render during SSR, including those with loaders. Users see the complete page immediately.
Key Takeaways
- Set
runLoaders: trueto execute loaders during SSR for fully rendered server output - Loader results are passed to components as the
dataprop during SSR, just as they are on the client - This mode requires a server runtime that handles requests dynamically
- After hydration, the real URL from the Navigation API takes over and
ssris ignored