Frontend
TanStack Router (Vite)
When you choose TanStack Router as your frontend framework, Grit scaffolds a Vite-powered React SPA with file-based routing, React Query, and Tailwind CSS. Fast builds, small bundles, no Node.js server needed.
Why TanStack Router?
Instant HMR
Vite provides sub-50ms hot module replacement. Changes appear instantly in the browser.
Small bundles
No server runtime overhead. The production output is static HTML + JS that any CDN can serve.
Type-safe routing
TanStack Router provides fully type-safe route params, search params, and loaders.
File-based routes
Routes auto-discovered by @tanstack/router-vite-plugin. No manual route registry needed.
Project structure
TanStack Router apps use src/routes/ for file-based routing instead of Next.js's app/ directory.
apps/web/├── src/│ ├── routes/│ │ ├── __root.tsx # Root layout (Navbar + Footer)│ │ ├── index.tsx # Home page (/)│ │ └── blog/│ │ ├── index.tsx # Blog list (/blog)│ │ └── $slug.tsx # Blog detail (/blog/:slug)│ ├── components/│ │ ├── navbar.tsx│ │ └── footer.tsx│ ├── hooks/│ │ └── use-blogs.ts│ ├── lib/│ │ ├── api.ts # Axios client│ │ └── utils.ts│ ├── main.tsx # Entry point│ └── globals.css├── index.html├── vite.config.ts # TanStack Router plugin + API proxy├── tailwind.config.ts└── package.json
Key differences from Next.js
| Aspect | Next.js | TanStack Router |
|---|---|---|
| Routing | app/ directory convention | src/routes/ via Vite plugin |
| Layouts | layout.tsx | __root.tsx + _layout.tsx |
| Build tool | Next.js (webpack/turbopack) | Vite |
| SSR | Built-in | SPA only (no SSR) |
| "use client" | Required for client components | Not needed (everything is client) |
| Dev server | next dev (:3000) | vite dev (:3000) |
| Output | .next/ | dist/ |
| Params | useParams() from next/navigation | Route.useParams() |
| Navigation | <Link> from next/link | <Link> from @tanstack/react-router |
Route examples
import { createRootRoute, Outlet } from '@tanstack/react-router'import { Navbar } from '@/components/navbar'import { Footer } from '@/components/footer'export const Route = createRootRoute({component: () => (<div className="min-h-screen bg-background flex flex-col"><Navbar /><main className="flex-1"><Outlet /></main><Footer /></div>),})
import { createFileRoute, Link } from '@tanstack/react-router'import { useQuery } from '@tanstack/react-query'import { api } from '@/lib/api'export const Route = createFileRoute('/blog/$slug')({component: BlogDetailPage,})function BlogDetailPage() {const { slug } = Route.useParams()const { data: blog } = useQuery({queryKey: ['blog', slug],queryFn: () => api.get('/api/blogs/' + slug).then(r => r.data.data),})return <h1>{blog?.title}</h1>}
Admin panel with TanStack Router
When you choose TanStack Router, the admin panel also uses it. Auth and dashboard are handled via layout routes with beforeLoad guards.
import { createFileRoute, Outlet, redirect } from '@tanstack/react-router'import { AdminLayout } from '@/components/layout/admin-layout'export const Route = createFileRoute('/_dashboard')({beforeLoad: () => {const token = localStorage.getItem('access_token')if (!token) {throw redirect({ to: '/login' })}},component: () => (<AdminLayout><Outlet /></AdminLayout>),})