TanStack Start on Easel
Deploy full-stack TanStack Start applications to Easel with SSR, streaming, server functions, and Nitro.
Easel runs TanStack Start applications built with the standard Nitro Vite plugin. Client assets and prerendered pages go to the CDN, and SSR, server functions, server routes, and middleware run in Easel Functions with streaming. Your app stays standard TanStack Start, not an Easel fork.
At a glance
| Support level | Production-ready |
| Rendering | Static, SSR, streaming, selective SSR, and SPA mode |
| Server runtime | Node.js |
| Deployment integration | Nitro |
| Adapter required | Yes |
| Automatic detection | Yes |
| Most recently tested with | TanStack Start 1.168.x |
Deploy a TanStack Start application
TanStack Start uses Nitro to produce the server entry and static assets Easel deploys. Install it:
Add the Nitro Vite plugin after tanstackStart() and before the React plugin:
Don't set a Nitro preset or NITRO_PRESET yourself. Easel selects the hosting
output it expects during the platform build, and a different preset produces
a server shape Easel can't deploy.
Create a project from your Git repository or deploy with the CLI:
Easel detects TanStack Start and runs your build script or vite build.
Install command, build command, root directory, and Node.js version live in
build configuration.
What Easel deploys
The Nitro build produces a client application and a server entry that handles TanStack Start requests.
| Application output | Easel resource |
|---|---|
| JavaScript, CSS, fonts, and public files | CDN assets |
| Prerendered pages | CDN assets |
| Server-rendered routes and loaders | Easel Functions |
| Server functions | Application server function |
| Server routes | Application server function |
| Start middleware | Application server function |
| Streaming responses | Function response stream |
Server functions, server routes, and middleware all live in one application server bundle. They aren't separate, individually configurable functions unless the build output splits them.
Supported features
| Feature | Support | Notes |
|---|---|---|
| File-based routing | Supported | Generated TanStack Router routes are part of the build |
| Full-document SSR | Supported | Routes render in Easel Functions |
| Streaming SSR | Supported | HTML is forwarded as the framework produces it |
Route loaders and beforeLoad | Supported | Run where the route's SSR mode puts them |
| Server functions | Supported | Validated, type-safe calls from client and server code |
| Streaming server functions | Supported | ReadableStream and async-generator responses |
| Server routes | Supported | External HTTP endpoints from the Start app |
| Middleware | Supported | Request, function, and server-route middleware |
| Selective SSR | Supported | Full, data-only, or client-only per route |
| SPA mode | Supported | The application server provides the document fallback |
| Static prerendering | Supported with setup | Routes configured for prerendering are emitted as static output |
| Incremental Static Regeneration | Experimental | Depends on the TanStack Start and Nitro versions in use |
| Cookies, redirects, headers | Supported | Standard framework response APIs |
| Request cancellation | Supported | Server work can observe the request's AbortSignal |
| Early Hints | Runtime-dependent | Verify in a preview deployment |
Rendering
Each route chooses how much work happens on the server.
Full SSR
Full SSR sends the route's data and rendered markup from the server:
The first request runs through the Start server entry in an Easel Function. Easel forwards HTML chunks as they render, so the shell reaches the browser while slower data resolves. TanStack Router handles later navigations in the browser.
Data-only and client-only routes
Set ssr: "data-only" to run the loader on the server and render the
component in the browser. Set ssr: false for a route that depends entirely
on browser APIs:
Server functions
Server functions hold server-only logic you can call from loaders, components, and other server functions:
TanStack Start turns client-side calls into HTTP requests to the application server function. The handler can reach databases, server-only environment variables, request headers and cookies, and the request's cancellation signal.
Keep implementation details that must never reach the browser in .server.ts
modules, and import them inside the handler as shown. TanStack Start's build
keeps those modules out of the client bundle.
Streaming from server functions
A server function can return a ReadableStream or be an async generator:
Easel keeps the response open and forwards each chunk as it's yielded. The invocation stays active until the stream closes, the client disconnects, or the function hits its maximum duration (limits). For work that must outlive the request, queue it and stream or poll its saved state.
Server routes
Server routes are HTTP endpoints for callers outside your Start app, such as webhooks, public APIs, and health checks:
They can return JSON, HTML, redirects, custom headers, ReadableStream
bodies, and Server-Sent Events. Use server functions for calls your own app
makes, and server routes for everything else.
Middleware
Start middleware adds shared behavior, such as authentication, request context, and response headers, across routes and server functions:
This middleware runs inside your application function, after Easel's security layer. Use the Firewall for infrastructure-level filtering and Start middleware for application logic.
Static prerendering
TanStack Start can prerender selected routes at build time. Easel deploys the HTML and assets Start and Nitro emit to the CDN, and requests for those pages don't invoke a function. Easel doesn't discover routes on its own, and the configuration depends on your TanStack Start version. Check the generated routes in a preview deployment after changing it.
Caching
Set Cache-Control on server-route responses, or from a server function with
setResponseHeaders:
The Easel CDN caches eligible responses from these headers. Server functions are often user-specific, so cache them publicly only when the output is safe to share. See Caching.
Nitro storage and cache utilities are only as durable as their driver. In-memory and local-filesystem drivers aren't shared across function instances, so configure an external driver for shared data.
Environment variables
Server functions, server routes, middleware, and server-side loaders read
variables from process.env. Vite inlines import.meta.env values into
client code at build time, so treat them as public and redeploy after
changing them. Set values per environment in
Environment variables.
OpenTelemetry
Register standard Node.js OpenTelemetry instrumentation at server startup to trace loaders, server functions, and server routes. Easel shows your spans in request traces. See Tracing.
Project configuration
| Setting | Default |
|---|---|
| Install command | Detected from the package manager |
| Build command | Package script or vite build |
| Development command | Package script or vite dev |
| Client and server output | Detected from the Nitro build |
In a monorepo, set the root directory to the folder that holds the app's
package.json and vite.config.
Local development
Keep using npm run dev. The Nitro plugin runs the application server inside
the Vite dev server. Use a preview deployment to
check production bundling, streaming, and CDN caching.
Known limitations
- Nitro integration maturity.
nitro/vitebuilds on Vite's environment APIs and is still changing. TanStack Start, Nitro, or Vite updates can change the server output. Pin versions when stability matters more than updates. - Incremental Static Regeneration is experimental. Use build-time prerendering or explicit cache headers for predictable results.
- Function runtime. No WebSockets, an ephemeral filesystem, per-instance memory, and no work after the response unless you register it. See Function runtime and Background work.
Troubleshooting
The build doesn't produce a server entry
Confirm that nitro is installed and that nitro() appears after
tanstackStart() in vite.config.ts. A client-only build has no server entry
for Easel to deploy.
Easel can't deploy the generated output
Remove any Nitro preset from your config and any NITRO_PRESET environment
variable. A Node, Bun, or provider preset produces a different output layout.
A server function is unavailable
Confirm that it's created with createServerFn and exported from a module
in the production build.
Server-only code appears in the client build
Move private code into a .server.ts module and import it inside the server
handler. Check that server variable names don't use Vite's public client
prefix.
Compatibility policy
Easel's TanStack Start baseline is the version under
At a glance. TanStack Start and nitro/vite move quickly, so
test upgrades in a preview deployment, as described in the
compatibility policy.
Next steps
Next.js
Deploy App Router and Pages Router applications to Easel with support for server rendering, streaming, Server Actions, route handlers, proxy (middleware), image optimization, and framework-native caching.
Nuxt
Deploy Nuxt applications to Easel with SSR, streaming, API routes, hybrid rendering, and Nitro route rules.