SSR with TanStack Router
Client-side routing with TanStack Router in Nitro using Vite.
import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider, createRouter } from "@tanstack/react-router";
// Import the generated route tree
import { routeTree } from "./routeTree.gen.ts";
// Create a new router instance
const router = createRouter({ routeTree });
// Register the router instance for type safety
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
// Render the app
const rootElement = document.querySelector("#root")!;
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
);
}Set up TanStack Router with React, Vite, and Nitro. This setup provides file-based routing with type-safe navigation and automatic code splitting.
#Overview
#1. Configure Vite
Add the Nitro, React, and TanStack Router plugins to your Vite config:
import { defineConfig } from "vite";
import { nitro } from "nitro/vite";
import react from "@vitejs/plugin-react";
import { tanstackRouter } from "@tanstack/router-plugin/vite";
export default defineConfig({
plugins: [tanstackRouter({ target: "react", autoCodeSplitting: true }), react(), nitro()],
});The tanstackRouter plugin generates a route tree from your routes/ directory structure. Enable autoCodeSplitting to automatically split routes into separate chunks. Place the TanStack Router plugin before the React plugin in the array.
#2. Create the HTML Template
Create an HTML file that serves as your app shell:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nitro + TanStack Router + React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>#3. Create the App Entry
Create the main entry that initializes TanStack Router:
import { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider, createRouter } from "@tanstack/react-router";
// Import the generated route tree
import { routeTree } from "./routeTree.gen.ts";
// Create a new router instance
const router = createRouter({ routeTree });
// Register the router instance for type safety
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
// Render the app
const rootElement = document.querySelector("#root")!;
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
);
}The routeTree.gen.ts file is auto-generated from your routes/ directory structure. The Register interface declaration provides full type inference for route paths and params. The !rootElement.innerHTML check prevents re-rendering during hot module replacement.
#4. Create the Root Route
The root route (__root.tsx) defines your app's layout:
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
const RootLayout = () => (
<>
<div className="p-2 flex gap-2">
<Link to="/" className="[&.active]:font-bold">
Home
</Link>
</div>
<hr />
<Outlet />
<TanStackRouterDevtools />
</>
);
export const Route = createRootRoute({ component: RootLayout });Use Link for type-safe navigation with active state styling. The Outlet component renders child routes. Include TanStackRouterDevtools for development tools (automatically removed in production).
#5. Create Page Routes
Page routes use createFileRoute and can include loaders:
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
loader: async () => {
const r = await fetch("/api/hello");
return r.json();
},
component: Index,
});
function Index() {
const r = Route.useLoaderData();
return (
<div className="p-2">
<h3>{JSON.stringify(r)}</h3>
</div>
);
}Fetch data before rendering with the loader function—data is available via Route.useLoaderData(). File paths determine URL paths: routes/index.tsx maps to /, routes/about.tsx to /about, and routes/users/$id.tsx to /users/:id.