Technology handbook
TanStack Start
Full-stack React with server functions, typed routing, streaming, middleware and deployment.
- Topics
- 32
- Sections
- 5
Hooks
useServerFn
Call a server function from a client component with its types preserved.
- The call looks local but crosses the network on every invocation.
- Arguments and return values keep their types across that boundary.
- Wrapping it in a mutation gives the call pending and error state.
Server functions in mutations
Pair a server function with a router invalidation so the page reflects the write.
- A write is only finished when the views reading that data are refreshed.
- Invalidate the routes the write affected rather than reloading the page.
- Do the invalidation after the call resolves, not alongside it.
useRouter in Start
Reach the router to invalidate loaders or navigate after a server call.
- The router instance is what invalidates loaders after a server call.
- Navigating from the same place keeps the post-write flow in one function.
- It reaches navigation from handlers that are not inside a route component.
useLoaderData with server loaders
Read data a route loaded on the server, without a client fetch on first paint.
- First paint already contains the data, so there is no client waterfall.
- The loader runs where the database is, so no query reaches the browser.
- The returned value must be serialisable to survive the transfer.
useRouteContext for request data
Pass request-derived values - a session, a user - down through route context.
- Request-derived values are read once and shared down the route tree.
- A session resolved in middleware is available to every child loader.
- Context is typed, so a missing value is a compile error rather than undefined.
Hooks and the client boundary
A hook runs in the browser; a server function does not, and the boundary is the file.
- A hook needs the browser; a server function must never be inlined into it.
- The boundary is enforced by the file, which is why imports matter.
- Importing server-only code into a component is what leaks a secret.
useNavigate after a form action
Navigate once the server function resolves, not before it does.
- Navigating before the write resolves shows the next screen with stale data.
- Await the server function, then navigate, then let the loader run.
- On failure stay put and show the error where the form is.
Streaming and pending state
Read a route's pending state so a slow loader shows progress instead of a blank shell.
- Pending state lets a slow route show progress instead of an empty shell.
- Deferred parts of a loader resolve after the first bytes are sent.
- A threshold avoids a placeholder for work that finishes in milliseconds.
Start fundamentals
Project structure
Understand which files run on the server, the client, or both.
- Route files run on both sides; server functions run only on one.
- Knowing which is which is what keeps credentials out of the bundle.
- Shared utilities must be safe for both environments or split in two.
Server functions
Define a function that executes server-side and is callable with typed arguments.
- The function is compiled into an endpoint and a typed client caller.
- Arguments cross the network, so they must be serialisable.
- It is the seam where authentication and validation belong.
Loaders with server data
Read data in a route loader without exposing the query to the browser.
- The loader talks to the database directly with no HTTP hop of its own.
- Only the result crosses to the client, never the query or the credentials.
- Route-level loading is what makes the first render complete.
Client and server boundaries
Know what ends up in the bundle and keep secrets out of it.
- Anything imported by a component ends up in the bundle, transitively.
- A server-only marker turns an accidental import into a build error.
- Audit the bundle: a leaked key is not visible in the source review.
Environment configuration
Separate server-only configuration from values the client may read.
- Server configuration is read at runtime; client configuration is inlined at build.
- A public prefix is what marks a value as safe to ship.
- Validate the whole configuration once at startup rather than at first use.
Routing integration
Reuse the Router model for typed paths and search parameters.
- The same typed routes, loaders and search validation carry over.
- Server rendering is added to that model rather than replacing it.
- Links, params and search stay compiler-checked end to end.
Server behaviour
Request context
Read headers, cookies and the session inside a server function.
- Headers and cookies are readable inside the function, not in the component.
- The session is resolved here and passed on through context.
- Setting a cookie is part of the response, so it happens on this side.
Validation and authorization
Treat every server function argument as untrusted input.
- A server function is a public endpoint whatever the client code does.
- Parse the arguments with a schema before touching anything.
- Check permission per call; a hidden button is not an authorisation check.
Middleware
Run shared logic before a function or a route, such as auth or logging.
- Middleware wraps functions and routes with logic they all share.
- Authentication, logging and rate limiting belong here rather than repeated.
- It can extend the context that the wrapped handler then receives.
API routes
Expose a plain HTTP endpoint when a caller is not this application.
- A plain endpoint serves webhooks, native clients and other services.
- The contract is HTTP, so it is versioned rather than typed across the wire.
- Server functions remain the better choice for this application's own calls.
Streaming responses
Send a shell early and stream slower content as it resolves.
- The shell is sent immediately and slower sections follow as they resolve.
- Time to first byte improves without waiting for the slowest query.
- An error after streaming has begun cannot change the status code.
Error handling
Convert a thrown server error into a response the route can render.
- A thrown error becomes a response the route can render deliberately.
- Separate an expected failure from a bug: one is UI, the other is a report.
- Never let an internal message or stack reach the browser.
Data & mutations
Mutations with server functions
Write through a server function and revalidate the affected routes.
- The write and the revalidation belong in one place so they cannot diverge.
- Return what the client needs so a round trip can be avoided.
- A failed write must leave the visible state unchanged.
Optimistic UI
Show the expected outcome while the server call is still in flight.
- Showing the expected result first is what removes the perceived wait.
- Keep the previous state so a failure can restore it exactly.
- Reconcile with the server response rather than assuming the guess was right.
Caching and revalidation
Decide what is cached, for how long, and what invalidates it.
- Decide per route whether a response may be cached and for how long.
- Anything user-specific must never reach a shared cache.
- A write invalidates the routes it affects rather than the whole cache.
Forms and progressive enhancement
Keep a form working before JavaScript has loaded.
- A real form element submits before any JavaScript has loaded.
- The server function handles both the enhanced and the plain submission.
- Validation on the server is what makes the unenhanced path safe.
File uploads
Accept a multipart request and stream it without buffering everything in memory.
- Streaming the body avoids holding a large file in memory.
- Check the size and type before writing anything to storage.
- Serverless platforms cap request size, which shapes the upload design.
Session management
Issue, read and rotate a session cookie with safe attributes.
- Cookies need HttpOnly, Secure and an explicit SameSite value.
- Rotate the identifier on sign-in so a fixed session cannot be reused.
- Server-side expiry is what actually ends a session, not the cookie lifetime.
Shipping
Build output
Understand the server and client artifacts a build produces.
- The build produces a client bundle and a server handler from one source.
- Knowing which artifact a file lands in explains most boundary errors.
- The server entry is what a deployment target actually runs.
Deployment targets
Deploy to a Node server or a serverless platform and know what changes.
- A long-running Node process keeps warm caches that serverless cannot.
- Serverless adds cold starts and per-request limits to consider.
- The same build targets both, but the runtime assumptions differ.
Static and prerendered routes
Prerender routes whose content does not depend on the request.
- A route whose output ignores the request can be rendered at build time.
- Prerendering removes both the server work and the latency.
- Anything personalised must stay dynamic or it is served to the wrong reader.
Observability
Log and trace server functions so a failure is diagnosable in production.
- Log server functions with enough context to find one failing call.
- Tracing across the function, the database and the response locates the delay.
- Keep personal data out of the logs you keep for months.
Performance
Measure payload size and server timing rather than guessing at bottlenecks.
- Measure the payload, the server time and the hydration cost separately.
- The largest win is usually less data, not faster rendering.
- Streaming helps perceived speed but not the total work done.
Testing
Test server functions directly and routes through the router.
- A server function is an ordinary function and can be called directly in a test.
- Route tests exercise loaders, guards and rendering together.
- Cover the unauthorised path as deliberately as the happy one.
Sources
- TanStack Start official documentation