@json-render/tanstack-start
TanStack Start renderer for JSON-defined applications with routes, layouts, head metadata, SSR loaders, prerender paths, and client navigation.
Installation#
npm install @json-render/core @json-render/react @json-render/tanstack-startschema#
Use the Start application schema to generate full multi-page specs.
import { defineCatalog } from "@json-render/core";
import {
schema,
startComponentDefinitions,
} from "@json-render/tanstack-start/server";
import { z } from "zod";
const catalog = defineCatalog(schema, {
components: {
...startComponentDefinitions,
Card: {
props: z.object({ title: z.string() }),
description: "Card container",
},
NavBar: {
props: z.object({}),
slots: ["default"],
description: "Application navigation",
},
},
actions: {},
});The generation prompt teaches TanStack Router's $param and $ splat route
syntax, reusable layouts, escaped JSON Patch route keys, and the built-in
Slot, Link, and navigate capabilities.
Include startComponentDefinitions in the catalog so generated Slot and
Link elements pass validation. PageRenderer supplies their React
implementations automatically.
createStartApp#
Create helpers for a TanStack Start splat route.
import { createStartApp } from "@json-render/tanstack-start/server";
export const { getPageData, getHead, getStaticPaths } = createStartApp({
spec,
loaders: {
post: async ({ slug }) => ({
post: await getPost(slug as string),
}),
},
});Options#
| Option | Type | Description |
|---|---|---|
spec | StartAppSpec | (() => StartAppSpec | Promise<StartAppSpec>) | A static application spec or an async spec factory |
loaders | Record<string, LoaderFn> | Named data loaders referenced by route specs |
Returns#
| Helper | Description |
|---|---|
getPageData | Matches a pathname, runs its loader, and returns serializable page and layout data |
getHead | Returns TanStack Router |
getStaticPaths | Returns concrete paths for TanStack Start prerendering |
State is merged in this order: application state, layout state, page state, then loader data. Later sources override earlier values.
StartAppSpec#
interface StartAppSpec {
metadata?: StartMetadata;
routes: Record<string, StartRouteSpec>;
layouts?: Record<string, Spec>;
state?: Record<string, unknown>;
}Each route requires a page spec and can select a layout, metadata, a named
loader, loading/error/not-found specs, and static parameters.
Route Patterns#
| Pattern | Example | Params |
|---|---|---|
/ | / | {} |
/about | /about | {} |
/blog/$slug | /blog/hello | { slug: "hello" } |
/docs/$ | /docs/guides/intro | { _splat: "guides/intro" } |
Loader parameters are URL-decoded before they reach named loaders. Splat
content is a slash-delimited string under _splat. Parameter values supplied
through staticParams are URL-encoded in the paths returned by
getStaticPaths().
Route matching treats trailing slashes as optional and accepts both encoded and decoded pathname representations. This keeps loader data and route metadata in sync for static paths containing spaces or non-ASCII characters.
For prerendered dynamic routes, provide staticParams:
routes: {
'/blog/$slug': {
page,
staticParams: [{ slug: 'hello' }, { slug: 'world' }],
},
'/docs/$': {
page: docsPage,
staticParams: [{ _splat: 'guides/intro' }],
},
}Map getStaticPaths() into TanStack Start's top-level pages configuration:
const pages = (await getStaticPaths()).map((path) => ({ path }));TanStack Route Setup#
Wire the helpers to a file-based $ splat route:
// src/routes/$.tsx
import { createFileRoute, notFound } from "@tanstack/react-router";
import {
PageRenderer,
StartErrorBoundary,
StartLoading,
StartNotFound,
} from "@json-render/tanstack-start";
import { getHead, getPageData } from "@/lib/json-app";
export const Route = createFileRoute("/$")({
loader: async ({ location }) => {
const data = await getPageData({ pathname: location.pathname });
if (!data) throw notFound();
return data;
},
head: ({ match }) => getHead({ pathname: match.pathname }),
component: Page,
pendingComponent: StartLoading,
errorComponent: StartErrorBoundary,
notFoundComponent: StartNotFound,
});
function Page() {
return <PageRenderer {...Route.useLoaderData()} />;
}TanStack Router loaders are isomorphic. When a spec factory or named loader
uses database clients, credentials, or server-only imports, invoke
getPageData and getHead inside a TanStack Start createServerFn and call
that server function from the route loader.
StartAppProvider#
Provide component implementations and action handlers around the root
Outlet. Render HeadContent for route metadata.
import {
createRootRoute,
HeadContent,
Outlet,
Scripts,
} from "@tanstack/react-router";
import { StartAppProvider } from "@json-render/tanstack-start";
import { spec } from "@/lib/spec";
export const Route = createRootRoute({
component: () => (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
<StartAppProvider
registry={registry}
handlers={handlers}
spec={spec}
>
<Outlet />
</StartAppProvider>
<Scripts />
</body>
</html>
),
});Passing spec lets StartLoading, StartErrorBoundary, and StartNotFound
automatically select the matched route's fallback specs. Their explicit
loadingSpec, errorSpec, and notFoundSpec props take precedence. For a
server-only application spec, omit spec and pass client-safe fallback specs
explicitly.
Pass named functions through functions when props use $computed:
<StartAppProvider
registry={registry}
spec={spec}
functions={{ uppercase: ({ value }) => String(value).toUpperCase() }}
>
<Outlet />
</StartAppProvider>Built-ins#
Slotinserts page content into a JSON-defined layout.Linkwraps TanStack Router'sLink; generated specs use anhrefprop.navigateperforms client-side navigation from action bindings.StartLoading,StartErrorBoundary, andStartNotFoundresolve the matched route's fallback specs when they are used as TanStack Router boundary components and the provider receivesspec.
The default StartErrorBoundary fallback invalidates the router and reruns the
failed loader when the user selects Try again.
Slot and Link are automatically added to the page registry.
Server Utilities#
import {
collectStaticPaths,
matchRoute,
metadataToHead,
resolveMetadata,
splatToPath,
} from "@json-render/tanstack-start/server";Entry Points#
| Import | Contents |
|---|---|
@json-render/tanstack-start | Provider, page renderer, Link, and route fallback components |
@json-render/tanstack-start/server | App factory, schema, matcher, metadata, and prerender helpers |
@json-render/tanstack-start/catalog | Server-safe definitions for built-in Slot and Link components |