TanStack Start server/client code boundary patterns. Use when: debugging virtual module resolution errors (tanstack-start-injected-head-scripts, tanstack-start-manifest, #tanstack-router-entry), importing from @tanstack/react-start/server, using getCookie/getRequestHeaders/getRequestUrl, adding providers to router.tsx, or when Vite fails to resolve TanStack Start imports. Covers createServerFn patterns, dynamic imports, and proper placement of server-only code.
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
npx agent-skills-cli listSkill Instructions
name: tanstack-start-boundaries description: "TanStack Start server/client code boundary patterns. Use when: debugging virtual module resolution errors (tanstack-start-injected-head-scripts, tanstack-start-manifest, #tanstack-router-entry), importing from @tanstack/react-start/server, using getCookie/getRequestHeaders/getRequestUrl, adding providers to router.tsx, or when Vite fails to resolve TanStack Start imports. Covers createServerFn patterns, dynamic imports, and proper placement of server-only code."
TanStack Start Server/Client Boundaries
The Problem
TanStack Start uses Vite's environment API to register virtual modules (tanstack-start-injected-head-scripts:v, tanstack-start-manifest:v, etc.) only for the server environment. When you import server-only code at the top level of files that are also bundled for the client, Vite fails to resolve these virtual modules.
Error Signatures
Failed to resolve import "tanstack-start-injected-head-scripts:v"
Failed to resolve import "tanstack-start-manifest:v"
Could not resolve "#tanstack-router-entry"
Could not resolve "#tanstack-start-entry"
Root Cause
Top-level imports from @tanstack/react-start/server in files processed by both client AND server:
// __root.tsx, router.tsx, or any route file
import { getCookie } from "@tanstack/react-start/server" // BREAKS CLIENT BUNDLE
Problematic Files
These files are bundled for BOTH client and server:
src/router.tsxsrc/routes/__root.tsxsrc/routes/**/*.tsx(all route files)- Any file imported by the above
Problematic Imports
Never import these at top level in client-bundled files:
// FROM @tanstack/react-start/server
import { getCookie, setCookie } from "@tanstack/react-start/server"
import { getRequestHeaders } from "@tanstack/react-start/server"
import { getRequestUrl } from "@tanstack/react-start/server"
import { getRequest } from "@tanstack/react-start/server"
// FROM @tanstack/react-start-server (internal package)
import { getCookie } from "@tanstack/react-start-server"
Solutions
Solution 1: Use createServerFn with Dynamic Import
Wrap server-only code in a server function using dynamic imports:
// server-fns/cookie-fns.ts
import { createServerFn } from "@tanstack/react-start"
export const getThemeCookieFn = createServerFn({ method: "GET" }).handler(
async () => {
const { getCookie } = await import("@tanstack/react-start/server")
return getCookie("theme")
},
)
Then use the server function instead:
// routes/__root.tsx
import { getThemeCookieFn } from "@/server-fns/cookie-fns"
beforeLoad: async () => {
const theme = await getThemeCookieFn() // Safe - calls server function
return { theme }
}
Solution 2: Keep Server Imports in server-fns/ Files
Server function files (src/server-fns/*.ts) can use top-level imports from @tanstack/react-start/server IF they only export createServerFn functions:
// server-fns/auth-fns.ts - OK because only exports server functions
import { createServerFn } from "@tanstack/react-start"
import { getCookie } from "@tanstack/react-start/server" // OK here
export const getSessionFn = createServerFn({ method: "GET" }).handler(
async () => {
const sessionId = getCookie("session")
// ...
},
)
Warning: If any non-server-function code imports from this file, the chain breaks.
Solution 3: Move Client Providers Out of router.tsx
The router.tsx file's InnerWrap runs during SSR. Client-only providers here cause issues:
// router.tsx - BAD
import { PostHogProvider } from "./lib/posthog/provider" // Client-only!
export const getRouter = () => {
const router = createRouter({
routeTree,
InnerWrap: ({ children }) => (
<PostHogProvider>{children}</PostHogProvider> // Breaks SSR
),
})
return router
}
Move providers to __root.tsx RootDocument:
// routes/__root.tsx - GOOD
import { PostHogProvider } from "@/lib/posthog/provider"
function RootDocument({ children }) {
return (
<html>
<body>
<PostHogProvider>
{children}
</PostHogProvider>
</body>
</html>
)
}
File Organization Pattern
src/
βββ router.tsx # NO server imports, NO client-only providers
βββ routes/
β βββ __root.tsx # NO direct server imports, use server-fns
β βββ *.tsx # NO direct server imports, use server-fns
βββ server-fns/ # Server functions with createServerFn
β βββ auth-fns.ts # Can import from @tanstack/react-start/server
β βββ cookie-fns.ts # Wraps getCookie in createServerFn
βββ lib/
βββ posthog/
βββ provider.tsx # "use client" - add to __root.tsx body, not router.tsx
Quick Diagnostic
When you see virtual module errors:
-
Search for
@tanstack/react-start/serverimports outsideserver-fns/:grep -r "@tanstack/react-start/server" src/ --include="*.tsx" | grep -v server-fns -
Check router.tsx for client-only provider imports
-
Look for import chains that pull server code into client bundles
GitHub References
- Issue #5196: Server imports in route files
- Issue #6189: Virtual module resolution failures
- Issue #5795: #tanstack-router-entry resolution
More by wodsmith
View allDocumentation guidance for competition athletes and volunteers in WODsmith. Use when writing, reviewing, or improving athlete-facing documentation including registration, scheduling, workout viewing, leaderboards, check-in, and volunteer coordination.
Implement drag and drop using @atlaskit/pragmatic-drag-and-drop. Use when implementing sortable lists, reorderable items, kanban boards, or any drag-drop interactions. Covers draggable setup, drop targets, edge detection, drag previews, and critical state management patterns to avoid performance issues.
Handle local database schema changes with Drizzle and PlanetScale. Use when making schema changes to src/db/schema.ts, adding/modifying database tables or columns, or when asked about database migrations. Covers the push-based local development workflow and when to generate migrations.
Guide for working with team-based permissions and authorization in the WODsmith codebase. Use when touching TEAM_PERMISSIONS constants, hasTeamPermission/requireTeamPermission functions, adding permission checks to actions or server functions, creating features requiring authorization, or ensuring client-server permission consistency.
