Start execution model
Skill fusengine/agents/plugins/tanstack-start-expert/skills/start-execution-model
TanStack Start execution model — isomorphic-by-default, environment boundary functions (createServerFn, createServerOnlyFn, createClientOnlyFn, createIsomorphicFn), <ClientOnly>, useHydrated(), server-only/client-only import markers, and env-var safety (VITE_/PUBLIC_ prefix vs process.env). Use when: deciding where code runs, fixing secret leaks, DB/filesystem in a loader, hydration mismatches, "process.env is undefined on the server". Do NOT use for: initial setup (use start-core), file/directory SOLID organization (use solid-tanstack-start).From its SKILL.md
npx -y skills add fusengine/agents --skill start-execution-modelAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 22 stars22 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
4.7 KB, 974 tokens by cl100k_base, as published. Nobody here has run it
TanStack Start Execution Model
Understanding where code runs is the single most important concept in Start,
and the #1 source of AI-generated bugs. Targets @tanstack/react-start v1.166.2.
CRITICAL — everything is isomorphic by default. Every module runs in BOTH the server and client bundles. Route loaders run on both — during SSR AND during client navigation. DB, filesystem, and secrets MUST live inside
createServerFn(orcreateServerOnlyFn), never a bare loader or module scope.CRITICAL — read
process.envper request, not at module scope. Module-level reads (1) can be inlined into the client bundle (secret leak) and (2) areundefinedon edge runtimes (Cloudflare Workers inject env at request time).CRITICAL —
VITE_/PUBLIC_prefixed vars are exposed to the client. Server secrets must have NO public prefix.
Agent Workflow (MANDATORY)
Verify APIs against Context7 (/websites/tanstack_start_framework_react) + Exa
before writing boundary code. After changes, run fuse-ai-pilot:sniper.
The Boundary APIs at a Glance
| API | Use case | Client behavior | Server behavior |
|---|---|---|---|
createServerFn() | RPC, data, mutations | network request | direct execution |
createServerOnlyFn(fn) | server utility | throws | direct execution |
createClientOnlyFn(fn) | browser utility | direct execution | throws |
createIsomorphicFn() | per-env implementation | .client() impl | .server() impl |
<ClientOnly fallback> | browser-only UI | renders children | renders fallback |
useHydrated() | post-hydration logic | true after hydrate | false |
import '@tanstack/react-start/server-only' | whole file server-only | import denied | allowed |
import '@tanstack/react-start/client-only' | whole file client-only | allowed | import denied |
All boundary creators are imported from @tanstack/react-start; <ClientOnly>
and useHydrated come from @tanstack/react-router.
The One Gotcha to Remember
// ❌ WRONG — loader is isomorphic; SECRET ships to the browser
export const Route = createFileRoute('/dashboard')({
loader: async () => {
const secret = process.env.API_SECRET // leaked
return fetch(url, { headers: { Authorization: secret } })
},
})
// ✅ CORRECT — server-only work behind createServerFn
const getData = createServerFn({ method: 'GET' }).handler(async () => {
const secret = process.env.API_SECRET // stays on the server
return fetch(url, { headers: { Authorization: secret } })
})
export const Route = createFileRoute('/dashboard')({ loader: () => getData() })
Reference Guide
| Topic | Reference | Load when |
|---|---|---|
| Why loaders/modules are isomorphic; the mental model | references/isomorphic-by-default.md | deciding server vs client vs both |
| Each boundary API + import markers + which to pick | references/environment-boundaries.md | choosing an API, marking a file |
VITE_/PUBLIC_ vs process.env, per-request reads, runtime vars | references/environment-variables.md | env var undefined / leaked |
| Complete copy-paste examples of every boundary | references/templates/boundaries.md | writing boundary code |
Decision Framework
- Server-only (
createServerFn/createServerOnlyFn): secrets, DB, filesystem, external API keys. - Client-only (
createClientOnlyFn/<ClientOnly>): DOM,localStorage, geolocation, analytics. - Isomorphic (default /
createIsomorphicFn): formatting, business logic, shared utilities, loaders.
Forbidden
- DB / secrets / filesystem in a bare loader (→
createServerFn). process.envat module scope (leak +undefinedon edge).VITE_/PUBLIC_prefix on a secret.- Non-deterministic render output (
new Date()directly) → hydration mismatch. - Dynamic
import()of a*.functions.tsserver function (bundler issues).
What ships with it: 5 files
14.8 KB alongside SKILL.md
references/
- environment-boundaries.md4.2 KB
- environment-variables.md3.9 KB
- isomorphic-by-default.md3.2 KB
- templates/boundaries.md3.5 KB
- .gitkeep0 B