{"id":"c441c0bd-8d92-4634-a5a9-804e854503a9","entityType":"agent","slug":"clawhub-tenequm-typescript-dev","name":"typescript-dev","canonicalUrl":"https://www.xpersona.co/agent/clawhub-tenequm-typescript-dev","canonicalPath":"/agent/clawhub-tenequm-typescript-dev","generatedAt":"2026-10-10T10:42:19.395Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T05:02:37.432Z","emptyReason":null},"description":"Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - components, styling, build and HMR, tests, lint/CI, or a Hono API with type-safe RPC. Skill: typescript-dev Owner: tenequm Summary: Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - components, styling, build and HMR, tests, lint/CI, or a Hono API with type-safe RPC. Tags: latest:0.4.0 Version history: v0.4.0 | 2026-10-06T12:21:40.608Z | user Updated typescript-dev from 0.3.5 to 0.4.0. Changes: - mod","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.7K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17bp3v1hm1dnkzey0c9tfh02183j0y5:typescript-dev","sourceUrl":"https://clawhub.ai/tenequm/typescript-dev","homepage":"https://clawhub.ai/tenequm/skills/typescript-dev","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/tenequm/typescript-dev","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/tenequm/skills/typescript-dev","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":64,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - compone"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:02:37.432Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:02:37.432Z","emptyReason":null},"stars":null,"forks":null,"downloads":1668,"packageName":null,"latestVersion":"0.4.0","tractionLabel":"1.7K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:02:37.432Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T05:02:37.432Z","lastCrawledAt":"2026-10-10T05:02:37.432Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T05:02:37.432Z","lastVerifiedAt":null,"highlights":[{"version":"0.4.0","createdAt":"2026-10-06T12:21:40.608Z","changelog":"Updated typescript-dev from 0.3.5 to 0.4.0. Changes: - modified `CHANGELOG.md` - modified `SKILL.md` - modified `references/biome.md` - modified `references/hono.md` - modified `references/react.md` - modified `references/shadcn.md` - modified `references/tailwind.md` - modified `references/typescript.md` - modified `references/vite.md` - modified `references/vitest.md`","fileCount":13,"zipByteSize":62951},{"version":"0.3.5","createdAt":"2026-09-09T10:13:45.226Z","changelog":"Updated typescript-dev from 0.3.4 to 0.3.5. Changes: - modified `CHANGELOG.md` - modified `SKILL.md`","fileCount":13,"zipByteSize":53528},{"version":"0.3.4","createdAt":"2026-08-21T12:22:32.648Z","changelog":"Updated typescript-dev from 0.3.3 to 0.3.4. Changes: - modified `CHANGELOG.md` - modified `SKILL.md` - deleted `skill-card.md`","fileCount":13,"zipByteSize":53550},{"version":"0.3.3","createdAt":"2026-08-07T13:57:36.081Z","changelog":"Updated typescript-dev from 0.3.2 to 0.3.3. Changes: - modified `CHANGELOG.md` - modified `SKILL.md` - modified `skill-card.md`","fileCount":13,"zipByteSize":53617},{"version":"0.3.2","createdAt":"2026-07-22T18:48:24.040Z","changelog":"Updated typescript-dev from 0.3.1 to 0.3.2. Changes: - modified `CHANGELOG.md` - modified `SKILL.md` - added `skill-card.md`","fileCount":13,"zipByteSize":53512},{"version":"0.3.1","createdAt":"2026-07-10T13:51:31.590Z","changelog":"Updated typescript-dev from 0.3.0 to 0.3.1. Changes: - modified `CHANGELOG.md` - modified `SKILL.md`","fileCount":13,"zipByteSize":53365},{"version":"0.3.0","createdAt":"2026-07-01T11:17:50.320Z","changelog":"Updated typescript-dev from 0.2.0 to 0.3.0. Changes: - modified `CHANGELOG.md` - modified `SKILL.md` - modified `references/biome.md` - modified `references/hono.md` - modified `references/react.md` - modified `references/shadcn.md` - modified `references/tailwind.md` - modified `references/typescript.md` - modified `references/vite.md` - modified `references/vitest.md`","fileCount":13,"zipByteSize":53189},{"version":"0.2.0","createdAt":"2026-06-09T11:47:06.662Z","changelog":"Updated typescript-dev from 0.1.0 to 0.2.0. Changes: - modified `CHANGELOG.md` - modified `SKILL.md` - added `references/hono.md` - modified `references/shadcn.md`","fileCount":13,"zipByteSize":49536}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17bp3v1hm1dnkzey0c9tfh02183j0y5:typescript-dev","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T10:42:19.391Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-tenequm-typescript-dev/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T05:02:37.432Z","emptyReason":null},"readme":"Skill: typescript-dev\n\nOwner: tenequm\n\nSummary: Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - components, styling, build and HMR, tests, lint/CI, or a Hono API with type-safe RPC.\n\nTags: latest:0.4.0\n\nVersion history:\n\nv0.4.0 | 2026-10-06T12:21:40.608Z | user\n\nUpdated typescript-dev from 0.3.5 to 0.4.0.\nChanges:\n- modified `CHANGELOG.md`\n- modified `SKILL.md`\n- modified `references/biome.md`\n- modified `references/hono.md`\n- modified `references/react.md`\n- modified `references/shadcn.md`\n- modified `references/tailwind.md`\n- modified `references/typescript.md`\n- modified `references/vite.md`\n- modified `references/vitest.md`\n\nv0.3.5 | 2026-09-09T10:13:45.226Z | user\n\nUpdated typescript-dev from 0.3.4 to 0.3.5.\nChanges:\n- modified `CHANGELOG.md`\n- modified `SKILL.md`\n\nv0.3.4 | 2026-08-21T12:22:32.648Z | user\n\nUpdated typescript-dev from 0.3.3 to 0.3.4.\nChanges:\n- modified `CHANGELOG.md`\n- modified `SKILL.md`\n- deleted `skill-card.md`\n\nv0.3.3 | 2026-08-07T13:57:36.081Z | user\n\nUpdated typescript-dev from 0.3.2 to 0.3.3.\nChanges:\n- modified `CHANGELOG.md`\n- modified `SKILL.md`\n- modified `skill-card.md`\n\nv0.3.2 | 2026-07-22T18:48:24.040Z | user\n\nUpdated typescript-dev from 0.3.1 to 0.3.2.\nChanges:\n- modified `CHANGELOG.md`\n- modified `SKILL.md`\n- added `skill-card.md`\n\nv0.3.1 | 2026-07-10T13:51:31.590Z | user\n\nUpdated typescript-dev from 0.3.0 to 0.3.1.\nChanges:\n- modified `CHANGELOG.md`\n- modified `SKILL.md`\n\nv0.3.0 | 2026-07-01T11:17:50.320Z | user\n\nUpdated typescript-dev from 0.2.0 to 0.3.0.\nChanges:\n- modified `CHANGELOG.md`\n- modified `SKILL.md`\n- modified `references/biome.md`\n- modified `references/hono.md`\n- modified `references/react.md`\n- modified `references/shadcn.md`\n- modified `references/tailwind.md`\n- modified `references/typescript.md`\n- modified `references/vite.md`\n- modified `references/vitest.md`\n\nv0.2.0 | 2026-06-09T11:47:06.662Z | user\n\nUpdated typescript-dev from 0.1.0 to 0.2.0.\nChanges:\n- modified `CHANGELOG.md`\n- modified `SKILL.md`\n- added `references/hono.md`\n- modified `references/shadcn.md`\n\nv0.1.0 | 2026-06-05T21:34:44.849Z | user\n\nInitial publish of typescript-dev 0.1.0.\nChanges:\n- added `CHANGELOG.md`\n- moved in `skills/biome/LICENSE.txt` -> `LICENSE.txt`\n- added `SKILL.md`\n- added `references/biome.md`\n- added `references/react.md`\n- added `references/shadcn.md`\n- added `references/tailwind.md`\n- added `references/typescript.md`\n- added `references/vite.md`\n- added `references/vitest.md`\n\nArchive index:\n\nArchive v0.4.0: 13 files, 62951 bytes\n\nFiles: CHANGELOG.md (9677b), LICENSE.txt (9157b), references/biome.md (12243b), references/hono.md (31691b), references/react.md (11073b), references/shadcn.md (8507b), references/tailwind.md (6781b), references/typescript.md (8746b), references/vite.md (15156b), references/vitest.md (9319b), skill-card.md (1925b), SKILL.md (15716b), _meta.json (133b)\n\nFile v0.4.0:SKILL.md\n\n---\nname: typescript-dev\ndescription: Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - components, styling, build and HMR, tests, lint/CI, or a Hono API with type-safe RPC.\nmetadata:\n  version: \"0.4.0\"\n  categories: \"development\"\n  topics: \"typescript, vite, react, tailwind, hono\"\n  openclaw:\n    homepage: https://github.com/tenequm/skills/tree/main/skills/typescript-dev\n    emoji: \"🟦\"\n  upstream: \"vite@8.3.3, @vitejs/plugin-react@6.1.2, react@19.3.0, typescript@7.0.2, tailwindcss@4.3.3, @biomejs/biome@2.5.15, vitest@5.0.3, babel-plugin-react-compiler@1.0.0, class-variance-authority@0.7.1, hono@4.13.13, shadcn@4.21.3, cn@0.4.0\"\n---\n\n# TypeScript Frontend Development\n\nOne coherent stack for building type-safe TypeScript apps: **Vite 8** (build + dev server, Rolldown-powered), **React 19.3** with the React Compiler, **TypeScript 7.0** (strict, Go-native), **Tailwind CSS v4.3 + shadcn/ui** for styling, **Biome 2.5** for linting and formatting, **Vitest 5** for testing, and **Hono 4** for the backend/edge API. The pieces are designed to fit together - this skill covers how they wire up and the sharp edges that span more than one of them. Hono's RPC client (`hc`) shares server types directly with the React frontend, so the front and back end stay type-safe end to end without codegen.\n\nThe body below is the cross-cutting layer: the rules that bite when these tools meet, plus one working end-to-end setup. Each tool also has a deep-dive reference - read the one you need:\n\n- **[references/vite.md](references/vite.md)** - Vite 8 config, dev server, proxy, HMR, Rolldown, code splitting, build optimization, deployment.\n- **[references/react.md](references/react.md)** - React 19 patterns: Actions, `use()`, Activity, `<ViewTransition>`, Fragment refs, `useEffectEvent`, document metadata, and the React Compiler.\n- **[references/typescript.md](references/typescript.md)** - Strict TypeScript 7.0 config and patterns: tsconfig defaults, the 6.0 compat path for API-consuming tools, generics, `import defer`.\n- **[references/tailwind.md](references/tailwind.md)** - Tailwind CSS v4 CSS-first config, OKLCH theming, dark mode, v4.3 utilities.\n- **[references/shadcn.md](references/shadcn.md)** - shadcn/ui CLI, component authoring with CVA + `data-slot`, the `cn` package, registries, Base UI / Radix / React Aria.\n- **[references/biome.md](references/biome.md)** - Biome config, `biome check`, domains, type-aware linting, GritQL, ESLint/Prettier migration.\n- **[references/vitest.md](references/vitest.md)** - Vitest 5 config, Testing Library, mocking, coverage, browser mode, projects, v4->v5 migration, test speed.\n- **[references/hono.md](references/hono.md)** - Hono 4 web framework: routing, context, middleware, validation (Zod), end-to-end type-safe RPC, OpenAPI, helpers, and multi-runtime deployment (Workers/Node/Bun/Deno).\n\n## Version targets\n\n| Tool | Version | Note |\n|------|---------|------|\n| Vite | 8.3.3 | Rolldown is the single default bundler |\n| @vitejs/plugin-react | 6.1.2 | v6 removed the inline `babel` option |\n| React / react-dom | 19.3.0 | React Compiler is stable (1.0) |\n| babel-plugin-react-compiler | 1.0.0 | pin with `--save-exact` |\n| TypeScript | 7.0.2 | Go-native, ~10x faster; ships no JS API (6.0 compat path in typescript.md) |\n| Tailwind CSS | 4.3.3 | CSS-first config, no JS config file |\n| shadcn/ui CLI | 4.21.3 | Base UI is the default base; `cn` is its own package |\n| Biome | 2.5.15 | single binary for lint + format + imports |\n| Vitest | 5.0.3 | Vite-native test runner; reuses vite.config |\n| Hono | 4.13.13 | Web Standards backend/edge framework; v5 in development |\n\n**Node.js 22.12+** is the effective floor for the stack: Vite alone accepts 20.19+, but Vitest 5 and `@rolldown/plugin-babel` both require 22.12+.\n\n## Cross-cutting critical rules\n\nThese are the rules that fail in confusing ways precisely because they sit at the seam between two tools. The single-tool details live in the references.\n\n### Vite plugin order: framework plugins first, `react()` last\n\nWhen a framework plugin (TanStack Router/Start, etc.) generates routes or transforms code, it must run before `@vitejs/plugin-react` so React's Fast Refresh transform sees the final output. Wrong order causes route-generation failures and broken HMR.\n\n```ts\nplugins: [\n  tanstackStart(),   // or tanstackRouter() for SPA - framework first\n  tailwindcss(),\n  react(),           // React plugin last among framework plugins\n]\n```\n\n### React Compiler replaces manual memoization - and changes how you wire Vite\n\nReact Compiler 1.0 auto-memoizes components, computations, and callbacks at build time. Write plain components; do not reach for `useMemo`/`useCallback`/`memo`. The catch lives at the Vite seam: **`@vitejs/plugin-react` v6 removed the inline `babel` option**, so the old `react({ babel: { plugins: [...] } })` wiring no longer works. The compiler now runs through a separate Babel plugin:\n\n```ts\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\n\nplugins: [react(), babel({ presets: [reactCompilerPreset()] })]\n```\n\nInstall: `pnpm add -D @rolldown/plugin-babel @babel/core babel-plugin-react-compiler @types/babel__core`.\n\nplugin-react 6.1 also ships an **experimental** native (Rust) compiler path, `react({ compiler: true })`, that skips Babel entirely - see [react.md](references/react.md) for its install trap. The Babel wiring above remains the stable route.\n\nThis also ripples into Biome: `useExhaustiveDependencies` can't tell the compiler is handling deps for you, so most compiler users turn it off (see [biome.md](references/biome.md)).\n\n### Tailwind v4 is CSS-first - there is no `tailwind.config.js`\n\nTailwind v4 configures everything in CSS via `@theme`, `@utility`, `@plugin`, `@source`. Never create or look for `tailwind.config.js`/`.ts`. The Vite integration is the `@tailwindcss/vite` plugin (no PostCSS config either). If you find a `tailwind.config.js` in a v4 project, it is leftover - delete it and migrate the values into CSS. Full details in [tailwind.md](references/tailwind.md).\n\n### Style with semantic tokens, never raw palette or dynamic class names\n\n```tsx\n<div className=\"bg-primary text-primary-foreground\">   // respects theme + dark mode\n<div className=\"bg-blue-500 text-white\">               // breaks theming - avoid\n```\n\nAnd never assemble class names from fragments (`bg-${color}-500`) - Tailwind's scanner only sees complete literal strings, so dynamic names silently produce no CSS. Use a lookup map of full class strings.\n\n### TypeScript 7.0: lean on the new defaults, and know it has no JS API\n\nTS 6.0 and 7.0 bake in much of what used to be manual: `strict` and `noUncheckedSideEffectImports` are **on by default**, so drop them from a fresh tsconfig. Two defaults break a Vite app if ignored: `types` defaults to `[]`, and side-effect imports are now checked - so `import \"./styles.css\"` fails (TS2882) and `import.meta.env` is untyped (TS2339) until you add `\"types\": [\"vite/client\"]` (append `\"node\"` etc. as needed). `baseUrl` is deprecated - use prefixed `paths`.\n\nA plain `pnpm add -D typescript` now installs **7.0**, which ships the `tsc` binary but **no programmatic API** (it returns until 7.1). This stack is fine - Biome, plugin-react, and the shadcn CLI never import `typescript` - but typescript-eslint, framework checkers (Vue, Astro, Svelte, MDX), and anything else that does `import ts from \"typescript\"` need the official 6.0 alias pair. See [typescript.md](references/typescript.md).\n\n### One Biome command, and `files.includes` is the only include key\n\nRun `biome check` (or `biome ci`) - it formats, lints, and organizes imports in a single pass; never split into separate `lint`+`format` calls. And in Biome 2.x the only file-selection key is `files.includes` (with the `s`); `files.ignore`/`files.include`/`files.exclude` do not exist and throw `Found an unknown key`. Exclude with negation: `\"includes\": [\"**\", \"!**/routeTree.gen.ts\"]`. More in [biome.md](references/biome.md).\n\n### Hono RPC ties the backend's types to the React frontend - keep them in sync\n\nWhen the API is Hono, the React app talks to it through the `hc<AppType>()` client, which\nimports the server's exported `typeof app` directly. That shared type is the seam: it only\nworks if **both sides run the same Hono version** and both `tsconfig.json` set `\"strict\": true`\n(a mismatch throws \"Type instantiation is excessively deep\"). Two more rules that bite at this\nseam: handlers must specify status codes (`c.json(data, 200)`) for the client to infer\nresponses, and routes the client calls must not use `c.notFound()`. As the route count grows,\ncompile the client type once (`hcWithType`) so the IDE stays fast. Run `pnpm why hono` to catch a\nsecond copy pulled in by an adapter's peer range. If Hono handles CORS behind the Vite dev server,\nset `server.cors: false` in `vite.config.ts` so the two CORS layers don't conflict. Full details in\n[hono.md](references/hono.md).\n\n## End-to-end setup\n\nA minimal but complete React + TypeScript + Tailwind + Biome project. Swap the framework plugin for your router/SSR choice (see [vite.md](references/vite.md) for TanStack and Cloudflare variants).\n\n### vite.config.ts\n\n```ts\nimport { defineConfig } from 'vite'\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n  plugins: [\n    tailwindcss(),\n    react(),\n    babel({ presets: [reactCompilerPreset()] }),\n  ],\n  resolve: {\n    alias: { '@': new URL('./src', import.meta.url).pathname },\n  },\n})\n```\n\n`import.meta.url` is the ESM-correct way to resolve paths - there is no `__dirname` in an ESM config, and Vite configs are ESM-only.\n\n### tsconfig.json (TypeScript 7.0, also valid on 6.0)\n\n```jsonc\n{\n  \"compilerOptions\": {\n    // strict + noUncheckedSideEffectImports are ON by default since 6.0 - omitted on purpose\n    \"target\": \"es2023\",\n    \"module\": \"preserve\",\n    \"moduleResolution\": \"bundler\",\n    \"moduleDetection\": \"force\",\n    \"jsx\": \"react-jsx\",\n    \"verbatimModuleSyntax\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"erasableSyntaxOnly\": true,\n    \"skipLibCheck\": true,\n    \"noEmit\": true,\n    \"types\": [\"vite/client\"],\n    \"paths\": { \"@/*\": [\"./src/*\"] }\n  },\n  \"include\": [\"src\"]\n}\n```\n\n`module: preserve` + `moduleResolution: bundler` is the right pairing for a Vite-bundled app; use `nodenext` instead only for Node-executed code. `\"types\": [\"vite/client\"]` is load-bearing: it declares CSS/asset modules and `import.meta.env`, without which the default-on side-effect-import check rejects `import \"./styles.css\"`. Other ambient types stay out until you list them (`\"node\"`, `\"vitest/globals\"`). `exactOptionalPropertyTypes` means an optional prop that may receive `undefined` must be declared `prop?: T | undefined`.\n\n### biome.json\n\n```json\n{\n  \"$schema\": \"./node_modules/@biomejs/biome/configuration_schema.json\",\n  \"vcs\": { \"enabled\": true, \"clientKind\": \"git\", \"useIgnoreFile\": true },\n  \"files\": { \"includes\": [\"**\", \"!**/components/ui\", \"!**/routeTree.gen.ts\"] },\n  \"formatter\": { \"enabled\": true, \"indentStyle\": \"space\", \"lineWidth\": 100 },\n  \"linter\": {\n    \"enabled\": true,\n    \"rules\": { \"preset\": \"recommended\" },\n    \"domains\": { \"react\": \"recommended\" }\n  },\n  \"javascript\": { \"formatter\": { \"quoteStyle\": \"double\" } },\n  \"assist\": { \"enabled\": true, \"actions\": { \"source\": { \"organizeImports\": \"on\" } } }\n}\n```\n\n### src/styles.css\n\n```css\n@import \"tailwindcss\";\n\n:root {\n  --background: oklch(1 0 0);\n  --foreground: oklch(0.145 0 0);\n  --primary: oklch(0.205 0 0);\n  --primary-foreground: oklch(0.985 0 0);\n  --radius: 0.5rem;\n}\n.dark {\n  --background: oklch(0.145 0 0);\n  --foreground: oklch(0.985 0 0);\n  --primary: oklch(0.922 0 0);\n  --primary-foreground: oklch(0.205 0 0);\n}\n\n@theme inline {\n  --color-background: var(--background);\n  --color-foreground: var(--foreground);\n  --color-primary: var(--primary);\n  --color-primary-foreground: var(--primary-foreground);\n}\n```\n\nThe `@import \"tailwindcss\";` line is load-bearing: the `@tailwindcss/vite` plugin alone produces no styles without it - a missing import is the classic \"Tailwind renders nothing\" footgun. Use `@theme inline` (not plain `@theme`) for tokens that reference CSS variables, so they track dark-mode changes.\n\n### A component, the way the whole stack wants it\n\nPlain function, `ref` as a regular prop (no `forwardRef`), native element props via `React.ComponentProps`, variants via CVA, `data-slot` for styling hooks, and no manual memoization - the compiler handles it.\n\n```tsx\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        outline: \"border border-input bg-background hover:bg-accent\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n      },\n      size: { default: \"h-9 px-4 py-2\", sm: \"h-8 px-3\", lg: \"h-10 px-8\" },\n    },\n    defaultVariants: { variant: \"default\", size: \"default\" },\n  }\n)\n\nfunction Button({\n  className,\n  variant,\n  size,\n  ref,\n  ...props\n}: React.ComponentProps<\"button\"> & VariantProps<typeof buttonVariants>) {\n  return (\n    <button\n      ref={ref}\n      data-slot=\"button\"\n      className={cn(buttonVariants({ variant, size }), className)}\n      {...props}\n    />\n  )\n}\n```\n\nNote the `cn()` order: defaults first, consumer `className` last, so tailwind-merge's last-wins resolution lets callers override.\n\n## Best practices\n\n1. **Let the compiler optimize.** Write plain components and computations; reserve `useMemo`/`useCallback` for the rare case where you need a value to be a stable effect dependency.\n2. **Model state as discriminated unions, not loose booleans** (`{ status: \"loading\" } | { status: \"error\"; error }`) so impossible states are unrepresentable.\n3. **Extend native props with `React.ComponentProps<\"el\">`** instead of re-declaring HTML attributes by hand.\n4. **Use `use()` over `useContext()`** - it works after early returns and inside conditionals.\n5. **Semantic color tokens only**, and always pair `bg-*` with the matching `text-*-foreground`.\n6. **`biome check --write`** is your one local command; `biome ci` in pipelines.\n7. **Rolldown is the default bundler in Vite 8** - no opt-in needed; split stable vendor code with Rolldown's `codeSplitting` (see [vite.md](references/vite.md)).\n8. **Pin exact versions for tooling that rewrites code** (`babel-plugin-react-compiler`, `@biomejs/biome`) to avoid surprise diffs between releases.\n9. **Keep secrets off the client** - only `VITE_`-prefixed env vars reach browser code via `import.meta.env`.\n10. **Test through `vite.config.ts`** - Vitest reuses your build config, so tests see the same aliases and transforms; `vitest run` in CI, `jsdom` for component tests.\n\n## Resources\n\n- Vite: https://vite.dev/guide/ - Vite 8 blog: https://vite.dev/blog/announcing-vite8\n- React 19.3: https://react.dev/blog/2026/09/09/react-19-3 - Compiler: https://react.dev/learn/react-compiler\n- TypeScript 7.0: https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/\n- Vitest 5: https://vitest.dev/blog/vitest-5 - Hono: https://hono.dev/docs/\n- Tailwind CSS: https://tailwindcss.com/docs - shadcn/ui: https://ui.shadcn.com/docs\n- Biome: https://biomejs.dev/\n\nFile v0.4.0:_meta.json\n\n{\n  \"ownerId\": \"kn76gpsgjw5chv0xvzbzcb8cxn81x46r\",\n  \"slug\": \"typescript-dev\",\n  \"version\": \"0.4.0\",\n  \"publishedAt\": 1791289300608\n}\n\nFile v0.4.0:references/biome.md\n\n# Biome\n\nFast, unified linting, formatting, and import organization for JS/TS/JSX/CSS/GraphQL in a single binary. Biome **2.5** (latest `2.5.15`) does type-aware linting without the TypeScript compiler, GritQL plugins for custom rules, and domain-based rule grouping. Zero config by default, ~97% Prettier compatibility.\n\n## Critical rules\n\n### `files.includes` is the only file key\n\nBiome 2.x supports only `files.includes` (with the `s`). There is **no** `files.ignore`, `files.include`, or `files.exclude` - any of them throws `Found an unknown key`. The valid `files` keys are `includes`, `maxSize`, `ignoreUnknown`. Exclude with negation patterns:\n\n```json\n{ \"files\": { \"includes\": [\"**\", \"!**/routeTree.gen.ts\", \"!**/generated/**\"] } }\n```\n\nFor paths the scanner must skip entirely (even for assists), use the `!!` force-ignore prefix - it replaces the deprecated `experimentalScannerIgnores`:\n\n```json\n{ \"files\": { \"includes\": [\"**\", \"!!**/legacy-vendor/**\"] } }\n```\n\n**`!**/dir` also matches ancestor directories.** The pattern is tested against the whole path, so if the project itself lives under a directory with that name - e.g. an agent worktree at `<repo>/.claude/worktrees/x/` with `\"!**/.claude\"` in its config - every file is excluded and Biome checks 0 files (2.5.15 reports `No files were processed`; older setups could pass vacuously). Root-anchor exclusions that refer to the project's own top-level folders (`\"!.claude\"`, `\"!dist\"`) and keep `**/` for names that genuinely recur at depth.\n\n### One command: `biome check`\n\n`biome check` runs formatter + linter + import organizer in one pass. Never split into separate `biome lint` and `biome format` in CI - use `biome check` (or `biome ci` for CI mode).\n\n```bash\nbiome check --write .            # apply safe fixes\nbiome check --write --unsafe .   # include unsafe fixes (review the diff)\n```\n\nRemoving unused imports/variables is classified **unsafe** (an external caller might reference the symbol), so plain `--write` reports but doesn't delete them - use `--write --unsafe` or remove by hand. Prefer `--write` over the `--fix` alias for consistency.\n\n### Pin versions, migrate after upgrades\n\n```bash\npnpm add --save-dev --save-exact @biomejs/biome@latest\npnpm biome migrate --write\n```\n\nThe `$schema` is version-pinned; after bumping the binary, the CLI errors with `The configuration schema version does not match the CLI version` until you run `biome migrate --write`. Do it as part of the upgrade.\n\n### `biome.json` at the project root\n\nOne config at the root; monorepo packages use `\"extends\": \"//\"` to inherit. Never reference it with a relative path like `\"../../biome.json\"`.\n\n## Quick start\n\n```bash\npnpm add --save-dev --save-exact @biomejs/biome\npnpm biome init\n```\n\n### Recommended config (React/TypeScript)\n\n```json\n{\n  \"$schema\": \"./node_modules/@biomejs/biome/configuration_schema.json\",\n  \"vcs\": { \"enabled\": true, \"clientKind\": \"git\", \"useIgnoreFile\": true },\n  \"files\": { \"includes\": [\"**\", \"!**/components/ui\", \"!**/routeTree.gen.ts\"] },\n  \"formatter\": { \"enabled\": true, \"indentStyle\": \"space\", \"lineWidth\": 100 },\n  \"linter\": {\n    \"enabled\": true,\n    \"rules\": { \"preset\": \"recommended\" },\n    \"domains\": { \"react\": \"recommended\" }\n  },\n  \"javascript\": { \"formatter\": { \"quoteStyle\": \"double\" } },\n  \"assist\": { \"enabled\": true, \"actions\": { \"source\": { \"organizeImports\": \"on\" } } }\n}\n```\n\nBiome 2.5 **deprecated `linter.rules.recommended`** in favor of `linter.rules.preset` (`\"recommended\"`, `\"all\"`, or `\"none\"`); the boolean still works but emits an info-level deprecation diagnostic - run `biome migrate --write` to convert. Groups take their own `preset` too (`\"style\": { \"preset\": \"none\" }`), as does `assist.actions`. The `domains` values (`\"recommended\"`/`\"all\"`/`\"none\"`) are unaffected.\n\n### IDE setup\n\nVS Code - install `biomejs.biome`:\n\n```json\n{\n  \"editor.defaultFormatter\": \"biomejs.biome\",\n  \"editor.formatOnSave\": true,\n  \"editor.codeActionsOnSave\": {\n    \"source.fixAll.biome\": \"explicit\",\n    \"source.organizeImports.biome\": \"explicit\"\n  }\n}\n```\n\nZed uses the Biome extension natively. Note the spelling: Zed's inline-config key is `inline_config` (snake_case); the VS Code extension uses `inlineConfig` (camelCase).\n\n### CI\n\n```bash\npnpm biome ci .                                          # no writes, non-zero exit on errors\npnpm biome ci --reporter=github .                        # GitHub Actions annotations\npnpm biome ci --reporter=concise .                       # short one-line-per-diagnostic output\n```\n\n`--reporter=concise` (Biome 2.5) prints one `file:line:col: rule: message` line per diagnostic - the recommended reporter when an AI coding agent reads the output, since it saves tokens versus the default rich format.\n\n## Configuration details\n\n### Import organizer\n\nThe organizer (a Biome Assist action, not a lint rule) merges duplicates, sorts by distance, and supports custom grouping:\n\n```json\n{\n  \"assist\": { \"actions\": { \"source\": { \"organizeImports\": {\n    \"level\": \"on\",\n    \"options\": { \"groups\": [\n      { \"source\": \"builtin\" }, { \"source\": \"external\" },\n      { \"source\": \"internal\", \"match\": \"@company/*\" }, { \"source\": \"relative\" }\n    ] }\n  } } } }\n}\n```\n\n### Per-subsystem includes and overrides\n\nEach subsystem (`linter`, `formatter`, `assist`) has its own `includes`, applied after `files.includes` (can only narrow). `overrides` apply different settings to file patterns - the field is `includes` (with `s`):\n\n```json\n{\n  \"overrides\": [\n    { \"includes\": [\"**/components/ui/**\"], \"linter\": { \"rules\": { \"style\": { \"useComponentExportOnlyModules\": \"off\" } } } },\n    { \"includes\": [\"**/*.test.ts\"], \"linter\": { \"rules\": { \"suspicious\": { \"noConsole\": \"off\" } } } }\n  ]\n}\n```\n\n### Monorepo\n\nRoot holds shared config; packages inherit with `\"extends\": \"//\"` and add their own `linter.rules` as needed.\n\n## Domains\n\nDomains group lint rules by technology - enable what your stack uses. Levels: `\"recommended\"` (the domain's recommended stable rules), `\"all\"` (every stable rule in the domain), `\"none\"`. **Domains never enable nursery rules** - not even at `\"all\"` - so a nursery rule listed under a domain must be turned on individually under `linter.rules.nursery`.\n\n```json\n{ \"linter\": { \"domains\": { \"react\": \"recommended\", \"test\": \"recommended\", \"types\": \"all\" } } }\n```\n\nCommon domains: `react` (auto-detected at `react >= 16`), `next`, `solid`, `vue`, `test` (jest/vitest/mocha/ava), `playwright`, `drizzle`, `tailwind` (auto-detected at `tailwindcss >= 3`; all its rules are nursery), `project` (cross-file: `noImportCycles`, `noUnresolvedImports`), `types` (type inference). The `project` and `types` domains trigger a file scan with small overhead.\n\n## Type-aware linting\n\nBiome has its own Rust type-inference engine - no `typescript` dependency needed. The headline promise rules are still **nursery**, so the `types` domain alone does not turn them on - enable them explicitly:\n\n```json\n{\n  \"linter\": {\n    \"domains\": { \"types\": \"all\" },\n    \"rules\": {\n      \"nursery\": {\n        \"noFloatingPromises\": \"error\",\n        \"noMisusedPromises\": \"error\",\n        \"useAwaitThenable\": \"error\"\n      }\n    }\n  }\n}\n```\n\n| Rule | Group | Catches |\n|------|-------|---------|\n| `noFloatingPromises` | nursery | unhandled promises (missing await/return/void) |\n| `noMisusedPromises` | nursery | promises in conditionals or array callbacks |\n| `useAwaitThenable` | nursery | awaiting non-thenables |\n| `noUnnecessaryConditions` | stable (`types` domain) | always-true/false conditions |\n| `useStrictBooleanExpressions` | nursery (2.5.15) | ambiguous truthiness like `if (count)` on `number \\| undefined` |\n| `noUnsafeTypeAssertion` | nursery | `as` assertions (const assertions allowed) |\n\n```ts\nasync function loadData() { fetch(\"/api\") }        // ERROR: floating promise\nasync function loadData() { void fetch(\"/api\") }   // OK: explicit fire-and-forget\n```\n\n### React Compiler interaction\n\nIf you use the React Compiler, `useExhaustiveDependencies` can't tell the compiler is handling memoization, so most compiler users turn it off:\n\n```json\n{ \"linter\": { \"rules\": { \"correctness\": { \"useExhaustiveDependencies\": \"off\" } } } }\n```\n\nThe nursery rule `useReactCompiler` (2.5.8+, React domain) reports React Compiler lint-mode diagnostics - code the compiler will bail out on - without ESLint. Enable it with `\"nursery\": { \"useReactCompiler\": \"error\" }`.\n\n### Tailwind v4 CSS\n\nTo lint CSS that uses Tailwind at-rules, enable `css.parser.tailwindDirectives` so Biome parses `@theme`, `@utility`, and `@apply` instead of erroring on them.\n\nThe `tailwind` domain's nursery rules lint class strings in JSX - enable them individually:\n\n```json\n{ \"linter\": { \"rules\": { \"nursery\": {\n  \"noTailwindRawColors\": \"error\",\n  \"useTailwindShorthandClasses\": \"warn\",\n  \"noTailwindArbitraryValue\": \"warn\"\n} } } }\n```\n\n`noTailwindRawColors` enforces this skill's semantic-token rule: it \"disallows Tailwind palette colors such as `bg-pink-500` and `text-white`, encouraging design system color utilities such as `bg-primary`.\" `useTailwindShorthandClasses` suggests `size-4` for `w-4 h-4`.\n\n## GritQL custom rules\n\nDeclarative pattern-matching for project-specific rules. Register `.grit` files as plugins:\n\n```json\n{ \"plugins\": [\"./lint-rules/no-object-assign.grit\"] }\n```\n\n```grit\n`$fn($args)` where {\n  $fn <: `Object.assign`,\n  register_diagnostic(span = $fn, message = \"Prefer object spread over Object.assign()\")\n}\n```\n\nTarget languages: JavaScript (default), CSS, and JSON.\n\n## Suppression\n\n```ts\n// biome-ignore lint/suspicious/noConsole: needed for debugging\nconsole.log(\"debug\")\n// biome-ignore-all lint/suspicious/noConsole: logger module      (file-level)\n// biome-ignore-start lint/style/useConst: legacy ... biome-ignore-end  (range)\n```\n\nBiome requires explanation text after the colon.\n\n## Migration from ESLint/Prettier\n\n```bash\npnpm biome migrate eslint --write     # legacy + flat configs, plugin mapping, .eslintignore\npnpm biome migrate prettier --write   # maps tabWidth/useTabs/singleQuote/trailingComma\n```\n\nAfter removing ESLint (which respected `.gitignore`), enable VCS integration so Biome ignores the same files:\n\n```json\n{ \"vcs\": { \"enabled\": true, \"clientKind\": \"git\", \"useIgnoreFile\": true } }\n```\n\n## CLI reference\n\n```bash\nbiome check --write .            # primary command\nbiome check --changed .          # only VCS-changed files\nbiome check --staged .           # only staged (good for pre-commit)\nbiome lint --only=types .        # run just type-aware rules (nursery ones report at info - won't fail CI)\nbiome lint --enforce-assist .    # fail CI when assist actions remain unapplied\nbiome check --watch .            # re-run on file changes (2.5; read-only, no --write/--fix)\nbiome explain noFloatingPromises # explain a rule\nbiome migrate --write            # after a version bump\nbiome upgrade                    # 2.5: self-upgrade standalone (Homebrew/binary) installs\n```\n\n2.5.15 also restricts the daemon's Unix socket to the current user (`0600`, in a private `biome-daemon` cache dir) - another reason to stay on the latest patch. Biome 2.5 also adds `formatter.delimiterSpacing` (pads `[ 1, 2 ]` / `{ a }` / `( x )` - an accessibility aid for dyslexia; behavior varies per language) and can now format/lint `.svg` files.\n\n## Gotchas\n\n1. **Only `files.includes` exists** - not `ignore`/`include`/`exclude`.\n2. **`organizeImports` is under `assist.actions.source`**, not a top-level key.\n3. **`overrides` disabling linter+formatter still run assists** - the import organizer will still rewrite a \"skipped\" file, silently dirtying your tree. Use `files.includes` negation to fully exclude.\n4. **`package.json` reformatting loop:** Biome's default JSON formatter uses tabs; pnpm writes 2-space `package.json`, so each install/check fight. Either exclude it (`\"!**/package.json\"`) or override JSON to spaces:\n\n```json\n{ \"overrides\": [{ \"includes\": [\"**/package.json\"], \"json\": { \"formatter\": { \"indentStyle\": \"space\", \"indentWidth\": 2 } } }] }\n```\n\n## Resources\n\n- Docs: https://biomejs.dev/ - Config reference: https://biomejs.dev/reference/configuration/\n- Domains: https://biomejs.dev/linter/domains/ - Migrate: https://biomejs.dev/guides/migrate-eslint-prettier/\n\nFile v0.4.0:references/hono.md\n\n# Hono\n\nHono (Japanese for \"flame\") is a small, ultrafast web framework built entirely on Web\nStandards (`Request`/`Response`/`fetch`). One codebase runs on Cloudflare Workers, Deno,\nBun, Node.js, Vercel, Netlify, AWS Lambda, Lambda@Edge, and Fastly Compute. Zero\ndependencies; the `hono/tiny` preset is under 14kB. It is the backend/edge counterpart to\nthis stack's React frontend - its RPC client (`hc`) shares server types directly with\nReact, giving end-to-end type safety without code generation.\n\nVersion target: **hono@4.13.13** (Hono 4 is current; v5 is in development on a `v5` branch -\nESM-only, Node.js 22.12+ - with no npm release yet). On Node, use `@hono/node-server@2`, which\nrequires Node.js >= 20. Adapters/middleware are versioned independently (`@hono/node-server@2`,\n`@hono/zod-validator`, `@hono/zod-openapi@1`, and the runtime adapters `@hono/cloudflare-workers`,\n`@hono/bun`, `@hono/deno`).\n\n> **Keep Hono patched - it ships security fixes frequently.** Since 2026-08 alone: `serveStatic`\n> double-decoding the path, bypassing middleware on static routes (GHSA-5r4p-p66f-jhc7, fixed\n> 4.13.11; the same bug in `@hono/node-server` is GHSA-rmxm-3fg6-px4f, fixed **only** in 2.1.3 -\n> the 1.x line has no fix); `hono/jsx` boundary components rendering strings unescaped (XSS,\n> GHSA-hxh3-vqpv-xpqv, 4.13.7); unbounded `parseBody({ dot: true })` nesting (memory DoS,\n> GHSA-g6gw-c38x-mqfc, 4.13.5); query parsing past the URL fragment (GHSA-crvj-82cr-hjcx, 4.13.5);\n> `toSSG()` path escape (GHSA-gqvv-2mrq-wpjv, 4.13.5); `memo()` leaking SSR output across users\n> (GHSA-f23p-vx2j-j53r, 4.12.34); CORS ReDoS when `allowHeaders` is unset (GHSA-8j4g-w8fx-2239,\n> 4.12.34). Pin `hono >= 4.13.11` and `@hono/node-server >= 2.1.3`, and track the latest patch.\n\n> **For anything this file does not cover, fetch Hono's own LLM-optimized docs** - they are\n> the fastest authoritative source and are kept in sync with releases:\n> - Full docs (one file, ~360KB): https://hono.dev/llms-full.txt\n> - Core-only (smaller): https://hono.dev/llms-small.txt\n> - Index of all doc pages: https://hono.dev/llms.txt\n>\n> This reference is the curated 80% you need most often; the `llms-*.txt` files are the\n> exhaustive long tail (every middleware option, every runtime's getting-started, edge cases).\n\n```sh\nnpm create hono@latest my-app          # scaffold (prompts for a template)\nnpm create hono@latest my-app -- --template cloudflare-workers --pm pnpm --install\nnpm create hono@latest my-app -- --template cloudflare-workers+vite   # full-stack Workers + Vite (recommended)\nnpm i hono                              # add to an existing project\n```\n\n## Mental model\n\n- A handler returns a `Response` (or `c.text()`/`c.json()`/etc., which build one). Exactly\n  one handler runs per request.\n- Middleware is `async (c, next) => { ... await next() ... }`. Code before `next()` runs on\n  the way in; code after runs on the way out (onion model). Return a `Response` from\n  middleware to short-circuit. Returning nothing (after `await next()`) continues the chain.\n- Execution order = registration order. Register middleware (`app.use`) and fallbacks\n  (`app.get('*', ...)`) relative to routes accordingly.\n- Hono catches throws from handlers/middleware and routes them to `app.onError` (or a 500),\n  so `next()` never throws - no try/catch needed around it.\n\n```ts\nimport { Hono } from 'hono'\n\nconst app = new Hono()\napp.get('/', (c) => c.text('Hono!'))\n\nexport default app // entry point for Cloudflare Workers, Bun, Deno\n```\n\n## Routing\n\n```ts\napp.get('/', (c) => c.text('GET /'))\napp.post('/', (c) => c.text('POST /'))\napp.put('/', (c) => c.text('PUT /'))\napp.delete('/', (c) => c.text('DELETE /'))\napp.all('/hello', (c) => c.text('Any method'))          // any HTTP method\napp.query('/search', (c) => c.json({ hits: [] }))       // HTTP QUERY (4.13): safe + idempotent, carries a body\napp.on('PURGE', '/cache', (c) => c.text('PURGE'))       // custom method\napp.on(['PUT', 'DELETE'], '/post', (c) => c.text('..')) // multiple methods\napp.on('GET', ['/a', '/b'], (c) => c.text('..'))        // multiple paths\n\napp.get('/wild/*/card', (c) => c.text('wildcard'))      // wildcard\napp.get('/user/:name', (c) => c.text(c.req.param('name')))           // param\napp.get('/api/animal/:type?', (c) => c.text('..'))                   // optional param\napp.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => c.text('..'))   // regexp param\napp.get('/posts/:filename{.+\\\\.png}', (c) => c.text('..'))           // slashes via regexp\n\n// Chained routes on one path\napp\n  .get('/endpoint', (c) => c.text('GET'))\n  .post((c) => c.text('POST'))\n  .delete((c) => c.text('DELETE'))\n```\n\n**Priority is registration order**, and the first matching handler wins and stops dispatch.\nPut middleware and specific routes *above* wildcard fallbacks:\n\n```ts\napp.get('/book/a', (c) => c.text('a'))        // GET /book/a -> 'a'\napp.get('/book/:slug', (c) => c.text('common')) // GET /book/b -> 'common'\n\napp.use(logger())                              // middleware first\napp.get('/foo', (c) => c.text('foo'))\napp.get('*', (c) => c.text('fallback'))        // fallback last\n```\n\n**HEAD is automatic.** Hono converts HEAD to GET and strips the body before route matching,\nso `app.head(...)` / `app.on('HEAD', ...)` handlers are never called. Add HEAD-specific\nheaders in middleware checking `c.req.method === 'HEAD'`.\n\n### Grouping and sub-apps\n\n`app.route(path, subApp)` mounts a sub-`Hono`. This is how you split a large app into files\n*without* losing type inference (see RPC). `basePath()` prefixes all routes on an instance.\n\n```ts\n// books.ts\nconst books = new Hono()\nbooks.get('/', (c) => c.text('List'))     // GET /books\nbooks.get('/:id', (c) => c.text('One'))   // GET /books/:id\nexport default books\n\n// index.ts\nconst app = new Hono()\napp.route('/books', books)\nconst api = new Hono().basePath('/api')   // all routes under /api\n```\n\nTo mount another framework's fetch handler, use the Mount middleware -\n`app.all('/itty/*', mount(ittyRouter.handle))` from `hono/mount`; `app.mount()` is deprecated\n(removed in v5).\n\nWatch grouping order: `app.route('/two', two)` snapshots `two`'s routes *at call time*, so\nregister child routes onto `two` before mounting `two` onto `app`, or you get 404s.\n\n## Context (`c`)\n\nThe `Context` is created per request and lives until the response is returned.\n\n**Responders** (each returns a `Response`):\n\n```ts\nc.text('Hello', 201, { 'X-Msg': 'hi' })   // text/plain\nc.json({ ok: true }, 200)                  // application/json\nc.html('<h1>Hi</h1>')                      // text/html\nc.body('raw', 201, { 'Content-Type': 'text/plain' })\nc.redirect('/', 301)                       // default 302\nc.notFound()                               // customizable via app.notFound()\nc.status(201)                              // set status without returning yet\nc.header('X-Message', 'hi')                // set a response header\nc.res                                      // the in-progress Response (read/mutate in mw)\n```\n\n**Per-request state** - `c.set` / `c.get` / `c.var`. Type it via the `Variables` generic so\nhandlers see the right types:\n\n```ts\ntype Variables = { user: { id: string } }\nconst app = new Hono<{ Variables: Variables }>()\n\napp.use(async (c, next) => {\n  c.set('user', { id: '123' })\n  await next()\n})\napp.get('/', (c) => c.json(c.get('user')))   // or c.var.user\n```\n\nState lives only for the current request; it is never shared across requests.\n\n**Bindings / env** - on Cloudflare Workers, KV/D1/R2/secrets are `c.env.*`. Type them with\nthe `Bindings` generic:\n\n```ts\ntype Bindings = { MY_KV: KVNamespace; TOKEN: string }\nconst app = new Hono<{ Bindings: Bindings }>()\n\napp.get('/', async (c) => {\n  const v = await c.env.MY_KV.get('key')\n  c.executionCtx.waitUntil(c.env.MY_KV.put('k', 'v')) // background work\n  return c.text(v ?? '')\n})\n```\n\n`c.render()` / `c.setRenderer()` set a layout in middleware then render content per route.\n`c.error` holds a thrown error inside post-`next()` middleware.\n\n## HonoRequest (`c.req`)\n\n```ts\nc.req.param('id')          // single path param (literal-typed from the route; string | undefined on a bare Context)\nc.req.param()              // all path params\nc.req.query('q')           // single query value\nc.req.query()              // all query values\nc.req.queries('tags')      // repeated query -> string[]\nc.req.header('User-Agent') // single header (pass the exact name)\nc.req.header()             // all headers, keys LOWERCASED\n\nawait c.req.json()         // parse application/json body\nawait c.req.text()         // text/plain body\nawait c.req.parseBody()    // multipart/form-data or x-www-form-urlencoded\nawait c.req.formData()     // FormData\nawait c.req.arrayBuffer()  // ArrayBuffer\nawait c.req.blob()         // Blob\n\nc.req.valid('json')        // validated data (see Validation)\nc.req.path                 // pathname\nc.req.url                  // full URL string\nc.req.method               // 'GET'\nc.req.raw                  // the underlying Web `Request` (e.g. c.req.raw.cf on Workers)\nawait cloneRawRequest(c.req) // from 'hono/request': clone even after a validator consumed the body\n```\n\n`parseBody()` notes: `body['foo[]']` is always `(string | File)[]`; `{ all: true }` collects\nrepeated same-name fields into arrays; `{ dot: true }` expands `obj.key` keys into nested\nobjects (nesting depth is bounded since 4.13.5 - older versions could be memory-DoSed).\n\n## Middleware\n\n```ts\napp.use(logger())              // all methods, all routes\napp.use('/posts/*', cors())    // scoped by path\napp.post('/posts/*', basicAuth({ username, password })) // method + path\n\n// Inline custom middleware\napp.use('/message/*', async (c, next) => {\n  await next()\n  c.header('x-message', 'after handler')\n})\n```\n\nFor reusable, type-safe middleware use `createMiddleware` from `hono/factory` - it preserves\n`Context`/`next` types and lets you declare the `Variables` it sets:\n\n```ts\nimport { createMiddleware } from 'hono/factory'\n\nconst auth = createMiddleware<{ Variables: { user: { id: string } } }>(\n  async (c, next) => {\n    c.set('user', { id: '123' })\n    await next()\n  }\n)\n```\n\n**Type inference accumulates across chained `.use()`.** Each `.use()` returns a new instance\nwith merged `Variables`, so later handlers see every preceding middleware's variables without\ndeclaring a combined `Env` upfront:\n\n```ts\nconst app = new Hono()\n  .use(authMiddleware)   // sets `user`\n  .use(dbMiddleware)     // sets `db`\n  .get('/', (c) => c.json({ user: c.var.user, hasDb: !!c.var.db }))\n```\n\nTo configure middleware from `c.env` (Workers can't read env at module scope), wrap it:\n\n```ts\napp.use('*', async (c, next) => cors({ origin: c.env.CORS_ORIGIN })(c, next))\n```\n\n`ContextVariableMap` module augmentation adds variable types *globally* - convenient for\napp-wide middleware, but it makes `c.get(...)` look typed even in handlers where the\nmiddleware never ran, hiding `undefined` bugs. Prefer the `Variables` generic or chained\n`.use()` typing.\n\n### Built-in middleware (import from `hono/<name>`)\n\nAuth & security: `basic-auth`, `bearer-auth`, `jwt`, `jwk`, `cors`, `csrf`, `secure-headers`,\n`ip-restriction`. Body/response: `body-limit`, `compress`, `etag`, `cache`, `pretty-json`,\n`trailing-slash`. Observability: `logger`, `timing`, `request-id`. Control flow: `combine`,\n`method-override`, `method-not-allowed` (4.13: 405 with a correct `Allow` header when the path\nmatches but the method doesn't), `mount`, `context-storage`, `timeout`, `language`. Plus the\n`powered-by` and `jsx-renderer` middleware.\n\n4.13 behavior changes to know when upgrading: the `cache` middleware's internal key format\nchanged (entries live under `/.hono/cache?__hono_cache_key=...`, so `caches.delete(url)` by plain\nURL no longer hits them); CORS default `allowMethods` now includes `QUERY`; and `RegExpRouter`\nrejects unsupported path combinations at registration, so a bad pattern fails at boot.\n\n```ts\nimport { cors } from 'hono/cors'\napp.use('/api/*', cors({\n  origin: ['https://example.com'],           // string | string[] | (origin, c) => string\n  allowMethods: ['GET', 'POST', 'OPTIONS'],\n  allowHeaders: ['X-Custom-Header'],\n  exposeHeaders: ['Content-Length'],\n  credentials: true,\n  maxAge: 600,\n}))\n// `origin`/`allowMethods` accept callbacks for per-origin logic. CORS must run before routes.\n// Behind the Vite dev server, set `server.cors: false` in vite.config.ts so Vite's CORS\n// doesn't conflict with Hono's.\n\nimport { jwt } from 'hono/jwt'\nimport type { JwtVariables } from 'hono/jwt'\nconst app = new Hono<{ Variables: JwtVariables }>()\napp.use('/auth/*', jwt({ secret: 'very-secret', alg: 'HS256', issuer: 'me' }))\napp.get('/auth/page', (c) => c.json(c.get('jwtPayload')))\n// Reads `Authorization: Bearer <token>` by default; set `cookie` or `headerName` to change.\n// `alg`: HS256/384/512, RS*, PS*, ES*, EdDSA. For c.env secrets, wrap like cors above.\n\nimport { secureHeaders } from 'hono/secure-headers'\nimport { csrf } from 'hono/csrf'\nimport { logger } from 'hono/logger'\napp.use(secureHeaders(), csrf({ origin: 'https://example.com' }), logger())\n\nimport { basicAuth } from 'hono/basic-auth'\nimport { bearerAuth } from 'hono/bearer-auth'\napp.use('/admin/*', basicAuth({ username: 'hono', password: 'secret' }))\napp.use('/api/*', bearerAuth({ token: 'a-static-token' }))  // or { verifyToken: async (t, c) => boolean }\n```\n\n## Validation\n\nHono ships a thin `validator`; pair it with a schema library for real validation. The\nvalidated value is read with `c.req.valid(target)`. Targets: `json`, `form`, `query`,\n`header`, `param`, `cookie`.\n\n```ts\nimport { validator } from 'hono/validator'\napp.post('/posts', validator('form', (value, c) => {\n  if (typeof value.body !== 'string') return c.text('Invalid!', 400)\n  return { body: value.body }            // return = the validated value\n}), (c) => c.json({ body: c.req.valid('form').body }))\n```\n\nPrefer the **Zod validator middleware** (Zod 4 supported):\n\n```ts\nimport { z } from 'zod'\nimport { zValidator } from '@hono/zod-validator'\n\nconst app = new Hono().post(\n  '/posts',\n  zValidator('form', z.object({ title: z.string(), body: z.string() })),\n  (c) => {\n    const { title, body } = c.req.valid('form') // fully typed\n    return c.json({ ok: true }, 201)\n  }\n)\n```\n\nOr `@hono/standard-validator`'s `sValidator` for any [Standard Schema](https://standardschema.dev)\nlibrary (Zod, Valibot, ArkType) with one adapter (0.4+ exports `flattenErrors` to group issues\ninto form and field errors). `zValidator`'s default failure is typed: it surfaces in\n`hc<typeof app>` as a `400` branch. Run multiple validators to check different\nparts: `validator('param', ...)`, `validator('query', ...)`, `validator('json', ...)`.\n\nGotchas: validating `json`/`form` requires the matching `Content-Type` on the request or the\nbody parses to `{}` (set it in tests too). For `header`, use **lowercase** keys\n(`value['idempotency-key']`).\n\n## RPC - end-to-end type safety\n\nThe flagship feature: export the server app's type, and the `hc` client infers every input\nand output - no codegen, no schema duplication. This is the natural way to connect a Hono\nbackend to the React frontend in this stack.\n\n```ts\n// server.ts\nimport { Hono } from 'hono'\nimport { z } from 'zod'\nimport { zValidator } from '@hono/zod-validator'\n\nconst route = new Hono()\n  .post('/posts',\n    zValidator('form', z.object({ title: z.string(), body: z.string() })),\n    (c) => c.json({ ok: true, message: 'Created!' }, 201)\n  )\n  .get('/posts/:id',\n    zValidator('query', z.object({ page: z.coerce.number().optional() })),\n    (c) => c.json({ title: 'Night', body: 'sleep' }, 200)\n  )\n\nexport type AppType = typeof route   // share the type with the client\nexport default route\n```\n\n```ts\n// client.ts (runs in the React app)\nimport { hc } from 'hono/client'\nimport type { AppType } from './server'\n\nconst client = hc<AppType>('http://localhost:8787/')\n\nconst res = await client.posts.$post({ form: { title: 'Hi', body: '...' } })\nif (res.ok) console.log((await res.json()).message)  // typed\n\n// Path params via [':id']; params/query MUST be strings even if validated to numbers\nconst res2 = await client.posts[':id'].$get({ param: { id: '123' }, query: { page: '1' } })\n```\n\nStatus codes flow through types: `c.json(data, 404)` makes `res.status === 404` narrow the\nJSON type. Helpers: `InferRequestType<typeof client.x.$post>`, `InferResponseType<...>`,\n`client.x.$url()` (needs absolute base URL), `client.x.$path()`, `parseResponse(...)` (parses\nby Content-Type and throws on non-ok). `app.query()` routes get a typed `$query`. Customize query\nserialization (e.g. bracket arrays) with `hc<AppType>(url, { buildSearchParams })`. Pass `{ init: { credentials: 'include' } }` or\n`{ headers: { Authorization: '...' } }` to `hc` for cookies/auth.\n\n**Do not** use `c.notFound()` on routes the client calls - its result can't be inferred. Use\n`c.json({ error: '...' }, 404)` instead. Global `onError` responses aren't auto-inferred;\nmerge them with `ApplyGlobalResponse<typeof app, { 500: { json: { error: string } } }>`.\n\nLarger apps: chain `.route()` and export the chained result's type:\n\n```ts\nconst routes = app.route('/authors', authors).route('/books', books)\nexport type AppType = typeof routes\n```\n\n**Two RPC requirements that bite:**\n1. `\"strict\": true` in `tsconfig.json` on *both* client and server (a monorepo split needs\n   matching Hono versions, or you get \"Type instantiation is excessively deep\").\n2. Type instantiation is heavy - many routes slow the IDE. The recommended fix is to compile\n   the client type once so `tsserver` doesn't recompute it:\n\n```ts\nimport { hc } from 'hono/client'\nimport { app } from './app'\nexport type Client = ReturnType<typeof hc<typeof app>>\nexport const hcWithType = (...args: Parameters<typeof hc>): Client =>\n  hc<typeof app>(...args)   // use hcWithType instead of hc\n```\n\n## OpenAPI\n\n`@hono/zod-openapi` extends Hono so the same Zod schema validates requests *and* generates an\nOpenAPI 3 document. Serve interactive docs with Swagger UI (`@hono/swagger-ui`) or Scalar.\n\n```ts\nimport { OpenAPIHono, createRoute, z } from '@hono/zod-openapi'\n\nconst UserSchema = z.object({\n  id: z.string().openapi({ example: '123' }),\n  name: z.string().openapi({ example: 'John' }),\n}).openapi('User')   // registers as #/components/schemas/User\n\nconst route = createRoute({\n  method: 'get',\n  path: '/users/{id}',\n  request: { params: z.object({ id: z.string().min(3) }) },\n  responses: {\n    200: { content: { 'application/json': { schema: UserSchema } }, description: 'A user' },\n  },\n})\n\nconst app = new OpenAPIHono()\napp.openapi(route, (c) => {\n  const { id } = c.req.valid('param')\n  return c.json({ id, name: 'Ultra-man' }, 200)  // specify the status code, even 200\n})\napp.doc('/doc', { openapi: '3.0.0', info: { version: '1.0.0', title: 'My API' } })\n```\n\n`OpenAPIHono` is a drop-in `Hono` (supports `.route()`, RPC `typeof`, etc.). Unlike plain\n`zValidator`, since 1.6.3 a body whose `Content-Type` matches none of the route's declared media\ntypes is rejected with **415** (it used to validate as `{}`); a request with no body still reaches\nthe handler with `{}` unless the body is marked `required: true` (then 400). Since 1.5.0 a mounted\nsub-app without its own `defaultHook` inherits the parent's. For many routes, `defineOpenAPIRoute`\n+ `openapiRoutes` register them in one typed batch.\n\n## Error handling\n\n```ts\nimport { HTTPException } from 'hono/http-exception'\n\nthrow new HTTPException(401, { message: 'Unauthorized' })          // text response\nthrow new HTTPException(401, { res: customResponse })              // full Response control\nthrow new HTTPException(401, { message, cause })                   // attach arbitrary cause\n\napp.onError((err, c) => {\n  if (err instanceof HTTPException) return err.getResponse()\n  console.error(err)\n  return c.text('Internal Server Error', 500)\n})\napp.notFound((c) => c.text('Custom 404', 404))\n```\n\n`notFound`/`onError` fire only on the top-level app; route-level `onError` takes priority over\na parent's. `HTTPException.getResponse()` is not `Context`-aware - reapply context headers if\nneeded.\n\n## Helpers (import from `hono/<name>`)\n\n```ts\n// hono/cookie\nimport { getCookie, setCookie, deleteCookie, getSignedCookie, setSignedCookie } from 'hono/cookie'\nsetCookie(c, 'name', 'value', { httpOnly: true, secure: true, sameSite: 'Lax', maxAge: 3600 })\nconst v = getCookie(c, 'name')\nawait setSignedCookie(c, 'name', 'value', secret)   // signed cookies are async (WebCrypto)\n\n// hono/streaming - streaming & Server-Sent Events\nimport { stream, streamText, streamSSE } from 'hono/streaming'\napp.get('/sse', (c) => streamSSE(c, async (stream) => {\n  while (!stream.aborted) {\n    await stream.writeSSE({ data: new Date().toISOString(), event: 'tick', id: String(id++) })\n    await stream.sleep(1000)\n  }\n}))\n// Note: errors thrown inside the stream callback do NOT trigger app.onError (response started).\n\n// hono/jwt - mint/verify tokens yourself (the jwt() middleware only verifies incoming ones)\nimport { sign, verify, decode } from 'hono/jwt'\nconst token = await sign({ sub: 'user123', exp: Math.floor(Date.now() / 1000) + 300 }, secret)\nconst payload = await verify(token, secret, 'HS256')  // throws JwtTokenExpired/-Invalid/... on failure\nconst { header, payload: p } = decode(token)          // inspect WITHOUT verifying (debug only)\n// verify() auto-checks exp/nbf/iat/iss when those claims are present. alg default HS256;\n// supports HS*/RS*/PS*/ES*/EdDSA. Catch the typed errors (JwtTokenExpired, etc.) to branch.\n\n// hono/context-storage - reach the Context from outside a handler (DB layer, logger, util)\nimport { contextStorage, getContext } from 'hono/context-storage'\napp.use(contextStorage())                       // requires AsyncLocalStorage support\nconst currentUser = () => getContext<Env>().var.user  // works anywhere downstream\n// Cloudflare Workers: needs the `nodejs_compat` (or `nodejs_als`) compatibility flag.\n// `tryGetContext()` returns undefined instead of throwing when no context is active.\n```\n\nOther helpers: `hono/factory` (`createFactory`, `createMiddleware`, `createHandlers`,\n`createApp`), `hono/jwt` (sign/verify/decode utilities), `hono/adapter` (`env(c)` for\nruntime-agnostic env access), `hono/dev` (`showRoutes(app)` prints the route table,\n`inspectRoutes`, `getRouterName`), `hono/html`, `hono/css`, `hono/ssg`, `hono/proxy`,\n`hono/conninfo`, `hono/accepts`, `hono/route`, `hono/testing` (`testClient`).\n\nThe Factory helper keeps `Env` types DRY and enables RoR-style \"controllers\" without losing\ninference (the one sanctioned way - see Best practices):\n\n```ts\nimport { createFactory } from 'hono/factory'\n\ntype Env = { Bindings: { MY_DB: D1Database }; Variables: { db: DrizzleD1Database } }\nconst factory = createFactory<Env>({\n  initApp: (app) => app.use(async (c, next) => { c.set('db', drizzle(c.env.MY_DB)); await next() }),\n})\nconst app = factory.createApp()          // Env applied once\nconst handlers = factory.createHandlers(logger(), (c) => c.json(c.var.db.select()))\napp.get('/posts', ...handlers)\n```\n\n## JSX (server-side rendering)\n\n`hono/jsx` renders HTML on the server (and works on the client). Configure\n`tsconfig.json`: `\"jsx\": \"react-jsx\"`, `\"jsxImportSource\": \"hono/jsx\"`, and use a `.tsx`\nfile. Components are plain functions typed with `FC`. This is separate from the React\nfrontend - use it for server-rendered HTML responses, not as a React replacement. Since 4.13 its\n`useRef`/`RefObject` types match React 19: `RefObject<T>` is `{ current: T }`, so type nullable\nrefs as `RefObject<T | null>` and call `useRef(undefined)` rather than `useRef()`.\n\n```tsx\nimport type { FC } from 'hono/jsx'\nconst Layout: FC = (props) => <html><body>{props.children}</body></html>\napp.get('/', (c) => c.html(<Layout><h1>Hello</h1></Layout>))\n```\n\n## Runtimes & deployment\n\nSame app, different entry point. Pick the matching `create-hono` template.\n\n**Cloudflare Workers** (`export default app`) - develop/deploy with Wrangler (`npm run dev`\nserves on :8787, `npm run deploy`). Bindings (KV/D1/R2/secrets) come in via `c.env`; type\nthem with the `Bindings` generic and generate types with `wrangler types`. Serve static files\nwith Workers Static Assets (`assets.directory` in `wrangler.jsonc`), not `serveStatic`.\n`hono/cloudflare-pages` is deprecated with no replacement - Cloudflare recommends Workers + Static\nAssets.\n\n**Runtime adapters moved to their own packages (4.13.10).** `hono/cloudflare-workers`,\n`hono/bun`, `hono/deno`, etc. \"still work in v4 but [are] deprecated and will be removed in v5\";\ninstall `@hono/cloudflare-workers`, `@hono/bun`, or `@hono/deno` (also on JSR) and change the\nimport - the API is identical.\n\n**Node.js** - needs the adapter `@hono/node-server`:\n\n```ts\nimport { serve } from '@hono/node-server'\nimport { serveStatic } from '@hono/node-server/serve-static'\nconst app = new Hono()\napp.use('/static/*', serveStatic({ root: './' }))\nserve({ fetch: app.fetch, port: 3000 }, (info) => console.log(info.port))\n// 2.1+: Early Hints via `earlyHints` from '@hono/node-server/early-hints'; WebSockets are built\n// in (`@hono/node-ws` is deprecated)\n// graceful shutdown:\nconst server = serve(app)\nprocess.on('SIGINT', () => { server.close(); process.exit(0) })\n```\n\n**Bun** - `export default { port: 3000, fetch: app.fetch }`. **Deno** -\n`Deno.serve(app.fetch)`; import from `jsr:@hono/hono` (adapter: `jsr:@hono/deno`) and keep all\nhono imports on one version. **Vercel / Netlify / AWS Lambda / Lambda@Edge / Fastly / Supabase Edge Functions /\nNext.js** each have a template and a thin adapter; the handler logic is identical.\n\n**Static files** - `serveStatic` is runtime-specific: `@hono/node-server/serve-static`,\n`@hono/bun`, or `@hono/deno` (on Workers use Static Assets instead). Since 4.13.11 / node-server\n2.1.3 paths that still contain `%` after decoding are rejected (opt out with\n`allowPercentInPath: true`). All take `{ root, path,\nrewriteRequestPath, onFound }`; mount it on a wildcard route (`app.use('/static/*', serveStatic({ root: './' }))`).\n\n## Realtime (WebSocket)\n\n`upgradeWebSocket()` adds server-side WebSockets, imported from the runtime adapter package\n(`@hono/cloudflare-workers`, `@hono/deno`, `@hono/bun`, or `@hono/node-server`). It returns a\nhandler that supplies `onOpen`/`onMessage`/`onClose`/`onError` callbacks. WS routes also work\nwith RPC: the client gets a typed `client.ws.$ws()`.\n\n```ts\n// Cloudflare Workers / Deno\nimport { upgradeWebSocket } from '@hono/cloudflare-workers'\nconst wsApp = app.get('/ws', upgradeWebSocket((c) => ({\n  onMessage(event, ws) { ws.send(`echo: ${event.data}`) },\n  onClose() { console.log('closed') },\n})))\nexport type WsApp = typeof wsApp   // hc<WsApp>(...).ws.$ws() on the client\n\n// Bun: export `{ fetch: app.fetch, websocket }` (import websocket from '@hono/bun')\n// Node: install `ws`; pass a WebSocketServer to serve({ fetch, websocket: { server: wss } })\n```\n\nGotcha: `onOpen` is not supported on Cloudflare Workers, and header-modifying middleware\n(e.g. CORS) on a WS route throws \"immutable headers\" because `upgradeWebSocket` sets headers\ninternally - keep such middleware off WS routes.\n\n## Testing\n\n`app.request()` runs the app in-process against a Web `Request` - no server needed, works on\nevery runtime. Pass mock bindings as the 3rd arg.\n\n```ts\nimport { describe, it, expect } from 'vitest'\n\ndescribe('api', () => {\n  it('GET /posts', async () => {\n    const res = await app.request('/posts')\n    expect(res.status).toBe(200)\n  })\n\n  it('POST /posts (json)', async () => {\n    const res = await app.request('/posts', {\n      method: 'POST',\n      body: JSON.stringify({ message: 'hi' }),\n      headers: { 'Content-Type': 'application/json' }, // required for json validators\n    })\n    expect(res.status).toBe(201)\n  })\n\n  it('uses mock env', async () => {\n    const res = await app.request('/posts', {}, { DB: mockD1, API_HOST: 'example.com' })\n  })\n})\n```\n\nFor a typed test client mirroring the RPC client, use `testClient(app)` from `hono/testing`.\nOn Cloudflare Workers, Cloudflare recommends `@cloudflare/vitest-pool-workers`. (This skill's\n[vitest.md](vitest.md) covers the runner itself.)\n\n## Best practices\n\n1. **Write handlers inline after the path** - `app.get('/books/:id', (c) => ...)`. A separate\n   `const handler = (c: Context) => ...` loses path-param inference - on a bare `Context`,\n   `c.req.param('id')` is `string | undefined`. If you must extract (e.g. a shared route\n   table), use `factory.createHandlers()` or narrow the param explicitly.\n2. **Chain routes** (`.get(...).post(...)`) and export `typeof app` - that's what makes RPC\n   types work. Split large apps with `.route()`, chaining the mounts.\n3. **Let the compiler accumulate types** via chained `.use()` instead of hand-writing a\n   combined `Env`. Reserve `ContextVariableMap` for truly app-wide middleware.\n4. **Always specify the status code** in `c.json(data, status)` on routes the client or\n   OpenAPI consumes - the status is part of the inferred/documented type.\n5. **Avoid `c.notFound()`** on RPC routes; return `c.json({ error }, 404)`.\n6. **Order matters**: middleware and specific routes before wildcards; CORS before routes.\n7. **Set `Content-Type`** on `json`/`form` requests (including tests) or the body is `{}`.\n8. **Keep Hono one version** across client/server (`pnpm why hono` - an adapter's peer range can\n   pull in a second copy), and compile the RPC client type (`hcWithType`) once the route count\n   grows, to keep the IDE fast.\n9. **Pick the right preset/router**: `hono` (default, `SmartRouter` = fast + full features),\n   `hono/quick` (fast registration, good for per-request init like some edges), `hono/tiny`\n   (smallest). Override with `new Hono({ router: new RegExpRouter() })` only if needed.\n\n## Resources\n\n**LLM-optimized docs (fetch these first when this file falls short):**\n- Full docs, one file: https://hono.dev/llms-full.txt\n- Core-only / smaller: https://hono.dev/llms-small.txt\n- Doc-page index: https://hono.dev/llms.txt\n\n**Official docs:**\n- Home / getting started: https://hono.dev/docs/\n- API reference - App: https://hono.dev/docs/api/hono - Context:\n  https://hono.dev/docs/api/context - Request: https://hono.dev/docs/api/request - Routing:\n  https://hono.dev/docs/api/routing\n- Guides - RPC: https://hono.dev/docs/guides/rpc - Validation:\n  https://hono.dev/docs/guides/validation - Middleware:\n  https://hono.dev/docs/guides/middleware - Testing: https://hono.dev/docs/guides/testing -\n  Best practices: https://hono.dev/docs/guides/best-practices - JSX:\n  https://hono.dev/docs/guides/jsx\n- Built-in middleware index: https://hono.dev/docs/middleware/builtin/basic-auth\n- Helpers (cookie, jwt, streaming, factory, websocket, ...): https://hono.dev/docs/helpers/cookie\n- Third-party middleware catalog: https://hono.dev/docs/middleware/third-party\n\n**Repos & packages:**\n- Core: https://github.com/honojs/hono\n- Official middleware monorepo (47 packages incl. zod-validator, zod-openapi, swagger-ui,\n  clerk-auth, oauth-providers, otel, mcp, trpc-server): https://github.com/honojs/middleware\n- Examples (basic, blog, durable-objects, nextjs-stack, pages-stack, jsx-ssr):\n  https://github.com/honojs/examples\n- Node.js adapter: https://github.com/honojs/node-server - Scaffolder:\n  https://github.com/honojs/create-hono\n\n**Key ecosystem packages:** `@hono/zod-validator`, `@hono/standard-validator`,\n`@hono/zod-openapi` + `@hono/swagger-ui` (OpenAPI), `@hono/node-server` (Node), Zod\n(https://zod.dev), Valibot (https://valibot.dev), ArkType (https://arktype.io), Standard\nSchema (https://standardschema.dev).\n\nFile v0.4.0:references/react.md\n\n# React 19\n\nPatterns for type-safe React 19.3 components (latest `19.3.0`). The headline shift from older React: **the React Compiler handles memoization**, `ref` is a normal prop, and `use()` reads context and promises without the old hook-placement rules. Write plain components and let the tooling optimize. For TypeScript specifics (props typing, generics, tsconfig) see [typescript.md](typescript.md).\n\n## Critical rules\n\n### `ref` is a prop - no `forwardRef`\n\n```tsx\n// React 19: ref is a regular prop\nfunction Input({ ref, ...props }: React.ComponentProps<\"input\"> & { ref?: React.Ref<HTMLInputElement> }) {\n  return <input ref={ref} {...props} />\n}\n```\n\n`React.ComponentProps<\"input\">` already includes `ref` in React 19's types, so for plain DOM-wrapping components you usually just destructure `ref` from props without declaring it.\n\n### No manual memoization\n\nThe compiler auto-memoizes return values, expensive computations, and callbacks. Drop `memo`, `useMemo`, `useCallback` from the common path:\n\n```tsx\n// Plain code - compiler memoizes sorting, the callback, and the JSX\nfunction List({ items, onSelect }: { items: Item[]; onSelect: (id: string) => void }) {\n  const sorted = items.toSorted(compare)\n  return sorted.map((item) => <Row key={item.id} onClick={() => onSelect(item.id)} />)\n}\n```\n\n### Extend native element props\n\n```tsx\ntype ButtonProps = React.ComponentProps<\"button\"> & { variant?: \"primary\" | \"ghost\" }\n```\n\n### `use()` over `useContext()`\n\n`use()` can read context after early returns and inside conditionals - `useContext` cannot. Pair it with a factory hook that throws on a missing provider so consumers never null-check:\n\n```tsx\nconst AuthContext = createContext<AuthState | null>(null)\n\nfunction useAuth(): AuthState {\n  const ctx = use(AuthContext)\n  if (ctx === null) throw new Error(\"useAuth must be used within AuthProvider\")\n  return ctx\n}\n```\n\n## React 19 patterns\n\n### Component authoring\n\nPlain functions with `data-slot` for styling hooks (the shadcn convention). No `forwardRef`, no `FC`:\n\n```tsx\nfunction Card({ className, ...props }: React.ComponentProps<\"div\">) {\n  return <div data-slot=\"card\" className={cn(\"rounded-xl border bg-card\", className)} {...props} />\n}\n```\n\n### Actions\n\nAsync transitions handle pending state, errors, and form resets. `useActionState` for forms:\n\n```tsx\nfunction UpdateProfile({ userId }: { userId: string }) {\n  const [error, submitAction, isPending] = useActionState(\n    async (_prev: string | null, formData: FormData) => {\n      const result = await updateProfile(userId, formData)\n      return result.error ?? null\n    },\n    null\n  )\n  return (\n    <form action={submitAction}>\n      <input name=\"displayName\" required />\n      <button type=\"submit\" disabled={isPending}>{isPending ? \"Saving...\" : \"Save\"}</button>\n      {error && <p className=\"text-destructive\">{error}</p>}\n    </form>\n  )\n}\n```\n\n`useTransition` for non-form Actions; `useOptimistic` for instant feedback:\n\n```tsx\nconst [isPending, startTransition] = useTransition()\n// onClick={() => startTransition(async () => { await onDelete() })}\n\nconst [optimisticLikes, addOptimisticLike] = useOptimistic(likes, (prev) => prev + 1)\n```\n\n### `use()` hook\n\nReads promises (suspends until resolved) and context, conditionally. The promise must come from a loader/cache, **not** be created during render:\n\n```tsx\nfunction Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {\n  const comments = use(commentsPromise) // parent wraps this in <Suspense>\n  return <ul>{comments.map((c) => <li key={c.id}>{c.text}</li>)}</ul>\n}\n```\n\n### Activity (19.2, stable)\n\nPreserve the state of hidden UI. Hidden children keep state and DOM but unmount effects. The API is `mode=\"visible\" | \"hidden\"`:\n\n```tsx\n{tabs.map((tab) => (\n  <Activity key={tab.id} mode={activeTab === tab.id ? \"visible\" : \"hidden\"}>\n    <tab.component />\n  </Activity>\n))}\n```\n\n`hidden` hides via `display: none`, cleans up effects, preserves state, and pre-renders children at low priority for faster reveals. DOM side effects (video/audio) persist when hidden - add `useLayoutEffect` cleanup if needed.\n\n### useEffectEvent (19.2, stable)\n\nExtract non-reactive logic from effects. The event function always sees the latest props/state without being an effect dependency:\n\n```tsx\nfunction ChatRoom({ roomId, theme }: { roomId: string; theme: string }) {\n  const onConnected = useEffectEvent(() => showNotification(\"Connected!\", theme))\n  useEffect(() => {\n    const conn = createConnection(roomId)\n    conn.on(\"connected\", () => onConnected())\n    conn.connect()\n    return () => conn.disconnect()\n  }, [roomId]) // theme is NOT a dep - it's read via the effect event\n}\n```\n\nRules: only call from inside effects/other effect events, never pass to children or list in dependency arrays, never call during render. (`useExhaustiveDependencies` in Biome and the react-hooks ESLint rule both understand it - upgrade to the latest plugin version.)\n\n### `<ViewTransition>` (19.3, stable)\n\nAnimate UI changes with the browser View Transitions API. Wrap what should animate; only updates inside a Transition (`startTransition`, `useDeferredValue`, Suspense reveals) trigger it - \"updates not marked as Transitions don't trigger animations.\" DOM only.\n\n```tsx\nimport { ViewTransition, addTransitionType, startTransition } from \"react\"\n\n<ViewTransition update=\"auto\" default=\"none\">\n  <Suspense fallback={<Skeleton />}><Feed /></Suspense>\n</ViewTransition>\n\nstartTransition(() => {\n  addTransitionType(\"navigate-forward\")   // tag the transition so CSS can pick an animation\n  setPage(next)\n})\n```\n\n### Fragment refs (19.3)\n\nPass a `ref` to `<Fragment>` to get a `FragmentInstance` - add event listeners, observe, or focus across a group of children without a wrapper `<div>`:\n\n```tsx\nconst ref = useRef<React.FragmentInstance>(null)   // DOM methods typed via @types/react-dom\n<Fragment ref={ref}><Item /><Item /></Fragment>\n```\n\n### `use(browser())` (19.3)\n\n`browser()` (`import { browser } from \"react-dom\"`) is a resource for `use()`: \"A component can call `use(browser())` to opt out of server-side rendering\" - it renders the nearest Suspense fallback on the server and renders for real on the client. Replaces the `useEffect`-mounted-flag pattern for client-only widgets.\n\n### Document metadata\n\nRender `<title>`, `<meta>`, `<link>` directly in components - React hoists them to `<head>`:\n\n```tsx\n<title>{post.title}</title>\n<meta name=\"description\" content={post.excerpt} />\n<link rel=\"canonical\" href={`https://example.com/posts/${post.slug}`} />\n```\n\n### Context as provider, ref cleanup\n\n```tsx\n<ThemeContext value=\"dark\">{children}</ThemeContext>   // no .Provider\n\n<div ref={(node) => {\n  const observer = new ResizeObserver(handleResize)\n  if (node) observer.observe(node)\n  return () => observer.disconnect()                    // cleanup return\n}} />\n```\n\n## React Compiler\n\n`babel-plugin-react-compiler` (stable **1.0**) analyzes code at build time and inserts memoization, replacing manual `useMemo`/`useCallback`/`memo` in most cases.\n\n### Setup with Vite\n\n`@vitejs/plugin-react` v6 **removed** the inline `babel` option, so the compiler runs through `@rolldown/plugin-babel`:\n\n```ts\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\n\nplugins: [react(), babel({ presets: [reactCompilerPreset()] })]\n```\n\n```bash\npnpm add -D --save-exact babel-plugin-react-compiler\npnpm add -D @rolldown/plugin-babel @babel/core @types/babel__core\n```\n\n`reactCompilerPreset({ compilationMode: 'annotation' })` compiles only components marked `\"use memo\"`; `target: '17' | '18'` supports older React (needs `react-compiler-runtime`).\n\n**Experimental native compiler (plugin-react 6.1+):** `react({ compiler: true })` runs the compiler in Rust via Oxc, no Babel. Install trap: its peer is `oxc-transform-react ^0.152.0`, so `pnpm add -D oxc-transform-react@latest` can fail with ERESOLVE once a newer minor ships - install the version the peer range names (`oxc-transform-react@0.152`). The old `compiler.logDiagnostics` option is deprecated in favor of `compiler.reportDiagnostics`. Stay on the Babel path for production until this graduates.\n\n### ESLint integration\n\nThe compiler's lint rules now ship **inside `eslint-plugin-react-hooks`** (v7.1, flat config by default, ESLint 10 supported): `recommended` carries the stable rules, `recommended-latest` adds the newest compiler-powered ones. If you lint with Biome instead, its nursery `useReactCompiler` rule surfaces the same compiler diagnostics (see [biome.md](biome.md)). The standalone `eslint-plugin-react-compiler` is merged in - remove it if present. New compiler-powered rules catch things like `setState` in render (`set-state-in-render`).\n\n### What not to do\n\n```tsx\n// Don't - the compiler handles all of this\nconst Memo = memo(MyComponent)\nconst value = useMemo(() => expensive(data), [data])\nconst cb = useCallback(() => handler(id), [id])\n```\n\nManual memoization still applies when you need a **stable value as an effect dependency**, or a value shared across many components (the compiler memoizes per-component). Opt a component out with the `\"use no memo\"` directive. Optimized components show a \"Memo ✨\" badge in React DevTools.\n\n## Worth knowing (newer surface)\n\n- **Partial Pre-rendering (19.2, stable)**: pre-render the static shell of a page, then finish it at request time. New react-dom APIs `prerender` (produce a prelude + a resumable state) and `resume`/`resumeToPipeableStream`/`resumeAndPrerender` continue rendering where the prerender left off. This is a framework/SSR-layer feature - reach for it through your framework, not hand-wired in an SPA.\n- **19.3 behavior changes:** Transitions now render independently instead of entangling into one render; StrictMode double-invokes effects during hydration (matching client roots); a DEV warning fires when a library calls `use()` to suspend but skips `use()` once its cache is warm.\n- **Trusted Types**: React passes `TrustedHTML`/`TrustedScript` values through without coercion. Server Components can render `<Context>` imported from a `'use client'` module directly.\n- **RSC security:** GHSA-wx67-qw84-cm4g (CVE-2026-44907, DoS in Server Functions) affects `react-server-dom-webpack`/`-parcel`/`-turbopack` 19.0.0-19.0.7, 19.1.0-19.1.8, 19.2.0-19.2.7; fixed in 19.0.8/19.1.9/19.2.8 and 19.3. Pure client SPAs are unaffected.\n- **`cacheSignal`** (RSC) tells you when a `cache()` lifetime is over.\n- **`captureOwnerStack()`** (dev-only) returns the component owner stack for better debugging.\n- **Performance Tracks**: React 19.2 adds Scheduler/Components tracks to Chrome DevTools profiles.\n- **`useDeferredValue`** gained an `initialValue` option.\n\n## Resources\n\n- React 19.3 blog: https://react.dev/blog/2026/09/09/react-19-3 - 19.2 blog: https://react.dev/blog/2025/10/01/react-19-2\n- React Compiler: https://react.dev/learn/react-compiler\n- Compiler 1.0: https://react.dev/blog/2025/10/07/react-compiler-1\n- API reference: https://react.dev/reference/react\n\nFile v0.4.0:references/shadcn.md\n\n# shadcn/ui\n\nCopy-in component patterns built on Tailwind v4 and a primitive library (Base UI, Radix UI, or React Aria). shadcn/ui is not a dependency you import - the CLI (latest `4.21.3`) writes component source into your project, which you then own and edit. For the Tailwind layer (theming, tokens) see [tailwind.md](tailwind.md).\n\n## Component authoring pattern\n\nThe canonical shadcn component is a **plain function** with `data-slot` for styling hooks, native props via `React.ComponentProps`, and variants via CVA. No `forwardRef` (React 19 - `ref` is a prop). Recent components also expose `data-variant`/`data-size` and a wider size scale.\n\n```tsx\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"cn\"\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground shadow hover:bg-primary/90\",\n        destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline: \"border border-input bg-background hover:bg-accent\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: { default: \"h-9 px-4 py-2\", sm: \"h-8 px-3\", lg: \"h-10 px-8\", icon: \"size-9\" },\n    },\n    defaultVariants: { variant: \"default\", size: \"default\" },\n  }\n)\n\nfunction Button({\n  className, variant, size, ...props\n}: React.ComponentProps<\"button\"> & VariantProps<typeof buttonVariants>) {\n  return (\n    <button\n      data-slot=\"button\"\n      data-variant={variant}\n      className={cn(buttonVariants({ variant, size }), className)}\n      {...props}\n    />\n  )\n}\n```\n\nThe `cn` helper merges class names with Tailwind conflict resolution. Since CLI 4.21 it is its own package, [`cn`](https://github.com/shadcn-ui/cn) - \"a drop-in replacement for `twMerge(clsx(...))`\", smaller and faster with the same API. `init` installs it and generates a re-export, and registry components import from `\"cn\"` directly:\n\n```ts\n// src/lib/utils.ts (generated by init)\nexport { cn } from \"cn\"\n```\n\nExisting projects on the clsx + tailwind-merge helper can switch with `shadcn migrate cn` (works without a `components.json`). `cn` targets Tailwind v4 like tailwind-merge v3; on Tailwind v3 stay on tailwind-merge v2.\n\n## CLI\n\n```bash\npnpm dlx shadcn@latest init        # scaffold config + tokens + lib/utils\npnpm dlx shadcn@latest add button card form   # add components\npnpm dlx shadcn@latest add button --overwrite # update an existing component\npnpm dlx shadcn@latest add button --dry-run   # preview what add would write, no changes\npnpm dlx shadcn@latest add button --diff      # show a diff against your current files (optional [path])\npnpm dlx shadcn@latest add button --view      # print the item's source without writing (optional [path])\n```\n\n`--dry-run`, `--diff`, and `--view` let you inspect an item before it touches your tree - useful before overwriting a component you've edited. The standalone `shadcn diff` command is deprecated in favor of `add --diff`.\n\n`init` adds `@import \"shadcn/tailwind.css\"` to your global CSS (custom variants like `data-open:` / `data-closed:` and utilities like `no-scrollbar`). `shadcn eject` inlines that file if you want full control.\n\n- **`create` is an alias of `init`** (not a separate command). `--defaults` resolves to `--template=next --preset=base-nova`. Templates: `next`, `start`, `vite`, `react-router`, `laravel`, `astro` - use `-t vite` for this stack.\n- **`--base base | radix | aria`** chooses the primitive library at init. **Base UI is the default since CLI 4.13** - non-interactive scripts or CI that expect Radix must pass `-b radix`. `aria` uses React Aria. Docs are split per base.\n- **`--pointer`** restores `cursor: pointer` on buttons (Tailwind v4 switched buttons to the default cursor); `--no-pointer` keeps v4's default.\n- **`--rtl`** / `migrate rtl` set up right-to-left support (rewrites `ml-4` -> `ms-4`, `text-left` -> `text-start`); `components.json` carries `rtl: true`.\n- **`shadcn docs`** fetches component documentation/API for an agent to read; `--base` selects base, radix, or aria.\n- **`shadcn migrate --list`** shows available migrations: `cn`, `icons`, `base-color` (both take `--from`/`--to`), `radix`, `rtl`.\n\n### Registries\n\nAdd from community/private registries, including any public GitHub repo as a **source registry** (no build step):\n\n```bash\npnpm dlx shadcn@latest add @acme/button          # named registry\npnpm dlx shadcn@latest add username/repo/item     # GitHub source registry (private repos via gh auth or GH_TOKEN)\npnpm dlx shadcn@latest registry validate          # validate before publishing\n```\n\nDiscover items with `search` (aliased as `list`). The registries arg is optional - omit it to\nsearch every registry in `components.json`:\n\n```bash\npnpm dlx shadcn@latest search @acme -q button -t ui   # filter by type: ui, block, hook (CSV)\npnpm dlx shadcn@latest search --json                  # machine-readable output\n```\n\n`--type`/`-t` and `--json` are new in CLI 4.11; before 4.11 `search` always printed JSON, so if\nyou script it, pass `--json` explicitly now that the default is human-readable.\n\nRegistries can also be declared in `package.json` (merged with `components.json`). Keep the CLI current: 4.13.1 fixed registry security issues - custom registry headers leaking on cross-origin redirects, path traversal from items without an explicit target, and flag injection via registry-supplied dependency strings.\n\nPresets are encoded codes that rewrite component code (not just colors): `shadcn preset decode <code>`, `shadcn apply <code> --only theme`.\n\n### Visual styles\n\n`init`/`create` offers eight built-in visual styles that rewrite component code (not only CSS variables): **Vega** (classic), **Nova** (compact), **Maia** (soft/rounded), **Lyra** (boxy/sharp, mono fonts), **Mira** (dense), **Luma**, **Rhea**, and **Sera** (minimal, editorial, typographic). All are available for Base UI, Radix, and React Aria.\n\n### MCP server\n\n```bash\npnpm dlx shadcn@latest mcp init    # exposes registry/search/add to an AI client\n```\n\n### Recent additions (4.12+)\n\n- **Chat-interface component family** - `MessageScroller`, `Message`, `Bubble`, `Attachment`, and `Marker` for building chat UIs.\n- **`@shadcn/react`** - a new package of unstyled, headless primitives (first one shipped: `@shadcn/react/message-scroller`) for when you want behavior without the styled shadcn layer.\n- **`scroll-fade` and `shimmer`** CSS utilities added to the shadcn utility set.\n\n## Primitives: Base UI, Radix, React Aria\n\nAll three are fully supported and selectable at init; Base UI is the default for new projects. Radix now uses the **unified `radix-ui` package** (not per-component `@radix-ui/react-*`):\n\n```tsx\nimport { Slot } from \"radix-ui\"\n```\n\n`migrate radix` rewrites old `@radix-ui/react-*` imports to the unified package. Base UI and React Aria (`--base aria`) are co-equal rebuilds of every component with the same abstraction.\n\n## Common patterns\n\n### Card\n\n```tsx\n<div className=\"rounded-xl border bg-card text-card-foreground shadow\">\n  <div className=\"flex flex-col space-y-1.5 p-6\">\n    <h3 className=\"font-semibold leading-none tracking-tight\">Title</h3>\n    <p className=\"text-sm text-muted-foreground\">Description</p>\n  </div>\n  <div className=\"p-6 pt-0\">Content</div>\n</div>\n```\n\n### Form field (with Zod)\n\nPair shadcn form components with React Hook Form + Zod, or React 19 Actions + `useActionState` for server-driven validation (see [react.md](react.md) and [typescript.md](typescript.md) for the Zod v4 pattern).\n\n### Accessibility\n\n```tsx\n<button className=\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\">\n<span className=\"sr-only\">Close dialog</span>\n<button className=\"disabled:cursor-not-allowed disabled:opacity-50\" disabled>\n```\n\n## Troubleshooting\n\n- **Dark mode flash:** add `suppressHydrationWarning` to `<html>`; ensure the theme provider uses `attribute=\"class\"`.\n- **Component overwritten on `add`:** you own the file - re-running `add` without `--overwrite` skips existing files.\n\n## Resources\n\n- Docs: https://ui.shadcn.com/docs - CLI: https://ui.shadcn.com/docs/cli\n- Changelog: https://ui.shadcn.com/docs/changelog - Registries: https://ui.shadcn.com/docs/registry\n\nFile v0.4.0:references/tailwind.md\n\n# Tailwind CSS v4\n\nUtility-first styling, CSS-first configuration. Tailwind **v4.3** (latest `4.3.3`) configures everything in CSS - there is no `tailwind.config.js`. In a Vite project the integration is the `@tailwindcss/vite` plugin (no PostCSS config). For shadcn/ui component authoring see [shadcn.md](shadcn.md).\n\n## CSS-first configuration\n\nEverything lives in your CSS entry. There is no JS/TS config file - migrate any old config into CSS:\n\n- `theme.extend.colors` -> `@theme { --color-*: ... }`\n- `plugins` -> `@plugin \"...\"` or `@utility`\n- `content` -> `@source \"...\"`\n- `tailwindcss-animate` -> `@import \"tw-animate-css\"`\n- `@layer utilities` -> `@utility name { ... }`\n\n```css\n@import \"tailwindcss\";\n\n@utility tab-highlight-none { -webkit-tap-highlight-color: transparent; }\n@custom-variant pointer-fine (@media (pointer: fine));\n@source not \"./legacy\";\n```\n\nThe `@import \"tailwindcss\";` line is mandatory - the Vite plugin alone emits nothing without it. A missing import is the classic \"Tailwind produces no styles\" bug.\n\n## Theming with CSS variables\n\nshadcn/ui maps semantic CSS variables to Tailwind utilities. Define variables under `:root` / `.dark`, then bridge them with `@theme inline`:\n\n```css\n:root {\n  --background: oklch(1 0 0);\n  --foreground: oklch(0.145 0 0);\n  --primary: oklch(0.205 0 0);\n  --primary-foreground: oklch(0.985 0 0);\n  --muted: oklch(0.97 0 0);\n  --border: oklch(0.922 0 0);\n  --radius: 0.5rem;\n}\n.dark {\n  --background: oklch(0.145 0 0);\n  --foreground: oklch(0.985 0 0);\n  --primary: oklch(0.922 0 0);\n  --primary-foreground: oklch(0.205 0 0);\n}\n\n@theme inline {\n  --color-background: var(--background);\n  --color-primary: var(--primary);\n  --color-primary-foreground: var(--primary-foreground);\n}\n```\n\n**`@theme` vs `@theme inline`:** plain `@theme` defines static tokens (overridable by plugins); `@theme inline` references CSS variables so the utility *follows* dark-mode changes. Use `inline` whenever a token points at a `var(--...)` that flips between `:root` and `.dark`.\n\n## Critical rules\n\n### Semantic tokens, paired foreground\n\n```tsx\n<div className=\"bg-primary text-primary-foreground\">    // respects theme + dark mode\n<div className=\"bg-blue-500 text-white\">                // breaks theming - avoid\n```\n\nAlways pair `bg-*` with the matching `text-*-foreground`. Background utilities omit the `-background` suffix (`bg-muted text-muted-foreground`).\n\n### Never build class names dynamically\n\n```tsx\n<div className={`bg-${color}-500`}>                      // scanner can't see it - no CSS emitted\nconst map = { red: \"bg-red-500\", blue: \"bg-blue-500\" } as const\n<div className={map[color]}>                             // complete literal strings\n```\n\n### `cn()` merge order\n\nDefaults first, consumer `className` last, so tailwind-merge's last-wins lets callers override:\n\n```tsx\nclassName={cn(buttonVariants({ variant, size }), className)}   // correct\n```\n\n### Transition only what changes\n\n`transition-all` thrashes layout. Name the properties, and respect reduced motion:\n\n```tsx\n<div className=\"transition-colors duration-200\">\n<div className=\"motion-safe:animate-fade-in\">\n```\n\n## Layout and responsiveness\n\nMobile-first breakpoints; container queries are first-class in v4 (no plugin):\n\n```tsx\n<div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n<div className=\"@container\">\n  <div className=\"grid gap-4 @sm:grid-cols-2 @lg:grid-cols-3\">  // responds to container, not viewport\n</div>\n```\n\nDark mode: prefer semantic colors (auto-flip) over manual `dark:` overrides. Use `next-themes` for the toggle (`attribute=\"class\"`), and add `suppressHydrationWarning` to `<html>` to avoid a flash.\n\n## Version-specific features\n\n### v4.1 / v4.2\n\n```tsx\n<h1 className=\"text-shadow-sm\">                          // text shadows (4.1)\n<div className=\"mask-b-from-50%\">                        // gradient masks (4.1)\n<input className=\"user-valid:border-success user-invalid:border-destructive\" /> // (4.1)\n<div className=\"pbs-4 pbe-8 mbs-2 border-bs-2\">          // logical block props (4.2)\n<div className=\"bg-mauve-100 text-olive-900\">            // new palettes: mauve/olive/mist/taupe (4.2)\n```\n\nThe positioning utilities `start-*`/`end-*` are **deprecated** in favor of `inset-s-*`/`inset-e-*` (don't confuse with logical padding `ps-*`/`pe-*`, which are fine).\n\n### v4.3 (current)\n\n```tsx\n<div className=\"scrollbar-thin scrollbar-thumb-muted scrollbar-gutter-stable\"> // scrollbar utils\n<div className=\"@container-size\">                        // size container for cqb/cqh units\n<img className=\"zoom-110\">                               // CSS zoom utilities\n<pre className=\"tab-4\">                                  // tab-size\n```\n\nIn CSS you can now stack and group `@variant`: `@variant hover:focus { ... }` and `@variant hover, focus { ... }`, and pass `--default(...)` to `--value(...)`/`--modifier(...)` in custom functional utilities.\n\n### Animations\n\n```css\n@import \"tw-animate-css\";\n```\n\n```tsx\n<div className=\"animate-fade-in\">\n```\n\n## OKLCH colors\n\nshadcn/ui colors use `oklch(lightness chroma hue)`: lightness 0-1, chroma 0-0.4 (0 = gray), hue 0-360. OKLCH gives perceptually uniform lightness, so dark-mode variants are easy to derive by adjusting L. Base neutral palettes: Neutral, Zinc, Slate, Stone, Gray.\n\n## Notes\n\n- A first-class `@tailwindcss/webpack` loader exists (added v4.2) for webpack projects, and `@tailwindcss/turbopack` for Next.js - relevant only if you're not on Vite.\n- 4.3.3 fixes `@tailwindcss/vite` triggering full page reloads for scanned files Vite processed but hadn't loaded as modules yet - upgrade if HMR keeps full-reloading.\n- To enforce semantic tokens in CI, Biome's nursery `noTailwindRawColors` rule flags palette classes like `bg-pink-500` (see [biome.md](biome.md)).\n- If you lint CSS with Biome, enable `css.parser.tailwindDirectives` so it understands `@theme`/`@utility`/`@apply` (see [biome.md](biome.md)).\n\n## Troubleshooting\n\n- **Colors not updating:** confirm the variable is in your CSS, `@theme inline` includes the mapping, then clear the build cache.\n- **`tailwind.config.js` present:** delete it; run `npx @tailwindcss/upgrade` to migrate to CSS-first.\n- **Classes not detected:** check `@source` covers your component paths and that no class name is constructed dynamically.\n- **A custom-token utility renders nothing:** a class like `bg-brand` whose token is not mapped under `@theme`/`@theme inline` emits no CSS and no error - tsc, Biome, and the Vite build all stay green. Cross-check the utility against your mapped tokens; a typo'd or unmapped token fails silently.\n\n## Resources\n\n- Docs: https://tailwindcss.com/docs - v4.3 blog: https://tailwindcss.com/blog/tailwindcss-v4-3\n- Vite plugin: https://tailwindcss.com/docs/installation/using-vite\n\nFile v0.4.0:references/typescript.md\n\n# TypeScript 7.0 (6.0-compatible)\n\nStrict TypeScript for React. **TS 7.0** (latest `7.0.2`, GA 2026-07-08) is the Go-native port - \"a 10x faster native port of TypeScript\" - and is what a plain `npm i -D typescript` installs today. Its type checker is a methodical port of 6.0, so the 6.0 defaults and deprecations below are the 7.0 rules too; 7.0 turns the 6.0 deprecations into hard errors.\n\n## What changed in 6.0/7.0 (and why your tsconfig shrinks)\n\nSeveral flags the old hand-tuned React tsconfig set manually are now **defaults**, so you delete them:\n\n- `strict` is **on by default**.\n- `noUncheckedSideEffectImports` is **on by default**.\n\nNew defaults that **break builds** if ignored:\n\n- `types` defaults to `[]`. Ambient `@types/*` no longer leak in globally - list what you need (`\"types\": [\"vite/client\", \"node\"]`). `[\"*\"]` restores the old include-everything behavior.\n- Side-effect imports are checked, so `import \"./styles.css\"` errors (TS2882) unless something declares the module - in a Vite app that is `vite/client` in `types`.\n- `module` defaults to `esnext` and `target` to a floating current-year ES version; they no longer default to `nodenext`. Pick deliberately per project type (below).\n- `rootDir` defaults to the tsconfig directory rather than being inferred from inputs - set it explicitly for non-trivial layouts.\n- 7.0 only: `libReplacement` is `false` by default and `stableTypeOrdering` is always on.\n\nDeprecated in 6.0, **errors in 7.0**:\n\n- `baseUrl` - use prefixed `paths` (`\"@/*\": [\"./src/*\"]`) only.\n- `moduleResolution: classic` / `node` / `node10` - use `bundler` or `nodenext`.\n- `esModuleInterop`, `allowSyntheticDefaultImports`, `alwaysStrict` set to `false`.\n- `target: es5` (lowest is ES2015) and `downlevelIteration`.\n- `--module amd|umd|systemjs|none` and `--outFile`.\n- Import-assertion `assert {}` syntax - use import-attributes `with {}`.\n- Legacy `module Foo {}` namespace syntax - use `namespace`.\n\n`\"ignoreDeprecations\": \"6.0\"` silences these on 6.0 only - 7.0 removes the flags outright, so treat it as a migration window, not a fix.\n\n## Strict tsconfig for a Vite React app\n\n```jsonc\n{\n  \"compilerOptions\": {\n    // strict, noUncheckedSideEffectImports: ON by default since 6.0\n    \"target\": \"es2023\",\n    \"module\": \"preserve\",\n    \"moduleResolution\": \"bundler\",\n    \"moduleDetection\": \"force\",\n    \"jsx\": \"react-jsx\",\n    \"verbatimModuleSyntax\": true,\n    \"erasableSyntaxOnly\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"skipLibCheck\": true,\n    \"noEmit\": true,\n    \"types\": [\"vite/client\"],\n    \"paths\": { \"@/*\": [\"./src/*\"] }\n  },\n  \"include\": [\"src\"]\n}\n```\n\n**`bundler` vs `nodenext`.** For code a bundler consumes (a Vite app), `module: preserve` + `moduleResolution: bundler` is correct - it lets you write extensionless imports and leaves module syntax for Vite/Rolldown. For code Node runs directly (scripts, a server entry), use `module: nodenext` (which sets resolution to match) and write real `.js` extensions on relative imports.\n\n**`exactOptionalPropertyTypes`** distinguishes \"absent\" from \"present but `undefined`\": an optional prop that may be passed `undefined` (e.g. forwarding an optional field) must be declared `prop?: T | undefined`.\n\n**`erasableSyntaxOnly`** (since 5.8) forbids TS constructs that emit runtime code (enums, parameter properties, namespaces with values), so your `.ts` files are pure type-erasable. This is what makes **Node's native type stripping** - now stable (Node 24.12 / 25.2) - work: Node can run `.ts` directly when paired with `erasableSyntaxOnly` + `verbatimModuleSyntax`. Keep it on for portability.\n\n## Patterns\n\n### Component props\n\n```tsx\ntype ButtonProps = React.ComponentProps<\"button\"> & { variant?: \"primary\" | \"secondary\"; isLoading?: boolean }\n\n// Polymorphic \"as\" prop\ntype PolymorphicProps<E extends React.ElementType> = { as?: E } & Omit<React.ComponentProps<E>, \"as\">\nfunction Text<E extends React.ElementType = \"span\">({ as, ...props }: PolymorphicProps<E>) {\n  const Component = as || \"span\"\n  return <Component {...props} />\n}\n```\n\n### Discriminated unions over booleans\n\nMake impossible states unrepresentable:\n\n```tsx\ntype AsyncState<T> =\n  | { status: \"idle\" }\n  | { status: \"loading\" }\n  | { status: \"error\"; error: Error }\n  | { status: \"success\"; data: T }\n```\n\nA `switch` over `status` with a `never` default gives exhaustiveness checking.\n\n### `satisfies` for config literals\n\nPreserves literal types while validating shape (unlike a `Record<string, T>` annotation, which widens):\n\n```tsx\nconst routes = {\n  home: { path: \"/\" },\n  about: { path: \"/about\" },\n} satisfies Record<string, { path: string }>\nroutes.home // autocompletes\n```\n\n### Hook and event types\n\n```tsx\nconst [user, setUser] = useState<User | null>(null)          // explicit for null init\nconst inputRef = useRef<HTMLInputElement>(null)               // React 19: RefObject<T | null>\nconst handleSubmit = (e: React.FormEvent<HTMLFormElement>) => e.preventDefault()\n```\n\nReducers use a discriminated-union action type; `useReducer(reducer, initial)` infers the rest.\n\n### Generic components\n\n```tsx\nfunction Select<T>({ items, value, onChange, getKey, getLabel }: {\n  items: T[]; value: T; onChange: (item: T) => void; getKey: (item: T) => string; getLabel: (item: T) => string\n}) {\n  return (\n    <select value={getKey(value)} onChange={(e) => {\n      const item = items.find((i) => getKey(i) === e.target.value)\n      if (item) onChange(item)\n    }}>\n      {items.map((item) => <option key={getKey(item)} value={getKey(item)}>{getLabel(item)}</option>)}\n    </select>\n  )\n}\n```\n\n### `import defer` (TS 5.9+)\n\nDefers module evaluation until first property access - useful for heavy, conditionally-used modules. Namespace imports only, and it is not downleveled, so it requires `module: preserve | esnext` and a runtime/bundler that supports it:\n\n```tsx\nimport defer * as heavy from \"./heavy-feature.js\"\n// heavy.* not evaluated until first access\n```\n\n### Zod v4 validation\n\n```tsx\nimport { z } from \"zod\"\nconst UserSchema = z.object({ name: z.string().min(1), email: z.email() })\ntype User = z.infer<typeof UserSchema>\n\nconst result = UserSchema.safeParse(Object.fromEntries(formData))\nif (!result.success) {\n  const flat = z.flattenError(result.error) // Zod v4 field-level errors\n  return flat.fieldErrors\n}\n```\n\n## TypeScript 7 in practice\n\n**No JS API in 7.0.** The `typescript@7` package exposes only `version` plus `unstable/*` subpaths - \"it does not ship with an API. We expect TypeScript 7.1 to ship with a new (and different) API.\" Tools that `import ts from \"typescript\"` break on 7.0. In this stack, Biome, `@vitejs/plugin-react`, and the shadcn CLI (bundles its own TS via ts-morph) are unaffected; **typescript-eslint** (peer `typescript <6.1.0`), and Volar-based checkers for **Vue, Astro, Svelte, and MDX** still need 6.0.\n\n**Side-by-side with 6.0** - the official alias pair keeps the 6.0 API for tools while `tsc` is 7.0:\n\n```json\n{\n  \"devDependencies\": {\n    \"@typescript/native\": \"npm:typescript@^7.0.2\",\n    \"typescript\": \"npm:@typescript/typescript6@^6.0.2\"\n  }\n}\n```\n\n`tsc` then runs 7.0 and `tsc6` runs 6.0; `require(\"typescript\")` resolves to the 6.0 API for typescript-eslint and friends. If a framework checker (`astro check`, `vue-tsc`) needs 6.0, keep it as the library and run 7.0 `tsc --noEmit` as a separate step for plain `.ts`.\n\n**Migrating:** \"Practically any TypeScript code that compiles cleanly with TypeScript 6.0 (with the `stableTypeOrdering` flag on, and without any `ignoreDeprecations` flag set) should compile identically in TypeScript 7.0.\" Get 6.0 clean under those two conditions first, then switch.\n\n**7.0-only behavior changes:**\n\n- `tsc file.ts` in a directory with a `tsconfig.json` errors (TS5112) unless you pass `--ignoreConfig`.\n- `/// <reference no-default-lib />` is no longer respected under `skipDefaultLibCheck`.\n- Template-literal inference treats Unicode code points as single characters.\n\n**Parallelism:** `--checkers` (default 4 type-check workers), `--builders` (parallel project-reference builds), `--singleThreaded`. On small CI runners lower `--checkers`, and fix the number across environments for reproducible results.\n\n**Nightlies** resume under the `typescript` package's `next` tag (`typescript@next`); `@typescript/native-preview` is frozen. TS 7.1 (new API, `es2026` target) is in beta.\n\n## Resources\n\n- TS 7.0 announcement: https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/\n- TS 6.0 announcement: https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/\n- Source and issues: https://github.com/microsoft/TypeScript - Release notes: https://www.typescriptlang.org/docs/handbook/release-notes/\n\nFile v0.4.0:references/vite.md\n\n# Vite 8\n\nBuild tooling and dev server. Vite 8 (stable, latest `8.3.3`) ships **Rolldown** - a Rust-based bundler from the Vite team - as its single default bundler, replacing both esbuild and Rollup. It is ESM-only and requires Node.js 20.19+ / 22.12+ (the rest of this stack - Vitest 5, `@rolldown/plugin-babel` - needs 22.12+).\n\n## Vite 8 essentials\n\n- **Rolldown is the default**, no opt-in. \"Vite 8 ships with Rolldown as its single, unified, Rust-based bundler.\" Build times drop dramatically vs the old esbuild+Rollup split.\n- **ESM-only config.** `vite.config.ts` must use `import`/`export`; `require()` is not supported in config files. Set `\"type\": \"module\"` in `package.json`: since 8.2 a `.ts` config loaded as CommonJS warns that it uses features unsupported by `configLoader: 'native'`, which \"is planned to become the default in a future major version\" (native TS config loading needs Node 22.18+).\n- **Default browser target** is `'baseline-widely-available'`, which in Vite 8 resolves to `['chrome111', 'edge111', 'firefox114', 'safari16.4', 'ios16.4']` (bumped from Vite 7's 107/107/104/16). Override with `build.target: 'es2022'` or an explicit list.\n- **Default minifiers changed:** JavaScript is minified by **Oxc** (`build.minify` default `'oxc'`), CSS by **Lightning CSS** (`build.cssMinify` default `'lightningcss'`). `build.minify: 'esbuild'` still works but is deprecated and requires installing `esbuild` yourself.\n- **Install grew ~15 MB** vs Vite 7 (Lightning CSS + the Rolldown binary are now regular dependencies).\n\n## New in Vite 8.1 - 8.3\n\n- **Wasm ESM integration (stable)** - import a `.wasm` file and call its exports directly: `import { add } from './add.wasm'`. No plugin needed.\n- **Experimental Bundled Dev Mode** (`experimental.bundledDev: true` or `--experimental-bundle`) - serves bundled files in dev instead of the classic unbundled server. Aimed at huge apps that suffer from module count (~15x faster startup in a 10k-component test); may not work with all third-party plugins yet.\n- **Experimental Chunk Import Map** (`build.chunkImportMap`) - uses an import map so a changed chunk's hash doesn't cascade new hashes to every importer, improving long-term cache hit rates. Does not compose with `experimental.renderBuiltUrl`.\n- **Lightning CSS as the future default** - Vite is working toward making Lightning CSS the default CSS transformer in the next major. Opt in early with `css: { transformer: 'lightningcss' }`.\n- `import.meta.glob` gained a `caseSensitive` option; `html.additionalAssetSources` lets asset discovery see custom HTML elements/attributes.\n- **Top-level `input`** (8.2) - one place to declare entry points; it becomes the default for `build.rolldownOptions.input`, `build.lib.entry`, `build.ssr`, and `optimizeDeps.entries`.\n- **Top-level `tsconfig`** (8.3) - forces one tsconfig for all files. Discouraged: it \"overrides Vite's per-file tsconfig discovery.\"\n- **`devtools`** option (8.3) - Vite DevTools now covers the dev server too: install `@vitejs/devtools-vite` (dev) and/or `@vitejs/devtools-rolldown` (build analysis); it runs for both `serve` and `build` unless limited with `apply`.\n- Dynamic `import()` accepts `#` subpath imports (from `package.json` `imports`).\n\n## Configuration\n\n### SPA with TanStack Router\n\n```ts\nimport { defineConfig } from 'vite'\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n  plugins: [\n    tanstackRouter({ autoCodeSplitting: true }), // framework plugin first\n    tailwindcss(),\n    react(),\n    babel({ presets: [reactCompilerPreset()] }),\n  ],\n  resolve: { alias: { '@': new URL('./src', import.meta.url).pathname } },\n})\n```\n\n### Full-stack with TanStack Start + Cloudflare\n\n```ts\nimport { defineConfig } from 'vite'\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite'\nimport { cloudflare } from '@cloudflare/vite-plugin'\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n  plugins: [\n    cloudflare(),\n    tanstackStart(),     // includes the router plugin internally - do NOT add both\n    tailwindcss(),\n    react(),\n    babel({ presets: [reactCompilerPreset()] }),\n  ],\n})\n```\n\n`tanstackStart({ spa: { enabled: true } })` runs SPA mode; `tanstackStart({ prerender: { enabled: true, crawlLinks: true } })` enables SSG.\n\n### Path aliases\n\nTwo options. Manual alias (works everywhere):\n\n```ts\nresolve: { alias: { '@': new URL('./src', import.meta.url).pathname } }\n```\n\nOr **new in Vite 8**, let Vite read tsconfig `paths` directly so you don't mirror them:\n\n```ts\nresolve: { tsconfigPaths: true }\n```\n\nIt is no longer experimental (8.2) and does follow project references - \"A config referenced by that config's `references` field is used when it matches the file\" - so solution-style setups (`tsconfig.json` -> `tsconfig.app.json`) work. The real limits: `paths` only apply to a file matched by a tsconfig's `files`/`include` (non-TS files such as CSS must be included explicitly), and they do not apply inside `.less` files. Footgun: Rolldown resolves tsconfig `paths` on its own at build time, so with `tsconfigPaths` off and no `resolve.alias`, `vite build` succeeds while `vite dev` fails with `Failed to resolve import \"@/...\"` - set one or the other deliberately.\n\n### Environment variables\n\nFiles: `.env`, `.env.local`, `.env.[mode]`, `.env.[mode].local`. Only `VITE_`-prefixed vars are exposed to client code via `import.meta.env`; everything else stays server-side. Built-in constants: `import.meta.env.MODE`, `.DEV`, `.PROD`, `.SSR`, `.BASE_URL`. Type them in `src/vite-env.d.ts`:\n\n```ts\ninterface ImportMetaEnv { readonly VITE_API_URL: string }\ninterface ImportMeta { readonly env: ImportMetaEnv }\n```\n\nOn Cloudflare, keep two channels straight: `VITE_`-prefixed `.env` values are statically injected into the **client** bundle at build time, while `.dev.vars` / Worker bindings are **runtime** server env passed to the handler - they are not available to client code, and vice versa.\n\n## Plugin ecosystem\n\n- **`@vitejs/plugin-react`** (v6) - Fast Refresh + JSX transform. v6 dropped Babel as a dependency (React Refresh runs through Oxc) and **removed the inline `babel` option**; run Babel-based transforms like the React Compiler through `@rolldown/plugin-babel` instead. Place last among framework plugins. v6 requires Vite 8 (use v5 if you must stay on Vite 7).\n- **`@tailwindcss/vite`** - native Tailwind v4 integration, no PostCSS config. API unchanged across v4.x.\n- **`@tanstack/router-plugin/vite`** - file-based routes; `tanstackRouter({ autoCodeSplitting: true })`. Must precede `react()`.\n- **`@tanstack/react-start/plugin/vite`** - full-stack TanStack Start; bundles the router plugin (don't add both).\n- **`@cloudflare/vite-plugin`** - runs Worker code in `workerd` during dev via the Environment API, matching production.\n\n## Dev server\n\n### Proxy\n\n```ts\nserver: {\n  proxy: {\n    '/api': { target: 'http://localhost:8787', changeOrigin: true, rewrite: (p) => p.replace(/^\\/api/, '') },\n    '/ws': { target: 'ws://localhost:8787', ws: true },\n  },\n}\n```\n\nProxy and most `server.*` changes are **not** hot-reloaded - restart the dev server after editing them.\n\n### allowedHosts (tunnels and custom domains)\n\nBy default Vite rejects requests whose Host header it doesn't recognize, which surfaces as `Blocked request. This host (...) is not allowed.` when you hit the dev server through ngrok, a custom domain, or a fallback port. Add the host:\n\n```ts\nserver: { allowedHosts: ['.ngrok-free.app', 'dev.example.com'] }\n```\n\nSetting it to `true` disables the check entirely and is a DNS-rebinding risk - scope it to known hosts.\n\n### forwardConsole (new in Vite 8)\n\n`server.forwardConsole` forwards browser runtime errors to the Vite server terminal - `true` forwards unhandled errors plus `console.error`/`console.warn` (not every log). It defaults to auto - **on when an AI coding agent is detected**, off otherwise - which is handy when an agent is driving the build and can't see the browser console.\n\n### HMR troubleshooting\n\n| Symptom | Fix |\n|---------|-----|\n| Full reload instead of HMR | Ensure `@vitejs/plugin-react` is loaded and a file exports a single component |\n| HMR not connecting behind a proxy | Set `server.ws.clientPort` (e.g. `443`) |\n| CSS not updating | Confirm `@tailwindcss/vite` is in plugins and `@import \"tailwindcss\";` is in your CSS entry |\n| Stale chunk after a build | Hard-refresh (`Cmd/Ctrl+Shift+R`) to bust the cached bundle |\n| `Tsconfig not found` from `builtin:vite-transform` in Docker/CI only | Vite's transform reads the full tsconfig `extends` chain - copy every extended tsconfig (e.g. the repo-root one) into the image |\n\nThe WebSocket knobs (`protocol`/`host`/`port`/`path`/`clientPort`/`timeout`/`server`) moved from `server.hmr.*` to `server.ws.*`. The old `server.hmr.*` keys are deprecated but auto-synced, so existing configs keep working; write new ones under `server.ws`.\n\n### File warmup\n\n```ts\nserver: { warmup: { clientFiles: ['./src/routes/__root.tsx', './src/components/*.tsx'] } }\n```\n\n### `cloudflare:workers` import errors\n\n`Failed to resolve import \"cloudflare:workers\"` (or `node:*` / `buffer` \"externalized for browser compatibility\") means Worker-only code is reaching the client graph - common with `createServerFn` + `import { env } from \"cloudflare:workers\"`, or web3/Solana SDKs that pull Node built-ins. Keep server-only imports in server modules; if a dependency forces it, externalize via `build.rolldownOptions.external`. Note `vite build` validates **all** emitted chunks (even behind dynamic import), so lazy-loading a heavy server chunk alone won't exclude it from the Worker build.\n\n## Build optimization\n\n### Code splitting (Rolldown)\n\nThe object form of `output.manualChunks` is **removed** in Vite 8 and the function form is deprecated - both will break or warn. Use Rolldown's `codeSplitting` via `build.rolldownOptions` (note: `build.rollupOptions` is now a deprecated alias of `build.rolldownOptions`, and the earlier `advancedChunks` name is deprecated - same shape, Vite warns `advancedChunks option is deprecated, please use codeSplitting instead`):\n\n```ts\nbuild: {\n  rolldownOptions: {\n    output: {\n      // see https://rolldown.rs/in-depth/manual-code-splitting\n      codeSplitting: {\n        groups: [\n          { name: 'react-vendor', test: /node_modules\\/(react|react-dom)\\// },\n          { name: 'tanstack', test: /node_modules\\/@tanstack\\// },\n        ],\n      },\n    },\n  },\n}\n```\n\nRoute-based splitting still comes for free with `tanstackRouter({ autoCodeSplitting: true })` - each route becomes its own chunk and shared code is extracted automatically.\n\n### Build defaults\n\n| Option | Default | Note |\n|--------|---------|------|\n| `build.target` | `baseline-widely-available` | chrome111/edge111/firefox114/safari16.4 |\n| `build.minify` | `'oxc'` (client), `false` (SSR) | Oxc minifier, 30-90x faster than terser |\n| `build.cssMinify` | `'lightningcss'` | set `'esbuild'` to revert (must install esbuild) |\n| `build.sourcemap` | `false` | use `'hidden'` for error tracking without exposing source |\n| `build.assetsInlineLimit` | `4096` | bytes below which assets inline as base64 |\n| `build.cssCodeSplit` | `true` | CSS stays with its async chunk |\n| `build.chunkSizeWarningLimit` | `500` | kB; large web3 deps routinely trip this (informational) |\n\n### Bundle analysis\n\n```ts\nimport { visualizer } from 'rollup-plugin-visualizer'\n// in plugins, gated to a mode:\nmode === 'analyze' && visualizer({ filename: 'stats.html', open: true, gzipSize: true })\n```\n\nVite DevTools (`devtools` option, `@vitejs/devtools-rolldown`) analyzes production builds without a plugin. Run the visualizer variant with `pnpm vite build --mode analyze`.\n\n### Noisy `INVALID_ANNOTATION` warnings\n\nRolldown warns when a dependency ships a misplaced `/*#__PURE__*/` hint. It is the dependency's bug, not yours - filter that one code from `node_modules` rather than silencing logs wholesale, so real warnings still surface:\n\n```ts\nbuild: {\n  rolldownOptions: {\n    onLog(level, log, defaultHandler) {\n      if (log.code === 'INVALID_ANNOTATION' && log.id?.includes('/node_modules/')) return\n      defaultHandler(level, log)\n    },\n  },\n}\n```\n\n### Tree shaking\n\nRolldown tree-shakes unused exports. Help it: use named ESM imports (`import { Button }`, not `import * as UI`), mark side-effect-free packages with `\"sideEffects\": false`, and avoid barrel files that re-export everything.\n\n### Chunk load errors after deploy\n\n```ts\nwindow.addEventListener('vite:preloadError', (e) => { e.preventDefault(); window.location.reload() })\n```\n\nServe `index.html` with `Cache-Control: no-cache` so clients don't hold stale asset references.\n\n## Migrating Vite 7 to Vite 8\n\nRolldown is built in, so remove any `rolldown-vite` aliasing. If you're coming straight from stock Vite 7, the team recommends an intermediate hop to isolate Rolldown-specific issues: first alias `vite` to `rolldown-vite` on Vite 7, fix any fallout, then upgrade to Vite 8 and undo the alias.\n\n```jsonc\n// Vite 7 intermediate step, then drop this for \"vite\": \"^8.0.0\"\n{ \"devDependencies\": { \"vite\": \"npm:rolldown-vite@7.2.2\" } }\n```\n\n`rolldown-vite` lives at `github.com/vitejs/rolldown-vite` (now **archived** - it was a technical preview, not an unrelated `nicepkg` repo). Other Vite 8 migration notes: `optimizeDeps.esbuildOptions` -> `optimizeDeps.rolldownOptions`; the `esbuild` config option -> `oxc`; `worker.rollupOptions` -> `worker.rolldownOptions`; consistent CJS interop and dropped format-sniffing resolution may surface edge cases (see https://vite.dev/guide/migration).\n\n## Environment API\n\nThe Environment API (formalized in Vite 6, now in **Release Candidate**) gives each target - browser, Node, edge - its own module graph, plugin pipeline, and build config. Most apps never touch it directly: `@cloudflare/vite-plugin` and `@tanstack/react-start` configure environments for you. Frameworks coordinate multi-environment builds through the `buildApp` builder hook. Direct use is for framework/runtime authors.\n\n## Deployment\n\n### Cloudflare Workers (via TanStack Start)\n\n```jsonc\n// wrangler.jsonc\n{\n  \"name\": \"my-app\",\n  \"compatibility_date\": \"2025-01-01\",\n  \"compatibility_flags\": [\"nodejs_compat\"],\n  \"main\": \"./dist/server/index.js\",\n  \"assets\": { \"directory\": \"./dist/client\" }\n}\n```\n\n```bash\npnpm vite build && pnpm wrangler deploy\n```\n\n### Static SPA / SSG\n\n`vite build` produces `dist/` for any static host. For prerendering, enable `tanstackStart({ prerender: { enabled: true, crawlLinks: true } })`.\n\n## Resources\n\n- Guide: https://vite.dev/guide/ - Vite 8 blog: https://vite.dev/blog/announcing-vite8\n- Migration: https://vite.dev/guide/migration - Build options: https://vite.dev/config/build-options\n- Rolldown: https://rolldown.rs - Cloudflare plugin: https://developers.cloudflare.com/workers/vite-plugin/\n\nFile v0.4.0:references/vitest.md\n\n# Vitest\n\nThe Vite-native test runner. Vitest **5** (latest `5.0.3`, released 2026-09-03) reuses your `vite.config.ts` - same plugins, resolve aliases, and transforms - so tests see the app exactly as the bundler builds it. It requires **Vite >= 6.4.0 and Node.js >= 22.12.0**, and `vite` is now a **required peer dependency** (install it explicitly - Yarn PnP and strict installs no longer get it transitively).\n\n> **Security:** Vitest 4.1.9 and below are affected by GHSA-p63j-vcc4-9vmv (critical - Browser Mode provider commands bypass the file-access gate, fixed in 4.1.10) and GHSA-82fw-gwwq-j7x9 (`@vitest/mocker` redirect-mock path traversal, fixed in 4.1.11 / 5.0.0). If you must stay on v4, pin `vitest@4.1.11` and every `@vitest/*` package to the same version.\n\n## Configuration\n\nVitest reads `vite.config.ts` by default - put the `test` block there and import `defineConfig` from `vitest/config` (not `vite`) to get typed test options. A separate `vitest.config.ts` is only needed when test settings must diverge from the build config. Vitest no longer looks for a config in parent directories.\n\n```ts\n// vite.config.ts\n/// <reference types=\"vitest/config\" />\nimport { defineConfig } from 'vitest/config'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n  test: {\n    globals: true,                    // optional: skip importing test/expect\n    environment: 'jsdom',             // 'jsdom' | 'happy-dom' | 'node' | 'edge-runtime'\n    setupFiles: ['./src/test/setup.ts'],\n    include: ['src/**/*.{test,spec}.{ts,tsx}'],\n    css: true,                        // process CSS imports per Vite rules\n    coverage: {\n      provider: 'v8',                 // default; or 'istanbul'\n      include: ['src/**/*.{ts,tsx}'], // required to report uncovered files\n      reporter: ['text', 'html', 'lcov'],\n    },\n  },\n})\n```\n\n```ts\n// src/test/setup.ts\nimport '@testing-library/jest-dom/vitest'   // registers DOM matchers with Vitest's expect\n```\n\nIf `globals: true`, add `\"vitest/globals\"` to tsconfig `compilerOptions.types`. **jsdom vs happy-dom:** jsdom is the safer default for React component tests; happy-dom is faster but covers a smaller API surface.\n\nAdd `.vitest` to `.gitignore` - in v5 it is the single artifact root (HTML/JSON/JUnit/blob reports, attachments, failure screenshots).\n\nInstall set:\n\n```bash\npnpm add -D vitest vite @vitejs/plugin-react jsdom \\\n  @testing-library/react @testing-library/dom @testing-library/jest-dom \\\n  @testing-library/user-event @vitest/coverage-v8\n```\n\n`@testing-library/react` (16.x) supports React 19 and requires the separate `@testing-library/dom` peer. Keep every `@vitest/*` package on the exact same version as `vitest` - they are exact-version peers.\n\n## Component test\n\n```tsx\n// src/components/Counter.test.tsx\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { expect, test } from 'vitest'   // omit if globals: true\nimport { Counter } from './Counter'\n\ntest('increments on click', async () => {\n  const user = userEvent.setup()\n  render(<Counter />)\n  expect(screen.getByText('Count: 0')).toBeInTheDocument()\n  await user.click(screen.getByRole('button', { name: /increment/i }))\n  expect(screen.getByText('Count: 1')).toBeInTheDocument()\n})\n```\n\n## Mocking\n\n```ts\nimport { afterEach, expect, test, vi } from 'vitest'\nimport { fetchUser } from './api'\nimport { greet } from './greet'\n\nvi.mock('./api', () => ({ fetchUser: vi.fn() }))   // hoisted to the top of the file\n\nafterEach(() => vi.useRealTimers())\n\ntest('greets the fetched user', async () => {\n  vi.mocked(fetchUser).mockResolvedValue({ name: 'Ada' })\n  await expect(greet('1')).resolves.toBe('Hello, Ada')   // async assertions must be awaited\n})\n\ntest('spies and fake timers', () => {\n  const log = vi.spyOn(console, 'log').mockImplementation(() => {})\n  vi.useFakeTimers()\n  setTimeout(() => console.log('tick'), 1000)\n  vi.advanceTimersByTime(1000)\n  expect(log).toHaveBeenCalledWith('tick')\n})\n```\n\n`vi.mock`/`vi.hoisted` must sit at module top level - in v5 calling them inside a function, block, or test **throws** (use `vi.doMock` for dynamic, non-hoisted mocks). New in v5, `vi.when` gives per-argument behavior without hand-written implementations:\n\n```ts\nconst getFlag = vi.fn<(key: string) => boolean>()\nvi.when(getFlag).calledWith('beta').thenReturn(true).calledWith('legacy').thenReturn(false)\n```\n\n## CLI\n\n```bash\nvitest                  # watch mode (default)\nvitest run              # single run - use in CI\nvitest --ui             # @vitest/ui dashboard (v5: token-authenticated)\nvitest run --coverage   # enable coverage\nvitest --typecheck      # type-level test mode\nvitest -p unit          # filter to a project (--project, repeatable)\nvitest doctor           # re-runs the suite with alternative configs, recommends faster options\n```\n\n## Coverage\n\nThe default provider is **v8** (`@vitest/coverage-v8`); `istanbul` is the alternative. The default reports only covered files, so set `coverage.include` to surface uncovered ones. In v5, `include`/`exclude` patterns match each file's path relative to the project root (no implicit \"contains\" matching), and a pattern without wildcards is a directory: `['src']` means `src/**`, not every path containing `src`. Re-check the reported file set after upgrading. When Vitest detects an AI coding agent, the `text` reporter auto-trims output (`skipFull: true` + a summary) to save tokens.\n\n## Browser Mode\n\nRuns tests in a real browser. The provider is an **imported factory object**, not a string:\n\n```ts\nimport { playwright } from '@vitest/browser-playwright'\n\ntest: {\n  browser: {\n    enabled: true,\n    provider: playwright(),\n    instances: [{ browser: 'chromium' }],   // at least one required\n    headless: true,\n  },\n}\n```\n\nSet it up with `npx vitest init browser`. Providers: `@vitest/browser-playwright` (recommended, supports parallelism), `@vitest/browser-preview` (local only - **not** for CI, it simulates events), and `@vitest/browser-webdriverio` (moved to the vitest-community org in v5). Render with `vitest-browser-react`; import `page`/`userEvent` from `vitest/browser`. v5 locators match text **exactly** by default, and browser `toHaveTextContent` is strict equality - partial/RegExp matching moved to `toMatchTextContent`. `browser.traceView: true` (experimental) records each interaction as a DOM snapshot you can step through in the UI or HTML report.\n\n## Projects\n\nUse `test.projects` in the root config (not the old `vitest.workspace.ts`):\n\n```ts\nexport default defineConfig({\n  test: {\n    projects: [\n      'packages/*',\n      { test: { name: 'unit', environment: 'jsdom', include: ['**/*.unit.test.ts'] } },\n      { extends: false, test: { name: 'node', environment: 'node', include: ['**/*.node.test.ts'] } },\n    ],\n  },\n})\n```\n\n**v5 change:** inline projects now **inherit the root config by default** (`extends` defaults to `true`), including `plugins`, `resolve.alias`, and `setupFiles` - arrays are merged, not replaced. Set `extends: false` for a project that must not see root plugins or setup files. Inline projects that don't change the Vite config share one Vite server (`sharedViteServer`), and a referenced project config may declare its own nested `projects`. Use `defineProject` for standalone project files - root-only keys (`coverage`, `reporters`) error inside a project.\n\n## Test speed\n\nVitest 5 is faster out of the box (shared Vite server, vm-pool reuse, `fsModuleCache` on by default - transformed modules persist on disk across runs; clear with `vitest --clearCache`). The duration breakdown (`environment 79%, import 13%, ...`) tells you where time goes; `vitest doctor` tries alternative configs for you. Beyond that, **the module graph dominates**:\n\n- Import the unit under test, not the app entry or a barrel file - a single barrel import can pull in hundreds of modules per test file.\n- `isolate: false` (or a project with it) for pure-function suites that touch no shared global state can cut runtime substantially; keep isolation for DOM and stateful tests.\n- Measure pool and cache tweaks before adopting them - on many suites they land within noise of the defaults.\n\n## Migrating from v4\n\n- **`clearMocks` defaults to `true`** - mock call history is cleared before every test; set `clearMocks: false` to keep v4 behavior.\n- **Unawaited async assertions fail** - `expect(p).resolves`/`.rejects`/`toMatchFileSnapshot` must be `await`ed.\n- **Inline projects inherit root config** (see Projects).\n- **Reporters write files**: `json`/`junit` go to `.vitest/` instead of stdout; HTML moved to `.vitest/index.html` and its option is `outputDir` (was `outputFile`).\n- **Removed entrypoints**: `vitest/coverage` -> `vitest/node`, `vitest/environments` -> `vitest/runtime`.\n- **Benchmarks rewritten**: `bench` is a test-context fixture, no longer a top-level import.\n- Coming from v3: `vite-node` was replaced by Vite's Module Runner, and `maxThreads`/`maxForks` became `maxWorkers`.\n\n## Resources\n\n- Guide: https://vitest.dev/guide/ - Config: https://vitest.dev/config/\n- Vitest 5 announcement: https://vitest.dev/blog/vitest-5 - Migration: https://vitest.dev/guide/migration\n- Mocking: https://vitest.dev/guide/mocking - Browser Mode: https://vitest.dev/guide/browser/\n\nFile v0.4.0:CHANGELOG.md\n\n# Changelog\n\nAll notable changes to this skill will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/2.0.0/),\nand this skill adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.4.0] - 2026-10-06\n\n### Changed\n- **Breaking:** TypeScript target moved to 7.0 (GA); typescript.md rewritten around TS 7 with a 6.0 compat path (`@typescript/typescript6` alias pair for typescript-eslint and other API consumers; Vue/Astro/Svelte/MDX stay on 6.0).\n- **Breaking:** Vitest target moved to 5.0 (requires Vite >= 6.4, Node >= 22.12); vitest.md covers the v4->v5 migration (clearMocks default, projects inherit by default, `.vitest/` output dir, unawaited async assertions fail, strict browser locators).\n- React 19.3: `<ViewTransition>`/`addTransitionType` now stable, Fragment refs, `use(browser())`.\n- shadcn CLI 4.21: Base UI is the default base, React Aria added, Sera style, `cn` package replaces clsx+tailwind-merge in `lib/utils`; `--pointer` description corrected.\n- Hono 4.13: `hono/<adapter>` imports deprecated in favor of `@hono/<runtime>` packages; v5 in development; Node >= 20 with `@hono/node-server@2`; zod-openapi 415 on mismatched Content-Type.\n- Vite 8.2/8.3: `codeSplitting` replaces deprecated `advancedChunks`; `resolve.tsconfigPaths` caveat corrected (it does follow references); `devtools` option; ESM config `\"type\": \"module\"` warning.\n- Version targets refreshed across SKILL.md and references; effective Node floor for the stack is 22.12.\n\n### Added\n- plugin-react 6.1 experimental native React Compiler (`react({ compiler: true })`).\n- Biome: Tailwind domain rules, `useReactCompiler`, ancestor-matching `!**/` exclusion gotcha.\n- Vitest: mocking basics, `vitest doctor`/`fsModuleCache`, test-speed guidance.\n- Hono: QUERY method, Method Not Allowed and Mount middleware, `hono/dev`, Vite `server.cors: false` seam.\n\n### Fixed\n- Canonical tsconfig: `\"types\": [\"vite/client\"]` (`types: []` broke CSS side-effect imports and `import.meta.env`).\n- Biome: domains never enable nursery rules - `noFloatingPromises` and friends must be enabled individually.\n- Tailwind: invalid `mask-linear-to-b` example; Vite `build.target` list missing `ios16.4`; stray tool-call markup at the end of hono.md.\n\n### Security\n- Vitest 4.1.9 affected by GHSA-p63j-vcc4-9vmv (critical) and GHSA-82fw-gwwq-j7x9; React 19.2.0-19.2.7 RSC by GHSA-wx67-qw84-cm4g; Hono < 4.13.11 by 9 advisories, `@hono/node-server` < 2.1.3 by GHSA-rmxm-3fg6-px4f.\n\nVerified against: vite@8.3.3, @vitejs/plugin-react@6.1.2, react@19.3.0, typescript@7.0.2, tailwindcss@4.3.3, @biomejs/biome@2.5.15, vitest@5.0.3, hono@4.13.13, shadcn@4.21.3, cn@0.4.0\n\n## [0.3.5] - 2026-09-09\n\n### Changed\n- Description condensed to fit the repo's 250-character limit.\n\n## [0.3.4] - 2026-08-21\n\n### Changed\n\n- Declared ClawHub browse categories (`development`) and topics in `metadata`, so the release pipeline publishes them instead of leaving the skill in the `other` category.\n\n### Removed\n\n- `skill-card.md`. The ClawHub CLI strips a root `skill-card.md` from every publish and the registry generates its own card, so the authored file never reached ClawHub.\n\n## [0.3.3] - 2026-08-07\n\n### Changed\n\n- Trimmed the frontmatter description to what-plus-when; dropped the trailing 20-term trigger-keyword sentence.\n\n## [0.3.2] - 2026-07-22\n\n### Added\n\n- skill-card.md release record following NVIDIA's skill-card format\n- metadata.openclaw block (emoji, homepage) for ClawHub display\n\n## [0.3.1] - 2026-07-10\n\n### Changed\n- CHANGELOG preamble pinned to Keep a Changelog 2.0.0 (format unchanged; KaC 2.0.0 keeps existing changelogs valid).\n\n## [0.3.0] - 2026-07-01\n\n### Changed\n- Refreshed version pins: vite 8.0.16->8.1.2, @vitejs/plugin-react 6.0.2->6.0.3,\n  tailwindcss 4.3.0->4.3.2, @biomejs/biome 2.4.16->2.5.2, vitest 4.1.8->4.1.9,\n  hono 4.12.25->4.12.27, shadcn CLI table 4.11.0->4.12.0 (metadata.upstream + Version targets + reference intros).\n- Biome: `linter.rules.recommended` is deprecated in favor of `linter.rules.preset` (Biome 2.5);\n  updated both canonical config examples (SKILL.md + biome.md) to `\"preset\": \"recommended\"` and noted `biome migrate`.\n- Vite: HMR WebSocket options moved from `server.hmr.*` to `server.ws.*`; corrected the HMR\n  troubleshooting entry to `server.ws.clientPort` and noted the deprecation/auto-sync.\n- TypeScript: reframed the tsgo/TS 7 section - TS 7.0 is now RC (~10x faster, targeted for stable\n  \"within the next month\"), with `@typescript/typescript6`/`tsc6` side-by-side install and\n  `--checkers`/`--builders`/`--singleThreaded` parallelism flags.\n\n### Added\n- Vite 8.1: WASM ESM integration (stable direct `.wasm` imports), experimental Bundled Dev Mode\n  (`experimental.bundledDev`) and Chunk Import Map (`build.chunkImportMap`), Lightning CSS being\n  evaluated as the next-major default CSS transformer.\n- React: Partial Pre-rendering (`prerender`/`resume` APIs, 19.2) added to the newer-surface list.\n- Biome: `--reporter=concise` (token-saving agent reporter), read-only `--watch` mode,\n  `formatter.delimiterSpacing`, and `biome upgrade`.\n- shadcn 4.12: chat-interface components + `@shadcn/react` headless package, `scroll-fade`/`shimmer`\n  utilities, and `add` inspection flags (`--dry-run`/`--diff`/`--view`).\n- Vite caveat: `resolve.tsconfigPaths: true` does not follow tsconfig project references (solution-style\n  configs) - use an explicit `resolve.alias` there.\n- Tailwind footgun: a utility referencing a token not mapped in `@theme`/`@theme inline` emits no CSS and no error.\n\n### Security\n- Bumped Hono pin to 4.12.27, covering two SSR advisories: `hono/jsx` cross-request context\n  disclosure (GHSA-hvrm-45r6-mjfj) and `hono/css` `cx()` XSS escaping bypass (GHSA-w62v-xxxg-mg59).\n\nVerified against: vite@8.1.2, @vitejs/plugin-react@6.0.3, tailwindcss@4.3.2, @biomejs/biome@2.5.2, vitest@4.1.9, hono@4.12.27\n\n## [0.2.0] - 2026-06-09\n\n### Added\n- references/hono.md - comprehensive Hono 4.12 reference: mental model, routing, Context,\n  HonoRequest, middleware (built-in + `createMiddleware` + chained type inference), validation\n  (`hono/validator`, `@hono/zod-validator`, Standard Schema), end-to-end type-safe RPC (`hc`,\n  status-code inference, larger-app chaining, `hcWithType` IDE-perf fix), OpenAPI\n  (`@hono/zod-openapi`), error handling (`HTTPException`/`onError`), helpers (cookie, streaming/SSE,\n  JWT sign/verify/decode, context-storage `getContext`, factory), realtime WebSocket\n  (`upgradeWebSocket` + RPC `$ws`), auth middleware (basic/bearer/jwt), server-side JSX,\n  static files across runtimes, multi-runtime deployment (Workers/Node/Bun/Deno), and testing.\n  Includes a prominent pointer to Hono's `llms-full.txt`/`llms.txt` as the authoritative\n  long-tail source, plus a categorized resource/link section.\n- SKILL.md: Hono added to the stack overview, references list, and version-targets table; new\n  cross-cutting rule on the Hono RPC seam (version match, `strict: true`, status codes, no\n  `c.notFound()` on RPC routes).\n- references/shadcn.md: documented the `search`/`list` command - new `-t, --type` and `--json`\n  flags, optional `[registries]` arg (searches all registries in `components.json` when omitted),\n  and the 4.11 switch of default output from JSON to human-readable.\n\n### Changed\n- Repositioned the skill from \"frontend\" to full-stack TypeScript: description and intro now\n  cover the Hono backend/edge layer and its RPC integration with the React frontend.\n- Bumped documented shadcn CLI version 4.10.0 -> 4.11.0 (SKILL.md version table + shadcn.md).\n\nVerified against: hono@4.12.25, @hono/node-server@2.0.4, @hono/zod-validator@0.8.0, @hono/zod-openapi@1.4.0\n\n## [0.1.0] - 2026-06-05\n\n### Added\n- Initial release. Merges the former `vite`, `react-typescript`, `shadcn-tailwind`, and `biome`\n  skills into one cohesive TypeScript frontend skill, plus net-new Vitest coverage.\n- SKILL.md cross-cutting layer: stack overview, version targets, the rules that bite at the\n  seams between tools, and one end-to-end working setup (vite.config.ts, tsconfig.json,\n  biome.json, styles.css, a canonical component).\n- references/vite.md - Vite 8 (Rolldown default), dev server, code splitting, build, deployment.\n- references/react.md - React 19.2 patterns and the React Compiler 1.0.\n- references/typescript.md - TypeScript 6.0 config and patterns.\n- references/tailwind.md - Tailwind CSS v4.3 CSS-first config and theming.\n- references/shadcn.md - shadcn/ui CLI 4.10 and component authoring.\n- references/biome.md - Biome 2.4 lint/format/imports.\n- references/vitest.md - Vitest 4 testing (net-new; not present in any source skill).\n\n### Notes\n- Retargets the former Vite content from Vite 7 to Vite 8: Rolldown is the single default\n  bundler, object-form `manualChunks` removed in favor of Rolldown `codeSplitting`,\n  `build.rollupOptions` -> `build.rolldownOptions`, default minifiers Oxc (JS) and\n  Lightning CSS, browser target chrome111/edge111/firefox114/safari16.4, React Compiler wired\n  via `reactCompilerPreset` + `@rolldown/plugin-babel`.\n- TypeScript content advanced from 5.9 to 6.0 (strict default-on, `types: []`, module/target\n  default shifts, `baseUrl` deprecated, `erasableSyntaxOnly`, tsgo note).\n- shadcn CLI corrected from 3.0 to 4.10 (`create` is an alias of `init`, unified `radix-ui`\n  import, GitHub source registries, new styles). Tailwind advanced 4.2 -> 4.3. Biome 2.4.13 -> 2.4.16.\n\nVerified against: vite@8.0.16, @vitejs/plugin-react@6.0.2, react@19.2.7, typescript@6.0.3, tailwindcss@4.3.0, @biomejs/biome@2.4.16, vitest@4.1.8, babel-plugin-react-compiler@1.0.0, class-variance-authority@0.7.1\n\nFile v0.4.0:skill-card.md\n\n## Description:\n\nProvides full-stack TypeScript development guidance for Vite, React, Tailwind CSS, shadcn/ui, Biome, Vitest, and Hono.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[tenequm](https://clawhub.ai/user/tenequm)\n\n### License/Terms of Use:\n\nApache-2.0\n\n## Use Case:\n\nDevelopers use this skill to set up and maintain full-stack TypeScript applications, including components, styling, builds, testing, linting, and type-safe Hono APIs.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Installing packages with floating versions can make project setup difficult to reproduce.\n\nMitigation: Pin dependency versions and review package changes before installation.\n\nRisk: Framework or component scaffolding may change existing project files.\n\nMitigation: Review generated diffs before accepting them.\n\n## Reference(s):\n\n- [Vite reference](references/vite.md)\n- [React reference](references/react.md)\n- [TypeScript reference](references/typescript.md)\n- [Tailwind CSS reference](references/tailwind.md)\n- [shadcn/ui reference](references/shadcn.md)\n- [Biome reference](references/biome.md)\n- [Vitest reference](references/vitest.md)\n- [Hono reference](references/hono.md)\n- [Project homepage (listed in skill metadata)](https://github.com/tenequm/skills/tree/main/skills/typescript-dev)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Code, Shell commands, Configuration]\n\n**Output Format:** [Markdown with code and shell examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [None]\n\n## Skill Version(s):\n\n0.4.0 (source: release metadata, skill metadata, CHANGELOG)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v0.3.5: 13 files, 53528 bytes\n\nFiles: CHANGELOG.md (7226b), LICENSE.txt (9157b), references/biome.md (9764b), references/hono.md (27986b), references/react.md (8325b), references/shadcn.md (7112b), references/tailwind.md (6436b), references/typescript.md (7778b), references/vite.md (12907b), references/vitest.md (5555b), skill-card.md (2652b), SKILL.md (14108b), _meta.json (133b)\n\nFile v0.3.5:SKILL.md\n\n---\nname: typescript-dev\ndescription: Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - components, styling, build and HMR, tests, lint/CI, or a Hono API with type-safe RPC.\nmetadata:\n  version: \"0.3.5\"\n  categories: \"development\"\n  topics: \"typescript, vite, react, tailwind, hono\"\n  openclaw:\n    homepage: https://github.com/tenequm/skills/tree/main/skills/typescript-dev\n    emoji: \"🟦\"\n  upstream: \"vite@8.1.2, @vitejs/plugin-react@6.0.3, react@19.2.7, typescript@6.0.3, tailwindcss@4.3.2, @biomejs/biome@2.5.2, vitest@4.1.9, babel-plugin-react-compiler@1.0.0, class-variance-authority@0.7.1, hono@4.12.27\"\n---\n\n# TypeScript Frontend Development\n\nOne coherent stack for building type-safe TypeScript apps: **Vite 8** (build + dev server, Rolldown-powered), **React 19.2** with the React Compiler, **TypeScript 6.0** (strict), **Tailwind CSS v4.3 + shadcn/ui** for styling, **Biome 2.4** for linting and formatting, **Vitest 4** for testing, and **Hono 4** for the backend/edge API. The pieces are designed to fit together - this skill covers how they wire up and the sharp edges that span more than one of them. Hono's RPC client (`hc`) shares server types directly with the React frontend, so the front and back end stay type-safe end to end without codegen.\n\nThe body below is the cross-cutting layer: the rules that bite when these tools meet, plus one working end-to-end setup. Each tool also has a deep-dive reference - read the one you need:\n\n- **[references/vite.md](references/vite.md)** - Vite 8 config, dev server, proxy, HMR, Rolldown, code splitting, build optimization, deployment.\n- **[references/react.md](references/react.md)** - React 19 patterns: Actions, `use()`, Activity, `useEffectEvent`, document metadata, and the React Compiler.\n- **[references/typescript.md](references/typescript.md)** - Strict TypeScript 6.0 config and patterns: tsconfig defaults, generics, utility types, `import defer`, tsgo.\n- **[references/tailwind.md](references/tailwind.md)** - Tailwind CSS v4 CSS-first config, OKLCH theming, dark mode, v4.3 utilities.\n- **[references/shadcn.md](references/shadcn.md)** - shadcn/ui CLI, component authoring with CVA + `data-slot`, registries, Radix vs Base UI.\n- **[references/biome.md](references/biome.md)** - Biome config, `biome check`, domains, type-aware linting, GritQL, ESLint/Prettier migration.\n- **[references/vitest.md](references/vitest.md)** - Vitest config, Testing Library, jsdom/happy-dom, coverage, browser mode, projects.\n- **[references/hono.md](references/hono.md)** - Hono 4 web framework: routing, context, middleware, validation (Zod), end-to-end type-safe RPC, OpenAPI, helpers, and multi-runtime deployment (Workers/Node/Bun/Deno).\n\n## Version targets\n\n| Tool | Version | Note |\n|------|---------|------|\n| Vite | 8.1.2 | Rolldown is the single default bundler |\n| @vitejs/plugin-react | 6.0.3 | v6 removed the inline `babel` option |\n| React / react-dom | 19.2.7 | React Compiler is stable (1.0) |\n| babel-plugin-react-compiler | 1.0.0 | pin with `--save-exact` |\n| TypeScript | 6.0.3 | last JS-based TS; TS 7.0 (tsgo) now RC |\n| Tailwind CSS | 4.3.2 | CSS-first config, no JS config file |\n| shadcn/ui CLI | 4.12.0 | `create` is an alias of `init` |\n| Biome | 2.5.2 | single binary for lint + format + imports |\n| Vitest | 4.1.9 | Vite-native test runner; reuses vite.config |\n| Hono | 4.12.27 | Web Standards backend/edge framework; no v5 |\n\n## Cross-cutting critical rules\n\nThese are the rules that fail in confusing ways precisely because they sit at the seam between two tools. The single-tool details live in the references.\n\n### Vite plugin order: framework plugins first, `react()` last\n\nWhen a framework plugin (TanStack Router/Start, etc.) generates routes or transforms code, it must run before `@vitejs/plugin-react` so React's Fast Refresh transform sees the final output. Wrong order causes route-generation failures and broken HMR.\n\n```ts\nplugins: [\n  tanstackStart(),   // or tanstackRouter() for SPA - framework first\n  tailwindcss(),\n  react(),           // React plugin last among framework plugins\n]\n```\n\n### React Compiler replaces manual memoization - and changes how you wire Vite\n\nReact Compiler 1.0 auto-memoizes components, computations, and callbacks at build time. Write plain components; do not reach for `useMemo`/`useCallback`/`memo`. The catch lives at the Vite seam: **`@vitejs/plugin-react` v6 removed the inline `babel` option**, so the old `react({ babel: { plugins: [...] } })` wiring no longer works. The compiler now runs through a separate Babel plugin:\n\n```ts\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\n\nplugins: [react(), babel({ presets: [reactCompilerPreset()] })]\n```\n\nInstall: `pnpm add -D @rolldown/plugin-babel @babel/core babel-plugin-react-compiler @types/babel__core`.\n\nThis also ripples into Biome: `useExhaustiveDependencies` can't tell the compiler is handling deps for you, so most compiler users turn it off (see [biome.md](references/biome.md)).\n\n### Tailwind v4 is CSS-first - there is no `tailwind.config.js`\n\nTailwind v4 configures everything in CSS via `@theme`, `@utility`, `@plugin`, `@source`. Never create or look for `tailwind.config.js`/`.ts`. The Vite integration is the `@tailwindcss/vite` plugin (no PostCSS config either). If you find a `tailwind.config.js` in a v4 project, it is leftover - delete it and migrate the values into CSS. Full details in [tailwind.md](references/tailwind.md).\n\n### Style with semantic tokens, never raw palette or dynamic class names\n\n```tsx\n<div className=\"bg-primary text-primary-foreground\">   // respects theme + dark mode\n<div className=\"bg-blue-500 text-white\">               // breaks theming - avoid\n```\n\nAnd never assemble class names from fragments (`bg-${color}-500`) - Tailwind's scanner only sees complete literal strings, so dynamic names silently produce no CSS. Use a lookup map of full class strings.\n\n### TypeScript 6.0 changed the defaults - lean on them, don't fight them\n\nTS 6.0 bakes in much of what used to be manual: `strict` and `noUncheckedSideEffectImports` are now **on by default**, so drop them from a fresh tsconfig. But two new defaults will break builds if you ignore them: `types` now defaults to `[]` (add `\"types\": [\"node\"]` if you use Node globals) and `module`/`target` shifted (`module` defaults to `esnext`, not `nodenext`). `baseUrl` is deprecated - use prefixed `paths` instead. See [typescript.md](references/typescript.md) for the full 6.0 tsconfig and migration notes.\n\n### One Biome command, and `files.includes` is the only include key\n\nRun `biome check` (or `biome ci`) - it formats, lints, and organizes imports in a single pass; never split into separate `lint`+`format` calls. And in Biome 2.x the only file-selection key is `files.includes` (with the `s`); `files.ignore`/`files.include`/`files.exclude` do not exist and throw `Found an unknown key`. Exclude with negation: `\"includes\": [\"**\", \"!**/routeTree.gen.ts\"]`. More in [biome.md](references/biome.md).\n\n### Hono RPC ties the backend's types to the React frontend - keep them in sync\n\nWhen the API is Hono, the React app talks to it through the `hc<AppType>()` client, which\nimports the server's exported `typeof app` directly. That shared type is the seam: it only\nworks if **both sides run the same Hono version** and both `tsconfig.json` set `\"strict\": true`\n(a mismatch throws \"Type instantiation is excessively deep\"). Two more rules that bite at this\nseam: handlers must specify status codes (`c.json(data, 200)`) for the client to infer\nresponses, and routes the client calls must not use `c.notFound()`. As the route count grows,\ncompile the client type once (`hcWithType`) so the IDE stays fast. Full details in\n[hono.md](references/hono.md).\n\n## End-to-end setup\n\nA minimal but complete React + TypeScript + Tailwind + Biome project. Swap the framework plugin for your router/SSR choice (see [vite.md](references/vite.md) for TanStack and Cloudflare variants).\n\n### vite.config.ts\n\n```ts\nimport { defineConfig } from 'vite'\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n  plugins: [\n    tailwindcss(),\n    react(),\n    babel({ presets: [reactCompilerPreset()] }),\n  ],\n  resolve: {\n    alias: { '@': new URL('./src', import.meta.url).pathname },\n  },\n})\n```\n\n`import.meta.url` is the ESM-correct way to resolve paths - there is no `__dirname` in an ESM config, and Vite configs are ESM-only.\n\n### tsconfig.json (TypeScript 6.0)\n\n```jsonc\n{\n  \"compilerOptions\": {\n    // strict + noUncheckedSideEffectImports are ON by default in 6.0 - omitted on purpose\n    \"target\": \"es2023\",\n    \"module\": \"preserve\",\n    \"moduleResolution\": \"bundler\",\n    \"moduleDetection\": \"force\",\n    \"jsx\": \"react-jsx\",\n    \"verbatimModuleSyntax\": true,\n    \"isolatedModules\"\n\nArchive v0.3.4: 13 files, 53550 bytes\n\nFiles: CHANGELOG.md (7125b), LICENSE.txt (9157b), references/biome.md (9764b), references/hono.md (27986b), references/react.md (8325b), references/shadcn.md (7112b), references/tailwind.md (6436b), references/typescript.md (7778b), references/vite.md (12907b), references/vitest.md (5555b), skill-card.md (2502b), SKILL.md (14505b), _meta.json (133b)\n\nArchive v0.3.3: 13 files, 53617 bytes\n\nFiles: CHANGELOG.md (6731b), LICENSE.txt (9157b), references/biome.md (9764b), references/hono.md (27986b), references/react.md (8325b), references/shadcn.md (7112b), references/tailwind.md (6436b), references/typescript.md (7778b), references/vite.md (12907b), references/vitest.md (5555b), skill-card.md (3184b), SKILL.md (14425b), _meta.json (133b)\n\nArchive v0.3.2: 13 files, 53512 bytes\n\nFiles: CHANGELOG.md (6580b), LICENSE.txt (9157b), references/biome.md (9764b), references/hono.md (27986b), references/react.md (8325b), references/shadcn.md (7112b), references/tailwind.md (6436b), references/typescript.md (7778b), references/vite.md (12907b), references/vitest.md (5555b), skill-card.md (3125b), SKILL.md (14636b), _meta.json (133b)\n\nArchive v0.3.1: 13 files, 53365 bytes\n\nFiles: CHANGELOG.md (6411b), LICENSE.txt (9157b), references/biome.md (9764b), references/hono.md (27986b), references/react.md (8325b), references/shadcn.md (7112b), references/tailwind.md (6436b), references/typescript.md (7778b), references/vite.md (12907b), references/vitest.md (5555b), skill-card.md (3118b), SKILL.md (14526b), _meta.json (133b)\n\nArchive v0.3.0: 13 files, 53189 bytes\n\nFiles: CHANGELOG.md (6256b), LICENSE.txt (9157b), references/biome.md (9764b), references/hono.md (27986b), references/react.md (8325b), references/shadcn.md (7112b), references/tailwind.md (6436b), references/typescript.md (7778b), references/vite.md (12907b), references/vitest.md (5555b), skill-card.md (2817b), SKILL.md (14526b), _meta.json (133b)\n\nArchive v0.2.0: 13 files, 49536 bytes\n\nFiles: CHANGELOG.md (4016b), LICENSE.txt (9157b), references/biome.md (8795b), references/hono.md (27518b), references/react.md (7928b), references/shadcn.md (6267b), references/tailwind.md (6139b), references/typescript.md (6660b), references/vite.md (11281b), references/vitest.md (5555b), skill-card.md (2804b), SKILL.md (14525b), _meta.json (133b)\n\nArchive v0.1.0: 12 files, 36673 bytes\n\nFiles: CHANGELOG.md (2224b), LICENSE.txt (9157b), references/biome.md (8795b), references/react.md (7928b), references/shadcn.md (5764b), references/tailwind.md (6139b), references/typescript.md (6660b), references/vite.md (11281b), references/vitest.md (5555b), skill-card.md (2707b), SKILL.md (13306b), _meta.json (133b)","readmeExcerpt":"Skill: typescript-dev Owner: tenequm Summary: Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - components, styling, build and HMR, tests, lint/CI, or a Hono API with type-safe RPC. Tags: latest:0.4.0 Version history: v0.4.0 | 2026-10-06T12:21:40.608Z | user Updated typescript-dev from 0.3.5 to 0.4.0. Changes: - mod","codeSnippets":[],"executableExamples":[{"language":"ts","snippet":"plugins: [\n  tanstackStart(),   // or tanstackRouter() for SPA - framework first\n  tailwindcss(),\n  react(),           // React plugin last among framework plugins\n]"},{"language":"ts","snippet":"import react, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\n\nplugins: [react(), babel({ presets: [reactCompilerPreset()] })]"},{"language":"tsx","snippet":"<div className=\"bg-primary text-primary-foreground\">   // respects theme + dark mode\n<div className=\"bg-blue-500 text-white\">               // breaks theming - avoid"},{"language":"ts","snippet":"import { defineConfig } from 'vite'\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\nimport tailwindcss from '@tailwindcss/vite'\n\nexport default defineConfig({\n  plugins: [\n    tailwindcss(),\n    react(),\n    babel({ presets: [reactCompilerPreset()] }),\n  ],\n  resolve: {\n    alias: { '@': new URL('./src', import.meta.url).pathname },\n  },\n})"},{"language":"jsonc","snippet":"{\n  \"compilerOptions\": {\n    // strict + noUncheckedSideEffectImports are ON by default since 6.0 - omitted on purpose\n    \"target\": \"es2023\",\n    \"module\": \"preserve\",\n    \"moduleResolution\": \"bundler\",\n    \"moduleDetection\": \"force\",\n    \"jsx\": \"react-jsx\",\n    \"verbatimModuleSyntax\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"erasableSyntaxOnly\": true,\n    \"skipLibCheck\": true,\n    \"noEmit\": true,\n    \"types\": [\"vite/client\"],\n    \"paths\": { \"@/*\": [\"./src/*\"] }\n  },\n  \"include\": [\"src\"]\n}"},{"language":"json","snippet":"{\n  \"$schema\": \"./node_modules/@biomejs/biome/configuration_schema.json\",\n  \"vcs\": { \"enabled\": true, \"clientKind\": \"git\", \"useIgnoreFile\": true },\n  \"files\": { \"includes\": [\"**\", \"!**/components/ui\", \"!**/routeTree.gen.ts\"] },\n  \"formatter\": { \"enabled\": true, \"indentStyle\": \"space\", \"lineWidth\": 100 },\n  \"linter\": {\n    \"enabled\": true,\n    \"rules\": { \"preset\": \"recommended\" },\n    \"domains\": { \"react\": \"recommended\" }\n  },\n  \"javascript\": { \"formatter\": { \"quoteStyle\": \"double\" } },\n  \"assist\": { \"enabled\": true, \"actions\": { \"source\": { \"organizeImports\": \"on\" } } }\n}"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: typescript-dev\ndescription: Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - components, styling, build and HMR, tests, lint/CI, or a Hono API with type-safe RPC.\nmetadata:\n  version: \"0.4.0\"\n  categories: \"development\"\n  topics: \"typescript, vite, react, tailwind, hono\"\n  openclaw:\n    homepage: https://github.com/tenequm/skills/tree/main/skills/typescript-dev\n    emoji: \"🟦\"\n  upstream: \"vite@8.3.3, @vitejs/plugin-react@6.1.2, react@19.3.0, typescript@7.0.2, tailwindcss@4.3.3, @biomejs/biome@2.5.15, vitest@5.0.3, babel-plugin-react-compiler@1.0.0, class-variance-authority@0.7.1, hono@4.13.13, shadcn@4.21.3, cn@0.4.0\"\n---\n\n# TypeScript Frontend Development\n\nOne coherent stack for building type-safe TypeScript apps: **Vite 8** (build + dev server, Rolldown-powered), **React 19.3** with the React Compiler, **TypeScript 7.0** (strict, Go-native), **Tailwind CSS v4.3 + shadcn/ui** for styling, **Biome 2.5** for linting and formatting, **Vitest 5** for testing, and **Hono 4** for the backend/edge API. The pieces are designed to fit together - this skill covers how they wire up and the sharp edges that span more than one of them. Hono's RPC client (`hc`) shares server types directly with the React frontend, so the front and back end stay type-safe end to end without codegen.\n\nThe body below is the cross-cutting layer: the rules that bite when these tools meet, plus one working end-to-end setup. Each tool also has a deep-dive reference - read the one you need:\n\n- **[references/vite.md](references/vite.md)** - Vite 8 config, dev server, proxy, HMR, Rolldown, code splitting, build optimization, deployment.\n- **[references/react.md](references/react.md)** - React 19 patterns: Actions, `use()`, Activity, `<ViewTransition>`, Fragment refs, `useEffectEvent`, document metadata, and the React Compiler.\n- **[references/typescript.md](references/typescript.md)** - Strict TypeScript 7.0 config and patterns: tsconfig defaults, the 6.0 compat path for API-consuming tools, generics, `import defer`.\n- **[references/tailwind.md](references/tailwind.md)** - Tailwind CSS v4 CSS-first config, OKLCH theming, dark mode, v4.3 utilities.\n- **[references/shadcn.md](references/shadcn.md)** - shadcn/ui CLI, component authoring with CVA + `data-slot`, the `cn` package, registries, Base UI / Radix / React Aria.\n- **[references/biome.md](references/biome.md)** - Biome config, `biome check`, domains, type-aware linting, GritQL, ESLint/Prettier migration.\n- **[references/vitest.md](references/vitest.md)** - Vitest 5 config, Testing Library, mocking, coverage, browser mode, projects, v4->v5 migration, test speed.\n- **[references/hono.md](references/hono.md)** - Hono 4 web framework: routing, context, middleware, validation (Zod), end-to-end type-safe RPC, OpenAPI, helpers, and multi-runtime deployment (Workers/Node/Bun/Deno).\n\n## Version targets\n\n| Tool | Version | Note |"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn76gpsgjw5chv0xvzbzcb8cxn81x46r\",\n  \"slug\": \"typescript-dev\",\n  \"version\": \"0.4.0\",\n  \"publishedAt\": 1791289300608\n}"},{"path":"references/biome.md","content":"# Biome\n\nFast, unified linting, formatting, and import organization for JS/TS/JSX/CSS/GraphQL in a single binary. Biome **2.5** (latest `2.5.15`) does type-aware linting without the TypeScript compiler, GritQL plugins for custom rules, and domain-based rule grouping. Zero config by default, ~97% Prettier compatibility.\n\n## Critical rules\n\n### `files.includes` is the only file key\n\nBiome 2.x supports only `files.includes` (with the `s`). There is **no** `files.ignore`, `files.include`, or `files.exclude` - any of them throws `Found an unknown key`. The valid `files` keys are `includes`, `maxSize`, `ignoreUnknown`. Exclude with negation patterns:\n\n```json\n{ \"files\": { \"includes\": [\"**\", \"!**/routeTree.gen.ts\", \"!**/generated/**\"] } }\n```\n\nFor paths the scanner must skip entirely (even for assists), use the `!!` force-ignore prefix - it replaces the deprecated `experimentalScannerIgnores`:\n\n```json\n{ \"files\": { \"includes\": [\"**\", \"!!**/legacy-vendor/**\"] } }\n```\n\n**`!**/dir` also matches ancestor directories.** The pattern is tested against the whole path, so if the project itself lives under a directory with that name - e.g. an agent worktree at `<repo>/.claude/worktrees/x/` with `\"!**/.claude\"` in its config - every file is excluded and Biome checks 0 files (2.5.15 reports `No files were processed`; older setups could pass vacuously). Root-anchor exclusions that refer to the project's own top-level folders (`\"!.claude\"`, `\"!dist\"`) and keep `**/` for names that genuinely recur at depth.\n\n### One command: `biome check`\n\n`biome check` runs formatter + linter + import organizer in one pass. Never split into separate `biome lint` and `biome format` in CI - use `biome check` (or `biome ci` for CI mode).\n\n```bash\nbiome check --write .            # apply safe fixes\nbiome check --write --unsafe .   # include unsafe fixes (review the diff)\n```\n\nRemoving unused imports/variables is classified **unsafe** (an external caller might reference the symbol), so plain `--write` reports but doesn't delete them - use `--write --unsafe` or remove by hand. Prefer `--write` over the `--fix` alias for consistency.\n\n### Pin versions, migrate after upgrades\n\n```bash\npnpm add --save-dev --save-exact @biomejs/biome@latest\npnpm biome migrate --write\n```\n\nThe `$schema` is version-pinned; after bumping the binary, the CLI errors with `The configuration schema version does not match the CLI version` until you run `biome migrate --write`. Do it as part of the upgrade.\n\n### `biome.json` at the project root\n\nOne config at the root; monorepo packages use `\"extends\": \"//\"` to inherit. Never reference it with a relative path like `\"../../biome.json\"`.\n\n## Quick start\n\n```bash\npnpm add --save-dev --save-exact @biomejs/biome\npnpm biome init\n```\n\n### Recommended config (React/TypeScript)\n\n```json\n{\n  \"$schema\": \"./node_modules/@biomejs/biome/configuration_schema.json\",\n  \"vcs\": { \"enabled\": true, \"clientKind\": \"git\", \"useIgnoreFile\": true },\n  \"files\": { \"includes\": [\"**\", \"!**/component"},{"path":"references/hono.md","content":"# Hono\n\nHono (Japanese for \"flame\") is a small, ultrafast web framework built entirely on Web\nStandards (`Request`/`Response`/`fetch`). One codebase runs on Cloudflare Workers, Deno,\nBun, Node.js, Vercel, Netlify, AWS Lambda, Lambda@Edge, and Fastly Compute. Zero\ndependencies; the `hono/tiny` preset is under 14kB. It is the backend/edge counterpart to\nthis stack's React frontend - its RPC client (`hc`) shares server types directly with\nReact, giving end-to-end type safety without code generation.\n\nVersion target: **hono@4.13.13** (Hono 4 is current; v5 is in development on a `v5` branch -\nESM-only, Node.js 22.12+ - with no npm release yet). On Node, use `@hono/node-server@2`, which\nrequires Node.js >= 20. Adapters/middleware are versioned independently (`@hono/node-server@2`,\n`@hono/zod-validator`, `@hono/zod-openapi@1`, and the runtime adapters `@hono/cloudflare-workers`,\n`@hono/bun`, `@hono/deno`).\n\n> **Keep Hono patched - it ships security fixes frequently.** Since 2026-08 alone: `serveStatic`\n> double-decoding the path, bypassing middleware on static routes (GHSA-5r4p-p66f-jhc7, fixed\n> 4.13.11; the same bug in `@hono/node-server` is GHSA-rmxm-3fg6-px4f, fixed **only** in 2.1.3 -\n> the 1.x line has no fix); `hono/jsx` boundary components rendering strings unescaped (XSS,\n> GHSA-hxh3-vqpv-xpqv, 4.13.7); unbounded `parseBody({ dot: true })` nesting (memory DoS,\n> GHSA-g6gw-c38x-mqfc, 4.13.5); query parsing past the URL fragment (GHSA-crvj-82cr-hjcx, 4.13.5);\n> `toSSG()` path escape (GHSA-gqvv-2mrq-wpjv, 4.13.5); `memo()` leaking SSR output across users\n> (GHSA-f23p-vx2j-j53r, 4.12.34); CORS ReDoS when `allowHeaders` is unset (GHSA-8j4g-w8fx-2239,\n> 4.12.34). Pin `hono >= 4.13.11` and `@hono/node-server >= 2.1.3`, and track the latest patch.\n\n> **For anything this file does not cover, fetch Hono's own LLM-optimized docs** - they are\n> the fastest authoritative source and are kept in sync with releases:\n> - Full docs (one file, ~360KB): https://hono.dev/llms-full.txt\n> - Core-only (smaller): https://hono.dev/llms-small.txt\n> - Index of all doc pages: https://hono.dev/llms.txt\n>\n> This reference is the curated 80% you need most often; the `llms-*.txt` files are the\n> exhaustive long tail (every middleware option, every runtime's getting-started, edge cases).\n\n```sh\nnpm create hono@latest my-app          # scaffold (prompts for a template)\nnpm create hono@latest my-app -- --template cloudflare-workers --pm pnpm --install\nnpm create hono@latest my-app -- --template cloudflare-workers+vite   # full-stack Workers + Vite (recommended)\nnpm i hono                              # add to an existing project\n```\n\n## Mental model\n\n- A handler returns a `Response` (or `c.text()`/`c.json()`/etc., which build one). Exactly\n  one handler runs per request.\n- Middleware is `async (c, next) => { ... await next() ... }`. Code before `next()` runs on\n  the way in; code after runs on the way out (onion model). Return a `Response` from\n  middleware to short-circuit. Ret"},{"path":"references/react.md","content":"# React 19\n\nPatterns for type-safe React 19.3 components (latest `19.3.0`). The headline shift from older React: **the React Compiler handles memoization**, `ref` is a normal prop, and `use()` reads context and promises without the old hook-placement rules. Write plain components and let the tooling optimize. For TypeScript specifics (props typing, generics, tsconfig) see [typescript.md](typescript.md).\n\n## Critical rules\n\n### `ref` is a prop - no `forwardRef`\n\n```tsx\n// React 19: ref is a regular prop\nfunction Input({ ref, ...props }: React.ComponentProps<\"input\"> & { ref?: React.Ref<HTMLInputElement> }) {\n  return <input ref={ref} {...props} />\n}\n```\n\n`React.ComponentProps<\"input\">` already includes `ref` in React 19's types, so for plain DOM-wrapping components you usually just destructure `ref` from props without declaring it.\n\n### No manual memoization\n\nThe compiler auto-memoizes return values, expensive computations, and callbacks. Drop `memo`, `useMemo`, `useCallback` from the common path:\n\n```tsx\n// Plain code - compiler memoizes sorting, the callback, and the JSX\nfunction List({ items, onSelect }: { items: Item[]; onSelect: (id: string) => void }) {\n  const sorted = items.toSorted(compare)\n  return sorted.map((item) => <Row key={item.id} onClick={() => onSelect(item.id)} />)\n}\n```\n\n### Extend native element props\n\n```tsx\ntype ButtonProps = React.ComponentProps<\"button\"> & { variant?: \"primary\" | \"ghost\" }\n```\n\n### `use()` over `useContext()`\n\n`use()` can read context after early returns and inside conditionals - `useContext` cannot. Pair it with a factory hook that throws on a missing provider so consumers never null-check:\n\n```tsx\nconst AuthContext = createContext<AuthState | null>(null)\n\nfunction useAuth(): AuthState {\n  const ctx = use(AuthContext)\n  if (ctx === null) throw new Error(\"useAuth must be used within AuthProvider\")\n  return ctx\n}\n```\n\n## React 19 patterns\n\n### Component authoring\n\nPlain functions with `data-slot` for styling hooks (the shadcn convention). No `forwardRef`, no `FC`:\n\n```tsx\nfunction Card({ className, ...props }: React.ComponentProps<\"div\">) {\n  return <div data-slot=\"card\" className={cn(\"rounded-xl border bg-card\", className)} {...props} />\n}\n```\n\n### Actions\n\nAsync transitions handle pending state, errors, and form resets. `useActionState` for forms:\n\n```tsx\nfunction UpdateProfile({ userId }: { userId: string }) {\n  const [error, submitAction, isPending] = useActionState(\n    async (_prev: string | null, formData: FormData) => {\n      const result = await updateProfile(userId, formData)\n      return result.error ?? null\n    },\n    null\n  )\n  return (\n    <form action={submitAction}>\n      <input name=\"displayName\" required />\n      <button type=\"submit\" disabled={isPending}>{isPending ? \"Saving...\" : \"Save\"}</button>\n      {error && <p className=\"text-destructive\">{error}</p>}\n    </form>\n  )\n}\n```\n\n`useTransition` for non-form Actions; `useOptimistic` for instant feedback:\n\n```tsx\nconst [isPending, startTr"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - components, styling, build and HMR, tests, lint/CI, or a Hono API with type-safe RPC. Skill: typescript-dev Owner: tenequm Summary: Full-stack TypeScript with Vite 8, React 19, Tailwind v4, shadcn/ui, Biome, Vitest, and Hono 4. Use when setting up or working in a TypeScript project - components, styling, build and HMR, tests, lint/CI, or a Hono API with type-safe RPC. Tags: latest:0.4.0 Version history: v0.4.0 | 2026-10-06T12:21:40.608Z | user Updated typescript-dev from 0.3.5 to 0.4.0. Changes: - mod","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1538,"uniquenessScore":48,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T05:02:37.432Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T05:02:37.432Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T10:42:19.395Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}