Documentation Index Fetch the complete documentation index at: https://mintlify.com/TanStack/router/llms.txt
Use this file to discover all available pages before exploring further.
TanStack Start provides extensive server-side configuration options for SSR, server functions, and deployment targets.
Server Entry
Customize the server entry point:
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
export default defineConfig ({
plugins: [
tanstackStart ({
server: {
entry: './src/server.ts' , // Custom server entry
},
}),
] ,
})
Default server entry (if not specified):
// Default server.ts
import { createStartHandler , defaultStreamHandler } from '@tanstack/start/server'
import { getRouterManifest } from '@tanstack/start/router-manifest'
export default createStartHandler ({
createRouter : () => import ( '#tanstack-router-entry' ) ,
getRouterManifest ,
})( defaultStreamHandler )
Server Functions
Configure how server functions are handled:
tanstackStart ({
serverFns: {
base: '/_serverFn' , // Base path for server function endpoints
generateFunctionId : ({ filename , functionName }) => {
// Custom ID generation for server functions
const cleanFilename = filename . replace ( / ^ . * \/ src \/ / , '' )
return ` ${ cleanFilename } : ${ functionName } `
},
},
})
Function ID Generation
By default, server functions receive auto-generated IDs. Customize this:
generateFunctionId : ({ filename , functionName }) => {
// Use file path + function name
return ` ${ filename } : ${ functionName } `
// Or use a hash for shorter IDs
const hash = createHash ( 'md5' )
. update ( filename + functionName )
. digest ( 'hex' )
. slice ( 0 , 8 )
return hash
}
Server Function Base Path
Server functions are served from a base path:
tanstackStart ({
serverFns: {
base: '/_serverFn' , // default
},
router: {
basepath: '/app' ,
},
})
// Functions will be at: /app/_serverFn/:functionId
Server Build Configuration
Configure server-side build options:
tanstackStart ({
server: {
build: {
staticNodeEnv: true , // Replace process.env.NODE_ENV at build time
},
},
})
Static NODE_ENV Replacement
When staticNodeEnv is enabled (default), process.env.NODE_ENV is replaced with its actual value during build:
// Development code
if ( process . env . NODE_ENV === 'development' ) {
console . log ( 'Debug information' )
}
// Production build output
// (code is eliminated completely)
Disable this if you need dynamic NODE_ENV checks:
tanstackStart ({
server: {
build: {
staticNodeEnv: false ,
},
},
})
Development Server
Configure development server behavior:
export default defineConfig ({
server: {
port: 3000 ,
host: 'localhost' ,
cors: true ,
proxy: {
'/api' : {
target: 'http://localhost:4000' ,
changeOrigin: true ,
},
},
} ,
plugins: [
tanstackStart ({
dev: {
ssrStyles: {
enabled: true , // Enable SSR styles in dev
basepath: '/' , // Base path for style URLs
},
},
}),
] ,
})
SSR Styles in Development
During development, CSS modules need special handling for SSR:
tanstackStart ({
dev: {
ssrStyles: {
enabled: true , // default: true
basepath: '/_styles' , // custom base path
},
},
})
Disable if you’re using a CSS-in-JS solution that doesn’t require it:
dev : {
ssrStyles : {
enabled : false ,
},
}
Middleware Mode
Use TanStack Start as middleware in an existing server:
export default defineConfig ({
server: {
middlewareMode: true ,
} ,
plugins: [ tanstackStart ()] ,
})
Then in your server:
import express from 'express'
import { createServer } from 'vite'
const app = express ()
const vite = await createServer ({
server: { middlewareMode: true },
})
app . use ( vite . middlewares )
app . listen ( 3000 )
Server Handler
Customize the request handler:
// src/server.ts
import { createStartHandler } from '@tanstack/start/server'
import { getRouterManifest } from '@tanstack/start/router-manifest'
export default createStartHandler ({
createRouter : () => import ( '#tanstack-router-entry' ) ,
getRouterManifest ,
})(( request , context ) => {
// Custom handler
return new Response ( 'Hello World' , {
headers: { 'Content-Type' : 'text/plain' },
})
} )
Stream Handler
Use the default stream handler for SSR:
import {
createStartHandler ,
defaultStreamHandler ,
} from '@tanstack/start/server'
export default createStartHandler ({
createRouter : () => import ( '#tanstack-router-entry' ) ,
getRouterManifest ,
})( defaultStreamHandler )
Add custom headers to all responses:
export default createStartHandler ({
createRouter : () => import ( '#tanstack-router-entry' ) ,
getRouterManifest ,
})( async ( request , context ) => {
const response = await defaultStreamHandler ( request , context )
response . headers . set ( 'X-Powered-By' , 'TanStack Start' )
response . headers . set ( 'X-Custom-Header' , 'value' )
return response
} )
Server-Only Code
Ensure code only runs on the server:
import { envOnly } from '@tanstack/start'
// This code will be removed from client bundle
const serverConfig = envOnly . server (() => {
return {
apiKey: process . env . API_KEY ,
dbConnection: process . env . DATABASE_URL ,
}
})
Import Protection
Prevent server-only imports from leaking to the client:
tanstackStart ({
importProtection: {
enabled: true ,
client: {
specifiers: [
'fs' ,
'path' ,
'crypto' ,
'node:fs' ,
'node:path' ,
],
files: [
/ \. server \. / , // Files with .server. in name
/ \/ server \/ / , // Files in server/ directory
],
},
},
})
See Import Protection for more details.
Prerendering
Configure static site generation:
tanstackStart ({
prerender: {
enabled: true ,
concurrency: 4 , // Parallel prerender workers
failOnError: true , // Fail build on prerender error
autoStaticPathsDiscovery: true , // Auto-discover static routes
maxRedirects: 5 , // Follow redirects during prerender
crawlLinks: true , // Crawl <a> tags to find pages
retryCount: 3 , // Retry failed pages
retryDelay: 1000 , // Delay between retries (ms)
headers: {
'User-Agent' : 'TanStack-Start-Prerenderer' ,
},
},
})
Page-Specific Prerender Options
tanstackStart ({
pages: [
{
path: '/' ,
prerender: {
enabled: true ,
outputPath: '/index.html' ,
autoSubfolderIndex: true , // Create /path/index.html for /path
},
},
{
path: '/blog/*' ,
prerender: {
crawlLinks: true , // Discover blog posts via links
},
},
],
})
Prerender Success Callback
tanstackStart ({
prerender: {
onSuccess : ({ page , html }) => {
console . log ( `✓ Prerendered ${ page . path } ` )
// Custom processing
const optimizedHtml = minifyHtml ( html )
return optimizedHtml
},
},
})
Deployment Targets
Node.js Server
Default setup works for Node.js:
npm run build
node dist/server/server.js
Vercel
No additional configuration needed:
// vercel.json
{
"buildCommand" : "npm run build" ,
"outputDirectory" : "dist/client"
}
Netlify
Use Netlify Edge Functions:
# netlify.toml
[ build ]
command = "npm run build"
publish = "dist/client"
[[ edge_functions ]]
function = "server"
path = "/*"
Cloudflare Workers
Configure for Workers runtime:
export default defineConfig ({
environments: {
ssr: {
build: {
target: 'es2020' , // Workers target
rollupOptions: {
output: {
format: 'esm' ,
},
},
},
},
} ,
plugins: [ tanstackStart ()] ,
})
Docker
Example Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist ./dist
EXPOSE 3000
CMD [ "node" , "dist/server/server.js" ]
Custom Server Environments
Create a custom server environment for edge deployments:
export default defineConfig ({
environments: {
client: { consumer: 'client' },
ssr: { consumer: 'server' },
edge: {
consumer: 'server' ,
build: {
ssr: true ,
target: 'es2020' ,
outDir: 'dist/edge' ,
},
},
} ,
plugins: [
tanstackStart ({
serverFn: {
providerEnv: 'edge' , // Use edge environment for server functions
},
}),
] ,
})
Environment Variables
Load environment variables:
// .env
PUBLIC_API_URL = https : //api.example.com
SECRET_KEY = secret123
// Use in server code
import { envOnly } from '@tanstack/start'
const config = envOnly . server (() => ({
apiUrl: process . env . PUBLIC_API_URL ,
secretKey: process . env . SECRET_KEY , // Only available on server
}))
Public variables (prefixed with PUBLIC_) are available on the client:
// Client-side
const apiUrl = import . meta . env . PUBLIC_API_URL
Response Caching
export default createStartHandler ({
createRouter : () => import ( '#tanstack-router-entry' ) ,
getRouterManifest ,
})( async ( request , context ) => {
const response = await defaultStreamHandler ( request , context )
// Cache static pages
if ( request . url . startsWith ( '/blog/' )) {
response . headers . set ( 'Cache-Control' , 'public, max-age=3600' )
}
return response
} )
Compression
Enable response compression:
import { compress } from '@tanstack/start/server'
export default createStartHandler ({
createRouter : () => import ( '#tanstack-router-entry' ) ,
getRouterManifest ,
})( async ( request , context ) => {
const response = await defaultStreamHandler ( request , context )
return compress ( response )
} )
Monitoring and Logging
Add request logging:
export default createStartHandler ({
createRouter : () => import ( '#tanstack-router-entry' ) ,
getRouterManifest ,
})( async ( request , context ) => {
const start = Date . now ()
const response = await defaultStreamHandler ( request , context )
const duration = Date . now () - start
console . log ( ` ${ request . method } ${ request . url } - ${ response . status } ( ${ duration } ms)` )
return response
} )
Error Handling
Custom error responses:
export default createStartHandler ({
createRouter : () => import ( '#tanstack-router-entry' ) ,
getRouterManifest ,
})( async ( request , context ) => {
try {
return await defaultStreamHandler ( request , context )
} catch ( error ) {
console . error ( 'Server error:' , error )
return new Response ( 'Internal Server Error' , {
status: 500 ,
headers: { 'Content-Type' : 'text/plain' },
})
}
} )
Next Steps
Server Functions Learn how to create and use server functions
Deployment Deploy your TanStack Start application