Every verified stack that runs on React Router v8.
React Router v8 (framework mode): SSR, config/file routes under app/, loaders/actions, and resource routes for API endpoints.
240 verified stacks
Showing 12 across 12 app types. Every stack page links its neighbours along each axis, so any combination is a click or two from here.
What React Router v8 gives you
React Router v8 in framework mode puts everything under app/, and `@/` maps to that root instead of Next's src/ — the one prefix that differs, which is why shared modules like @/lib/auth and @/db/schema stay byte-identical to their Next counterparts. initCode writes app/lib/db.ts, then the auth fragment adds app/lib/auth.ts, the resource route app/routes/api.auth.$.ts, and app/lib/require-auth.ts. The route table itself is app/routes.ts: routes are declared configuration, and a file becomes a URL because that table says so. There are no React Server Components here. Every server-rendered route is a loader plus an ordinary client component: the loader runs on the server before render, the component reads its result with useLoaderData, and mutations go through an action read back with useActionData.
An API endpoint is the same module minus the default export — a resource route, named with the flat dotted convention (app/routes/api.auth.$.ts for the auth splat, app/routes/webhooks.polar.ts for a webhook POST). Auth gates in the loader rather than in a middleware layer. A protected route awaits requireAuth(request) from app/lib/require-auth.ts, which calls auth.api.getSession({ headers: request.headers }) — a real server-side validation, not a cookie peek — and throws redirect("/sign-in") when there is no session. React Router treats a thrown Response as the route's outcome, so the loader short-circuits and neither the protected query nor the component ever runs. The trade that follows: there is no matcher array to widen and no edge tier to keep honest, but protection is per-route discipline.
A new route is protected because its loader calls requireAuth; forget the call and the page is public. In return, every gate sits one function call away from the data it guards, the session is already in hand when the loader queries db, and the same request-in / Response-out contract covers pages, API endpoints and the auth mount alike.
Built for
The route module's loader imports db from app/lib/db.ts, runs the Drizzle query on the server before render, and the component reads the result through useLoaderData — the RR replacement for a Server Component's inline await.
`const session = await requireAuth(request)` as the first line of the loader. app/lib/require-auth.ts validates via auth.api.getSession and throws a redirect Response, which React Router short-circuits on before the loader's data ever loads.
app/routes/api.auth.$.ts — a resource route with no default component whose loader and action both return auth.handler(request). The `$` splat catches every /api/auth/* sub-path the auth library routes internally.
app/routes/webhooks.polar.ts exports an action that verifies the provider signature and hands the event to the idempotent record module the billing fragment emits — a resource route again, since there is nothing to render.
Declare it in app/routes.ts, the config route table, and put the module under app/routes/. The emitted routes use RR's flat dotted naming — webhooks.polar.ts, api.auth.$.ts — where a dot is a path separator and `$` is a splat segment.
Why React Router v8, specifically
Framework mode (not data/library mode): routes live under app/, declared in app/routes.ts. API endpoints are resource routes (a route module exporting loader/action but no default component).
Data flows through loaders (run on the server before render) and actions (mutations); components read it with useLoaderData / useActionData. There are no React Server Components — every server-rendered route is a loader plus a client component.
Auth gates in the loader, not in middleware: a protected route's loader calls requireAuth(request), which throws a redirect Response that React Router short-circuits on — so a logged-out user never reaches the protected data or renders the page.
The `@/` import alias maps to app/ (this framework's source root), so shared modules like @/lib/auth resolve under app/ — the one path prefix that differs from Next's src/, which is why the auth slice's mount code is framework-specific.
What React Router v8 pulls in
bun add react-router react react-dom