with one click
solid-start
Solid bindings for TanStack Start: useServerFn hook, tanstackStart Vite plugin, StartClient, StartServer, Solid-specific setup, re-exports from @tanstack/start-client-core. Full project setup with Solid.
Menu
Solid bindings for TanStack Start: useServerFn hook, tanstackStart Vite plugin, StartClient, StartServer, Solid-specific setup, re-exports from @tanstack/start-client-core. Full project setup with Solid.
Step-by-step migration from Next.js App Router to TanStack Start: route definition conversion, API mapping, server function conversion from Server Actions, middleware conversion, data fetching pattern changes.
Implement, review, debug, and refactor TanStack Start React Server Components in React 19 apps. Use when tasks mention @tanstack/react-start/rsc, renderServerComponent, createCompositeComponent, CompositeComponent, renderToReadableStream, createFromReadableStream, createFromFetch, Composite Components, React Flight streams, loader or query owned RSC caching, router.invalidate, structuralSharing: false, selective SSR, stale names like renderRsc or .validator, or migration from Next App Router RSC patterns. Do not use for generic SSR or non-TanStack RSC frameworks except brief comparison.
React bindings for TanStack Start: createStart, StartClient, StartServer, React-specific imports, re-exports from @tanstack/react-router, full project setup with React, useServerFn hook.
Server-side authentication primitives for TanStack Start: session cookies (HttpOnly, Secure, SameSite, __Host- prefix), session read/issue/destroy via createServerFn and middleware, OAuth authorization-code flow with state and PKCE, password-reset enumeration defense, CSRF for non-GET RPCs, rate limiting auth endpoints, session rotation on privilege change. Pairs with router-core/auth-and-guards for the routing side.
createMiddleware, request middleware (.server only), server function middleware (.client + .server), context passing via next({ context }), sendContext for client-server transfer, global middleware via createStart in src/start.ts, middleware factories, method order enforcement, fetch override precedence.
createServerFn (GET/POST), validator (Zod or function), useServerFn hook, server context utilities (getRequest, getRequestHeader, setResponseHeader, setResponseStatus), error handling (throw errors, redirect, notFound), streaming, FormData handling, file organization (.functions.ts, .server.ts).
| name | solid-start |
| description | Solid bindings for TanStack Start: useServerFn hook, tanstackStart Vite plugin, StartClient, StartServer, Solid-specific setup, re-exports from @tanstack/start-client-core. Full project setup with Solid. |
| type | framework |
| library | tanstack-start |
| library_version | 1.166.2 |
| framework | solid |
| requires | ["start-core"] |
| sources | ["TanStack/router:packages/solid-start/src","TanStack/router:docs/start/framework/solid/build-from-scratch.md"] |
@tanstack/solid-start)This skill builds on start-core. Read start-core first for foundational concepts.
This skill covers the Solid-specific bindings, setup, and patterns for TanStack Start.
CRITICAL: All code is ISOMORPHIC by default. Loaders run on BOTH server and client. Use
createServerFnfor server-only logic.
CRITICAL: Do not confuse
@tanstack/solid-startwith SolidStart (@solidjs/start). They are completely different frameworks with different APIs.
CRITICAL: Types are FULLY INFERRED. Never cast, never annotate inferred values.
@tanstack/solid-start re-exports everything from @tanstack/start-client-core plus:
useServerFn — Solid hook for calling server functions from componentsAll core APIs (createServerFn, createMiddleware, createStart, createIsomorphicFn, createServerOnlyFn, createClientOnlyFn) are available from @tanstack/solid-start.
Server utilities (getRequest, getRequestHeader, setResponseHeader, setCookie, getCookie, useSession) are imported from @tanstack/solid-start/server.
npm i @tanstack/solid-start @tanstack/solid-router solid-js
npm i -D vite vite-plugin-solid typescript
{
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"start": "node .output/server/index.mjs"
}
}
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js",
"moduleResolution": "Bundler",
"module": "ESNext",
"target": "ES2022",
"skipLibCheck": true,
"strictNullChecks": true
}
}
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/solid-start/plugin/vite'
import solidPlugin from 'vite-plugin-solid'
export default defineConfig({
plugins: [
tanstackStart(), // MUST come before solid plugin
solidPlugin({ ssr: true }),
],
})
import { createRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'
export function getRouter() {
const router = createRouter({
routeTree,
scrollRestoration: true,
})
return router
}
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from '@tanstack/solid-router'
import { HydrationScript } from 'solid-js/web'
import { Suspense } from 'solid-js'
export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ title: 'My TanStack Start App' },
],
}),
// shellComponent renders the HTML document shell (always SSR'd)
shellComponent: RootDocument,
})
function RootDocument(props: { children: any }) {
return (
<html>
<head>
<HydrationScript />
</head>
<body>
<HeadContent />
<Suspense>{props.children}</Suspense>
<Scripts />
</body>
</html>
)
}
import { createFileRoute } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'
const getGreeting = createServerFn({ method: 'GET' }).handler(async () => {
return 'Hello from TanStack Start!'
})
export const Route = createFileRoute('/')({
loader: () => getGreeting(),
component: HomePage,
})
function HomePage() {
const greeting = Route.useLoaderData()
return <h1>{greeting()}</h1>
}
Use useServerFn to call server functions from Solid components with automatic redirect handling:
import { createServerFn, useServerFn } from '@tanstack/solid-start'
import { createSignal } from 'solid-js'
const updatePost = createServerFn({ method: 'POST' })
.validator((data: { id: string; title: string }) => data)
.handler(async ({ data }) => {
await db.posts.update(data.id, { title: data.title })
return { success: true }
})
function EditPostForm(props: { postId: string }) {
const updatePostFn = useServerFn(updatePost)
const [title, setTitle] = createSignal('')
return (
<form
onSubmit={async (e) => {
e.preventDefault()
await updatePostFn({ data: { id: props.postId, title: title() } })
}}
>
<input value={title()} onInput={(e) => setTitle(e.target.value)} />
<button type="submit">Save</button>
</form>
)
}
Unlike the React version, useServerFn does NOT wrap the returned function in any memoization (no useCallback equivalent needed — Solid's setup runs once).
All routing components from @tanstack/solid-router work in Start:
<Outlet> — renders matched child route<Link> — type-safe navigation<Navigate> — declarative redirect<HeadContent> — renders head tags via @solidjs/meta (must be in <body> — uses portals to inject into <head>)<Scripts> — renders body scripts (must be in <body>)<Await> — renders deferred data with <Suspense><ClientOnly> — renders children only after hydration<CatchBoundary> — error boundary wrapping Solid.ErrorBoundaryAll hooks from @tanstack/solid-router work in Start. Most return Accessor<T> — call the accessor to read:
useRouter() — router instance (NOT an Accessor)useRouterState() — Accessor<T>, subscribe to router stateuseNavigate() — navigation function (NOT an Accessor)useSearch({ from }) — Accessor<T>, validated search paramsuseParams({ from }) — Accessor<T>, path paramsuseLoaderData({ from }) — Accessor<T>, loader datauseMatch({ from }) — Accessor<T>, full route matchuseRouteContext({ from }) — Accessor<T>, route contextRoute.useLoaderData() — Accessor<T>, typed loader data (preferred in route files)Route.useSearch() — Accessor<T>, typed search params (preferred in route files)// WRONG — this is the SPA router, NOT Start
import { createServerFn } from '@tanstack/solid-router'
// CORRECT — server functions come from solid-start
import { createServerFn } from '@tanstack/solid-start'
// CORRECT — routing APIs come from solid-router (re-exported by Start too)
import { createFileRoute, Link } from '@tanstack/solid-router'
Most hooks return Accessor<T>. Must call to read the value.
// WRONG
const data = Route.useLoaderData()
return <h1>{data.message}</h1>
// CORRECT
const data = Route.useLoaderData()
return <h1>{data().message}</h1>
Without <Scripts /> in the root route's <body>, client JavaScript doesn't load and the app won't hydrate.
// WRONG
plugins: [solidPlugin(), tanstackStart()]
// CORRECT
plugins: [tanstackStart(), solidPlugin()]