Use when deciding where TanStack Start code runs, fixing secret leaks, DB-in-loader bugs, or hydration mismatches.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add fusengine/agents --skill start-execution-model --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Start Execution Model?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/fusengine-start-execution-model)More formats (shields.io, HTML) on the badges page.
---
name: start-execution-model
version: 1.0.0
description: Use when deciding where TanStack Start code runs, fixing secret leaks, DB-in-loader bugs, or hydration mismatches.
user-invocable: true
references: references/isomorphic-by-default.md, references/environment-boundaries.md, references/environment-variables.md, references/templates/boundaries.md
---
<objective>
Covers TanStack Start's execution model — where code actually runs, described as the single most important concept in Start and the #1 source of AI-generated bugs. Targets @tanstack/react-start v1.166.2.
Every module is isomorphic by default: it runs in BOTH server and client bundles, and route loaders run on both server (SSR) and client (navigation) — so DB access, filesystem, and secrets must live inside createServerFn or createServerOnlyFn, never a bare loader or module scope. process.env must be read per request, not at module scope, because module-level reads can leak into the client bundle and are undefined on edge runtimes that inject env at request time. VITE_/PUBLIC_-prefixed vars are exposed to the client, so server secrets must never carry that prefix.
Documents every environment-boundary API at a glance — createServerFn, createServerOnlyFn, createClientOnlyFn, createIsomorphicFn, <ClientOnly>, useHydrated(), and the server-only/client-only import markers — with a decision framework for which to use and complete copy-paste boundary examples.
Do NOT use this skill for initial project setup (use start-core) or for file/directory SOLID organization (use solid-tanstack-start).
</objective>
# 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` (or `createServerOnlyFn`), never a bare loader or module scope.
>
> **CRITICAL — read `process.env` per request, not at module scope.** Module-level
> reads (1) can be inlined into the client bundle (secret leak) and (2) are
> `undefined` on 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
```tsx
// ❌ 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.env` at module scope (leak + `undefined` on edge).
- `VITE_`/`PUBLIC_` prefix on a secret.
- Non-deterministic render output (`new Date()` directly) → hydration mismatch.
- Dynamic `import()` of a `*.functions.ts` server function (bundler issues).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!