storybook-addon-tanstack-start
Vite plugin + mock stubs + router context that make Storybook work with TanStack Start.
The Problem
TanStack Start has server-side code (createServerFn, SSR entry points, Nitro/Worker
entries) that crashes when Storybook tries to bundle it in the browser. This addon
intercepts those imports and replaces them with safe stubs.
Install
pnpm add -D storybook-addon-tanstack-start
One package — includes storybook-addon-tanstack-router as a dependency.
Setup
1. Add the Vite plugin to .storybook/main.ts
import { tanstackStartPlugin } from 'storybook-addon-tanstack-start/plugin'
import { mergeConfig } from 'vite'
const config = {
async viteFinal(config) {
return mergeConfig(config, {
plugins: [tanstackStartPlugin()],
})
},
}
2. Add the router decorator to .storybook/preview.ts
export { decorators } from 'storybook-addon-tanstack-start/preview'
3. Configure route params and loader data per story
import { tanstackRouterParameters } from 'storybook-addon-tanstack-start'
export const MyStory: Story = {
parameters: {
tanstackRouter: tanstackRouterParameters({
location: {
path: '/users/$userId',
params: { userId: '42' },
search: { tab: 'settings' },
},
loader: { data: { user: { name: 'Alice' } } },
}),
},
}
What the Plugin Does
- Strips TanStack/Nitro Vite plugins — prevents them from injecting server
entries that break with stubbed imports (opt out with
stripTanStackPlugins: false) - Intercepts server imports via
resolveId:@tanstack/react-startand all sub-paths@tanstack/start-server-core- Server/worker entries (
server-entry,worker-entry,virtual:cloudflare) routeTree.gen(from user code, not node_modules)- Any modules in
additionalServerModulesoption
- Provides mock exports:
createServerFn(chainable builder),createStart, cookie helpers, router stubs, component stubs
Plugin Options
tanstackStartPlugin({
// Stub additional server modules (e.g. your auth/billing server code)
additionalServerModules: ['~/lib/auth/server', '~/lib/billing/server'],
// Keep TanStack Vite plugins in the pipeline (default: true strips them)
stripTanStackPlugins: false,
})
Double RouterProvider Warning
If you already wrap stories in a custom withRouter() decorator, don't also use
the global decorator from the preview export — you'll get nested providers. Either
remove your custom decorator or skip the global one.
App-Specific Server Mocks
For your app's own server modules (auth, billing, etc.), use additionalServerModules
to redirect them to stubs, then create mock files:
// .storybook/mocks/auth-server.ts
export async function loginWithPassword({ data }) {
if (data.email === 'test@example.com' && data.password === 'password123')
return { ok: true, role: 'parent' }
return { ok: false, error: 'Invalid credentials' }
}
// in tanstackStartPlugin config:
additionalServerModules: ['~/lib/auth/server']
// in .storybook/main.ts resolve.alias:
'~/lib/auth/server': path.resolve(__dirname, 'mocks/auth-server.ts')