{"owner":"vercel-labs","repo":"agent-skills","hasSkills":true,"totalSkillsCount":213,"totalTokensCount":171264,"categories":["root-instruction","claude-rule","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","CLAUDE.md","skills/composition-patterns/AGENTS.md","skills/composition-patterns/README.md","skills/composition-patterns/SKILL.md","skills/composition-patterns/metadata.json","skills/composition-patterns/rules/_sections.md","skills/composition-patterns/rules/_template.md","skills/composition-patterns/rules/architecture-avoid-boolean-props.md","skills/composition-patterns/rules/architecture-compound-components.md","skills/composition-patterns/rules/patterns-children-over-render-props.md","skills/composition-patterns/rules/patterns-explicit-variants.md","skills/composition-patterns/rules/react19-no-forwardref.md","skills/composition-patterns/rules/state-context-interface.md","skills/composition-patterns/rules/state-decouple-implementation.md","skills/composition-patterns/rules/state-lift-state.md","skills/deploy-to-vercel/SKILL.md","skills/react-best-practices/AGENTS.md","skills/react-best-practices/README.md","skills/react-best-practices/SKILL.md","skills/react-best-practices/metadata.json","skills/react-best-practices/rules/_sections.md","skills/react-best-practices/rules/_template.md","skills/react-best-practices/rules/advanced-effect-event-deps.md","skills/react-best-practices/rules/advanced-event-handler-refs.md","skills/react-best-practices/rules/advanced-init-once.md","skills/react-best-practices/rules/advanced-use-latest.md","skills/react-best-practices/rules/async-api-routes.md","skills/react-best-practices/rules/async-cheap-condition-before-await.md","skills/react-best-practices/rules/async-defer-await.md","skills/react-best-practices/rules/async-dependencies.md","skills/react-best-practices/rules/async-parallel.md","skills/react-best-practices/rules/async-suspense-boundaries.md","skills/react-best-practices/rules/bundle-analyzable-paths.md","skills/react-best-practices/rules/bundle-barrel-imports.md","skills/react-best-practices/rules/bundle-conditional.md","skills/react-best-practices/rules/bundle-defer-third-party.md","skills/react-best-practices/rules/bundle-dynamic-imports.md","skills/react-best-practices/rules/bundle-preload.md","skills/react-best-practices/rules/client-event-listeners.md","skills/react-best-practices/rules/client-localstorage-schema.md","skills/react-best-practices/rules/client-passive-event-listeners.md","skills/react-best-practices/rules/client-swr-dedup.md","skills/react-best-practices/rules/js-batch-dom-css.md","skills/react-best-practices/rules/js-cache-function-results.md","skills/react-best-practices/rules/js-cache-property-access.md","skills/react-best-practices/rules/js-cache-storage.md","skills/react-best-practices/rules/js-combine-iterations.md","skills/react-best-practices/rules/js-early-exit.md","skills/react-best-practices/rules/js-flatmap-filter.md","skills/react-best-practices/rules/js-hoist-regexp.md","skills/react-best-practices/rules/js-index-maps.md","skills/react-best-practices/rules/js-length-check-first.md","skills/react-best-practices/rules/js-min-max-loop.md","skills/react-best-practices/rules/js-request-idle-callback.md","skills/react-best-practices/rules/js-set-map-lookups.md","skills/react-best-practices/rules/js-tosorted-immutable.md","skills/react-best-practices/rules/rendering-activity.md","skills/react-best-practices/rules/rendering-animate-svg-wrapper.md","skills/react-best-practices/rules/rendering-conditional-render.md","skills/react-best-practices/rules/rendering-content-visibility.md","skills/react-best-practices/rules/rendering-hoist-jsx.md","skills/react-best-practices/rules/rendering-hydration-no-flicker.md","skills/react-best-practices/rules/rendering-hydration-suppress-warning.md","skills/react-best-practices/rules/rendering-resource-hints.md","skills/react-best-practices/rules/rendering-script-defer-async.md","skills/react-best-practices/rules/rendering-svg-precision.md","skills/react-best-practices/rules/rendering-usetransition-loading.md","skills/react-best-practices/rules/rerender-defer-reads.md","skills/react-best-practices/rules/rerender-dependencies.md","skills/react-best-practices/rules/rerender-derived-state-no-effect.md","skills/react-best-practices/rules/rerender-derived-state.md","skills/react-best-practices/rules/rerender-functional-setstate.md","skills/react-best-practices/rules/rerender-lazy-state-init.md","skills/react-best-practices/rules/rerender-memo-with-default-value.md","skills/react-best-practices/rules/rerender-memo.md","skills/react-best-practices/rules/rerender-move-effect-to-event.md","skills/react-best-practices/rules/rerender-no-inline-components.md","skills/react-best-practices/rules/rerender-simple-expression-in-memo.md","skills/react-best-practices/rules/rerender-split-combined-hooks.md","skills/react-best-practices/rules/rerender-transitions.md","skills/react-best-practices/rules/rerender-use-deferred-value.md","skills/react-best-practices/rules/rerender-use-ref-transient-values.md","skills/react-best-practices/rules/server-after-nonblocking.md","skills/react-best-practices/rules/server-auth-actions.md","skills/react-best-practices/rules/server-cache-lru.md","skills/react-best-practices/rules/server-cache-react.md","skills/react-best-practices/rules/server-dedup-props.md","skills/react-best-practices/rules/server-hoist-static-io.md","skills/react-best-practices/rules/server-no-shared-module-state.md","skills/react-best-practices/rules/server-parallel-fetching.md","skills/react-best-practices/rules/server-parallel-nested-fetching.md","skills/react-best-practices/rules/server-serialization.md","skills/react-native-skills/AGENTS.md","skills/react-native-skills/README.md","skills/react-native-skills/SKILL.md","skills/react-native-skills/metadata.json","skills/react-native-skills/rules/_sections.md","skills/react-native-skills/rules/_template.md","skills/react-native-skills/rules/animation-derived-value.md","skills/react-native-skills/rules/animation-gesture-detector-press.md","skills/react-native-skills/rules/animation-gpu-properties.md","skills/react-native-skills/rules/design-system-compound-components.md","skills/react-native-skills/rules/fonts-config-plugin.md","skills/react-native-skills/rules/imports-design-system-folder.md","skills/react-native-skills/rules/js-hoist-intl.md","skills/react-native-skills/rules/list-performance-callbacks.md","skills/react-native-skills/rules/list-performance-function-references.md","skills/react-native-skills/rules/list-performance-images.md","skills/react-native-skills/rules/list-performance-inline-objects.md","skills/react-native-skills/rules/list-performance-item-expensive.md","skills/react-native-skills/rules/list-performance-item-memo.md","skills/react-native-skills/rules/list-performance-item-types.md","skills/react-native-skills/rules/list-performance-virtualize.md","skills/react-native-skills/rules/monorepo-native-deps-in-app.md","skills/react-native-skills/rules/monorepo-single-dependency-versions.md","skills/react-native-skills/rules/navigation-native-navigators.md","skills/react-native-skills/rules/react-compiler-destructure-functions.md","skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md","skills/react-native-skills/rules/react-state-dispatcher.md","skills/react-native-skills/rules/react-state-fallback.md","skills/react-native-skills/rules/react-state-minimize.md","skills/react-native-skills/rules/rendering-no-falsy-and.md","skills/react-native-skills/rules/rendering-text-in-text-component.md","skills/react-native-skills/rules/scroll-position-no-state.md","skills/react-native-skills/rules/state-ground-truth.md","skills/react-native-skills/rules/ui-expo-image.md","skills/react-native-skills/rules/ui-image-gallery.md","skills/react-native-skills/rules/ui-measure-views.md","skills/react-native-skills/rules/ui-menus.md","skills/react-native-skills/rules/ui-native-modals.md","skills/react-native-skills/rules/ui-pressable.md","skills/react-native-skills/rules/ui-safe-area-scroll.md","skills/react-native-skills/rules/ui-scrollview-content-inset.md","skills/react-native-skills/rules/ui-styling.md","skills/react-view-transitions/AGENTS.md","skills/react-view-transitions/README.md","skills/react-view-transitions/SKILL.md","skills/react-view-transitions/metadata.json","skills/react-view-transitions/references/css-recipes.md","skills/react-view-transitions/references/implementation.md","skills/react-view-transitions/references/nextjs.md","skills/react-view-transitions/references/patterns.md","skills/react-view-transitions/references/troubleshooting.md","skills/vercel-cli-with-tokens/SKILL.md","skills/vercel-optimize/AGENTS.md","skills/vercel-optimize/CONTRIBUTING.md","skills/vercel-optimize/README.md","skills/vercel-optimize/SKILL.md","skills/vercel-optimize/metadata.json","skills/vercel-optimize/references/candidates.md","skills/vercel-optimize/references/data-collection.md","skills/vercel-optimize/references/docs-library.json","skills/vercel-optimize/references/doctrine.md","skills/vercel-optimize/references/observability-plus.md","skills/vercel-optimize/references/playbooks/README.md","skills/vercel-optimize/references/playbooks/ai-application.md","skills/vercel-optimize/references/playbooks/api-service.md","skills/vercel-optimize/references/playbooks/content-site.md","skills/vercel-optimize/references/playbooks/ecommerce.md","skills/vercel-optimize/references/playbooks/marketing.md","skills/vercel-optimize/references/playbooks/saas.md","skills/vercel-optimize/references/playbooks/sveltekit.md","skills/vercel-optimize/references/recommendations.md","skills/vercel-optimize/references/scanner-patterns.md","skills/vercel-optimize/references/scoring.md","skills/vercel-optimize/references/support-topics/README.md","skills/vercel-optimize/references/support-topics/astro-edge-middleware-scope.md","skills/vercel-optimize/references/support-topics/astro-output-mode-and-isr.md","skills/vercel-optimize/references/support-topics/auth-preserving-parallelization.md","skills/vercel-optimize/references/support-topics/bot-protection-product-guardrails.md","skills/vercel-optimize/references/support-topics/build-minutes-monorepo-fanout.md","skills/vercel-optimize/references/support-topics/cache-components-static-shell-boundaries.md","skills/vercel-optimize/references/support-topics/cache-components-suspense-dedupe-pitfall.md","skills/vercel-optimize/references/support-topics/cdn-cache-auth-safety.md","skills/vercel-optimize/references/support-topics/cold-start-initialization-bundle.md","skills/vercel-optimize/references/support-topics/core-web-vitals-client-bottlenecks.md","skills/vercel-optimize/references/support-topics/database-egress-pooling-region.md","skills/vercel-optimize/references/support-topics/dynamic-rendering-traps.md","skills/vercel-optimize/references/support-topics/external-api-critical-path-platform.md","skills/vercel-optimize/references/support-topics/external-api-critical-path.md","skills/vercel-optimize/references/support-topics/fast-data-transfer-payloads.md","skills/vercel-optimize/references/support-topics/fluid-compute-caveats.md","skills/vercel-optimize/references/support-topics/function-duration-io-and-after.md","skills/vercel-optimize/references/support-topics/function-invocation-reduction.md","skills/vercel-optimize/references/support-topics/function-region-misconfiguration-ttfb.md","skills/vercel-optimize/references/support-topics/image-optimization-cost-control.md","skills/vercel-optimize/references/support-topics/isr-revalidation-static-generation.md","skills/vercel-optimize/references/support-topics/middleware-proxy-edge-cost.md","skills/vercel-optimize/references/support-topics/next-fetch-revalidate-floor.md","skills/vercel-optimize/references/support-topics/next-font-cls-self-hosting.md","skills/vercel-optimize/references/support-topics/next-heavy-ui-lazy-load-boundaries.md","skills/vercel-optimize/references/support-topics/next-image-lcp-preload-sizes.md","skills/vercel-optimize/references/support-topics/next-route-handler-get-cache-defaults.md","skills/vercel-optimize/references/support-topics/next-script-third-party-strategy.md","skills/vercel-optimize/references/support-topics/nextjs-version-cache-semantics.md","skills/vercel-optimize/references/support-topics/not-found-catchall-request-waste.md","skills/vercel-optimize/references/support-topics/nuxt-route-rules-cache-isr.md","skills/vercel-optimize/references/support-topics/observability-events-cost-attribution.md","skills/vercel-optimize/references/support-topics/post-response-work-waituntil.md","skills/vercel-optimize/references/support-topics/route-error-durable-offload.md","skills/vercel-optimize/references/support-topics/route-error-runtime-limits.md","skills/vercel-optimize/references/support-topics/runtime-cache-reusable-data.md","skills/vercel-optimize/references/support-topics/sveltekit-isr-prerender-safety.md","skills/vercel-optimize/references/support-topics/sveltekit-split-cold-start-tradeoff.md","skills/vercel-optimize/references/support-topics/usage-spike-triage.md","skills/vercel-optimize/references/support-topics/use-cache-date-stamp-isr-write-amplifier.md","skills/vercel-optimize/references/support-topics/use-cache-remote-shared-origin-data.md","skills/vercel-optimize/references/support-topics/workflow-resumable-stream-routes.md","skills/vercel-optimize/references/verification.md","skills/vercel-optimize/references/voice.md","skills/web-design-guidelines/SKILL.md","skills/writing-guidelines/SKILL.md"],"skills":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to AI coding agents (Claude Code, Cursor, Copilot, etc.) when working with code in this repository.\n\n## Repository Overview\n\nA collection of skills for AI coding agents working with Vercel projects. Skills are packaged instructions and scripts that extend agent capabilities.\n\n## Creating a New Skill\n\n### Directory Structure\n\n```\nskills/\n  {skill-name}/           # kebab-case directory name\n    SKILL.md              # Required: skill definition\n    scripts/              # Optional: executable scripts\n      {script-name}.sh    # Bash scripts\n      {script-name}.mjs   # Node scripts\n    references/           # Optional: supporting docs loaded on demand\n    lib/                  # Optional: shared code for scripts\n```\n\n### Naming Conventions\n\n- **Skill directory**: `kebab-case` (e.g., `vercel-deploy`, `log-monitor`)\n- **SKILL.md**: Always uppercase, always this exact filename\n- **Scripts**: `kebab-case.sh` or `kebab-case.mjs` (e.g., `deploy.sh`, `collect-signals.mjs`)\n\n### SKILL.md Format\n\n```markdown\n---\nname: {skill-name}\ndescription: {One sentence describing when to use this skill. Include trigger phrases like \"Deploy my app\", \"Check logs\", etc.}\n---\n\n# {Skill Title}\n\n{Brief description of what the skill does.}\n\n## How It Works\n\n{Numbered list explaining the skill's workflow}\n\n## Usage\n\n```bash\nbash /mnt/skills/user/{skill-name}/scripts/{script}.sh [args]\n```\n\n**Arguments:**\n- `arg1` - Description (defaults to X)\n\n**Examples:**\n{Show 2-3 common usage patterns}\n\n## Output\n\n{Show example output users will see}\n\n## Present Results to User\n\n{Template for how Claude should format results when presenting to users}\n\n## Troubleshooting\n\n{Common issues and solutions, especially network/permissions errors}\n```\n\n### Best Practices for Context Efficiency\n\nSkills are loaded on-demand — only the skill name and description are loaded at startup. The full `SKILL.md` loads into context only when the agent decides the skill is relevant. To minimize context usage:\n\n- **Keep SKILL.md under 500 lines** — put detailed reference material in separate files\n- **Write specific descriptions** — helps the agent know exactly when to activate the skill\n- **Use progressive disclosure** — reference supporting files that get read only when needed\n- **Prefer scripts over inline code** — script execution doesn't consume context (only output does)\n- **File references work one level deep** — link directly from SKILL.md to supporting files\n\n### Script Requirements\n\n- Bash scripts: use `#!/bin/bash` and `set -e`\n- Node scripts: use `#!/usr/bin/env node` and `.mjs`\n- Write status messages to stderr\n- Write machine-readable output (JSON) to stdout\n- Include a cleanup trap for temp files when scripts create them\n- Reference scripts by relative path, for example `node scripts/{script}.mjs`\n\n### End-User Installation\n\nDocument skills.sh installation for public skills:\n\n```bash\nnpx skills add vercel-labs/agent-skills --skill {skill-name}\n```\n\nFor manual installs, document the native path when the skill needs one.\n\n**Claude Code:**\n```bash\ncp -r skills/{skill-name} ~/.claude/skills/\n```\n\n**claude.ai:**\nAdd the skill to project knowledge or paste SKILL.md contents into the conversation.\n\nIf the skill requires network access, instruct users to add required domains at `claude.ai/settings/capabilities`.\n","CLAUDE.md":"AGENTS.md","skills/composition-patterns/AGENTS.md":"# React Composition Patterns\n\n**Version 1.0.0**  \nEngineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React codebases using composition. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n---\n\n## Table of Contents\n\n1. [Component Architecture](#1-component-architecture) — **HIGH**\n   - 1.1 [Avoid Boolean Prop Proliferation](#11-avoid-boolean-prop-proliferation)\n   - 1.2 [Use Compound Components](#12-use-compound-components)\n2. [State Management](#2-state-management) — **MEDIUM**\n   - 2.1 [Decouple State Management from UI](#21-decouple-state-management-from-ui)\n   - 2.2 [Define Generic Context Interfaces for Dependency Injection](#22-define-generic-context-interfaces-for-dependency-injection)\n   - 2.3 [Lift State into Provider Components](#23-lift-state-into-provider-components)\n3. [Implementation Patterns](#3-implementation-patterns) — **MEDIUM**\n   - 3.1 [Create Explicit Component Variants](#31-create-explicit-component-variants)\n   - 3.2 [Prefer Composing Children Over Render Props](#32-prefer-composing-children-over-render-props)\n4. [React 19 APIs](#4-react-19-apis) — **MEDIUM**\n   - 4.1 [React 19 API Changes](#41-react-19-api-changes)\n\n---\n\n## 1. Component Architecture\n\n**Impact: HIGH**\n\nFundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n### 1.1 Avoid Boolean Prop Proliferation\n\n**Impact: CRITICAL (prevents unmaintainable component variants)**\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\n\ncomponent behavior. Each boolean doubles possible states and creates\n\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect: boolean props create exponential complexity**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct: composition eliminates conditionals**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\n\nsharing a single monolithic parent.\n\n### 1.2 Use Compound Components\n\n**Impact: HIGH (enables flexible composition without prop drilling)**\n\nStructure complex components as compound components with a shared context. Each\n\nsubcomponent accesses shared state via context, not props. Consumers compose the\n\npieces they need.\n\n**Incorrect: monolithic component with render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct: compound components with shared context**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n\n---\n\n## 2. State Management\n\n**Impact: MEDIUM**\n\nPatterns for lifting state and managing shared context across\ncomposed components.\n\n### 2.1 Decouple State Management from UI\n\n**Impact: MEDIUM (enables swapping state implementations without changing UI)**\n\nThe provider component should be the only place that knows how state is managed.\n\nUI components consume the context interface—they don't know if state comes from\n\nuseState, Zustand, or a server sync.\n\n**Incorrect: UI coupled to state implementation**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct: state management isolated in provider**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\n\ndepends on the context interface, not the implementation.\n\n### 2.2 Define Generic Context Interfaces for Dependency Injection\n\n**Impact: HIGH (enables dependency-injectable state across use-cases)**\n\nDefine a **generic interface** for your component context with three parts:\n\n`state`, `actions`, and `meta`. This interface is a contract that any provider\n\ncan implement—enabling the same UI components to work with completely different\n\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\n\ndependency-injectable.\n\n**Incorrect: UI coupled to specific state implementation**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct: generic interface enables dependency injection**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe provider boundary is what matters—not the visual nesting. Components that\n\nneed shared state don't have to be inside the `Composer.Frame`. They just need\n\nto be within the provider.\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\n\nbox, but they can still access its state and actions. This is the power of\n\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\n\nby the provider. Swap the provider, keep the UI.\n\n### 2.3 Lift State into Provider Components\n\n**Impact: HIGH (enables state sharing outside component boundaries)**\n\nMove state management into dedicated provider components. This allows sibling\n\ncomponents outside the main UI to access and modify state without prop drilling\n\nor awkward refs.\n\n**Incorrect: state trapped inside component**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect: useEffect to sync state up**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect: reading state from ref on submit**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct: state lifted to provider**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\n\nsubmit action because it's within the provider. Even though it's a one-off\n\ncomponent, it can still access the composer's state and actions from outside the\n\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\n\nnested inside each other—they just need to be within the same provider.\n\n---\n\n## 3. Implementation Patterns\n\n**Impact: MEDIUM**\n\nSpecific techniques for implementing compound components and\ncontext providers.\n\n### 3.1 Create Explicit Component Variants\n\n**Impact: MEDIUM (self-documenting code, no hidden conditionals)**\n\nInstead of one component with many boolean props, create explicit variant\n\ncomponents. Each variant composes the pieces it needs. The code documents\n\nitself.\n\n**Incorrect: one component, many modes**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct: explicit variants**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\n\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n\n- What UI elements it includes\n\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n\n### 3.2 Prefer Composing Children Over Render Props\n\n**Impact: MEDIUM (cleaner composition, better readability)**\n\nUse `children` for composition instead of `renderX` props. Children are more\n\nreadable, compose naturally, and don't require understanding callback\n\nsignatures.\n\n**Incorrect: render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct: compound components with children**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\n\nUse children when composing static structure.\n\n---\n\n## 4. React 19 APIs\n\n**Impact: MEDIUM**\n\nReact 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n\n### 4.1 React 19 API Changes\n\n**Impact: MEDIUM (cleaner component definitions and context usage)**\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect: forwardRef in React 19**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct: ref as a regular prop**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect: useContext in React 19**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct: use instead of useContext**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n\n---\n\n## References\n\n1. [https://react.dev](https://react.dev)\n2. [https://react.dev/learn/passing-data-deeply-with-context](https://react.dev/learn/passing-data-deeply-with-context)\n3. [https://react.dev/reference/react/use](https://react.dev/reference/react/use)\n","skills/composition-patterns/README.md":"# React Composition Patterns\n\nA structured repository for React composition patterns that scale. These\npatterns help avoid boolean prop proliferation by using compound components,\nlifting state, and composing internals.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Rules\n\n### Component Architecture (CRITICAL)\n\n- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize\n  behavior\n- `architecture-compound-components.md` - Structure as compound components with\n  shared context\n\n### State Management (HIGH)\n\n- `state-lift-state.md` - Lift state into provider components\n- `state-context-interface.md` - Define clear context interfaces\n  (state/actions/meta)\n- `state-decouple-implementation.md` - Decouple state management from UI\n\n### Implementation Patterns (MEDIUM)\n\n- `patterns-children-over-render-props.md` - Prefer children over renderX props\n- `patterns-explicit-variants.md` - Create explicit component variants\n\n## Core Principles\n\n1. **Composition over configuration** — Instead of adding props, let consumers\n   compose\n2. **Lift your state** — State in providers, not trapped in components\n3. **Compose your internals** — Subcomponents access context, not props\n4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer\n   with isThread\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `architecture-` for Component Architecture\n   - `state-` for State Management\n   - `patterns-` for Implementation Patterns\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n\n## Impact Levels\n\n- `CRITICAL` - Foundational patterns, prevents unmaintainable code\n- `HIGH` - Significant maintainability improvements\n- `MEDIUM` - Good practices for cleaner code\n","skills/composition-patterns/SKILL.md":"---\nname: vercel-composition-patterns\ndescription:\n  React composition patterns that scale. Use when refactoring components with\n  boolean prop proliferation, building flexible component libraries, or\n  designing reusable APIs. Triggers on tasks involving compound components,\n  render props, context providers, or component architecture. Includes React 19\n  API changes.\nlicense: MIT\nmetadata:\n  author: vercel\n  version: '1.0.0'\n---\n\n# React Composition Patterns\n\nComposition patterns for building flexible, maintainable React components. Avoid\nboolean prop proliferation by using compound components, lifting state, and\ncomposing internals. These patterns make codebases easier for both humans and AI\nagents to work with as they scale.\n\n## When to Apply\n\nReference these guidelines when:\n\n- Refactoring components with many boolean props\n- Building reusable component libraries\n- Designing flexible component APIs\n- Reviewing component architecture\n- Working with compound components or context providers\n\n## Rule Categories by Priority\n\n| Priority | Category                | Impact | Prefix          |\n| -------- | ----------------------- | ------ | --------------- |\n| 1        | Component Architecture  | HIGH   | `architecture-` |\n| 2        | State Management        | MEDIUM | `state-`        |\n| 3        | Implementation Patterns | MEDIUM | `patterns-`     |\n| 4        | React 19 APIs           | MEDIUM | `react19-`      |\n\n## Quick Reference\n\n### 1. Component Architecture (HIGH)\n\n- `architecture-avoid-boolean-props` - Don't add boolean props to customize\n  behavior; use composition\n- `architecture-compound-components` - Structure complex components with shared\n  context\n\n### 2. State Management (MEDIUM)\n\n- `state-decouple-implementation` - Provider is the only place that knows how\n  state is managed\n- `state-context-interface` - Define generic interface with state, actions, meta\n  for dependency injection\n- `state-lift-state` - Move state into provider components for sibling access\n\n### 3. Implementation Patterns (MEDIUM)\n\n- `patterns-explicit-variants` - Create explicit variant components instead of\n  boolean modes\n- `patterns-children-over-render-props` - Use children for composition instead\n  of renderX props\n\n### 4. React 19 APIs (MEDIUM)\n\n> **⚠️ React 19+ only.** Skip this section if using React 18 or earlier.\n\n- `react19-no-forwardref` - Don't use `forwardRef`; use `use()` instead of `useContext()`\n\n## How to Use\n\nRead individual rule files for detailed explanations and code examples:\n\n```\nrules/architecture-avoid-boolean-props.md\nrules/state-context-interface.md\n```\n\nEach rule file contains:\n\n- Brief explanation of why it matters\n- Incorrect code example with explanation\n- Correct code example with explanation\n- Additional context and references\n\n## Full Compiled Document\n\nFor the complete guide with all rules expanded: `AGENTS.md`\n","skills/composition-patterns/metadata.json":"{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://react.dev/learn/passing-data-deeply-with-context\",\n    \"https://react.dev/reference/react/use\"\n  ]\n}\n","skills/composition-patterns/rules/_sections.md":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Component Architecture (architecture)\n\n**Impact:** HIGH  \n**Description:** Fundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n## 2. State Management (state)\n\n**Impact:** MEDIUM  \n**Description:** Patterns for lifting state and managing shared context across\ncomposed components.\n\n## 3. Implementation Patterns (patterns)\n\n**Impact:** MEDIUM  \n**Description:** Specific techniques for implementing compound components and\ncontext providers.\n\n## 4. React 19 APIs (react19)\n\n**Impact:** MEDIUM  \n**Description:** React 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n","skills/composition-patterns/rules/_template.md":"---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: brief description of impact\ntags: composition, components\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect:**\n\n```tsx\n// Bad code example\n```\n\n**Correct:**\n\n```tsx\n// Good code example\n```\n\nReference: [Link](https://example.com)\n","skills/composition-patterns/rules/architecture-avoid-boolean-props.md":"---\ntitle: Avoid Boolean Prop Proliferation\nimpact: CRITICAL\nimpactDescription: prevents unmaintainable component variants\ntags: composition, props, architecture\n---\n\n## Avoid Boolean Prop Proliferation\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\ncomponent behavior. Each boolean doubles possible states and creates\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect (boolean props create exponential complexity):**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct (composition eliminates conditionals):**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\nsharing a single monolithic parent.\n","skills/composition-patterns/rules/architecture-compound-components.md":"---\ntitle: Use Compound Components\nimpact: HIGH\nimpactDescription: enables flexible composition without prop drilling\ntags: composition, compound-components, architecture\n---\n\n## Use Compound Components\n\nStructure complex components as compound components with a shared context. Each\nsubcomponent accesses shared state via context, not props. Consumers compose the\npieces they need.\n\n**Incorrect (monolithic component with render props):**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct (compound components with shared context):**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n","skills/composition-patterns/rules/patterns-children-over-render-props.md":"---\ntitle: Prefer Composing Children Over Render Props\nimpact: MEDIUM\nimpactDescription: cleaner composition, better readability\ntags: composition, children, render-props\n---\n\n## Prefer Children Over Render Props\n\nUse `children` for composition instead of `renderX` props. Children are more\nreadable, compose naturally, and don't require understanding callback\nsignatures.\n\n**Incorrect (render props):**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct (compound components with children):**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\nUse children when composing static structure.\n","skills/composition-patterns/rules/patterns-explicit-variants.md":"---\ntitle: Create Explicit Component Variants\nimpact: MEDIUM\nimpactDescription: self-documenting code, no hidden conditionals\ntags: composition, variants, architecture\n---\n\n## Create Explicit Component Variants\n\nInstead of one component with many boolean props, create explicit variant\ncomponents. Each variant composes the pieces it needs. The code documents\nitself.\n\n**Incorrect (one component, many modes):**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct (explicit variants):**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n- What UI elements it includes\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n","skills/composition-patterns/rules/react19-no-forwardref.md":"---\ntitle: React 19 API Changes\nimpact: MEDIUM\nimpactDescription: cleaner component definitions and context usage\ntags: react19, refs, context, hooks\n---\n\n## React 19 API Changes\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect (forwardRef in React 19):**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct (ref as a regular prop):**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect (useContext in React 19):**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct (use instead of useContext):**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n","skills/composition-patterns/rules/state-context-interface.md":"---\ntitle: Define Generic Context Interfaces for Dependency Injection\nimpact: HIGH\nimpactDescription: enables dependency-injectable state across use-cases\ntags: composition, context, state, typescript, dependency-injection\n---\n\n## Define Generic Context Interfaces for Dependency Injection\n\nDefine a **generic interface** for your component context with three parts:\n`state`, `actions`, and `meta`. This interface is a contract that any provider\ncan implement—enabling the same UI components to work with completely different\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\ndependency-injectable.\n\n**Incorrect (UI coupled to specific state implementation):**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct (generic interface enables dependency injection):**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\nThe provider boundary is what matters—not the visual nesting. Components that\nneed shared state don't have to be inside the `Composer.Frame`. They just need\nto be within the provider.\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\nbox, but they can still access its state and actions. This is the power of\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\nby the provider. Swap the provider, keep the UI.\n","skills/composition-patterns/rules/state-decouple-implementation.md":"---\ntitle: Decouple State Management from UI\nimpact: MEDIUM\nimpactDescription: enables swapping state implementations without changing UI\ntags: composition, state, architecture\n---\n\n## Decouple State Management from UI\n\nThe provider component should be the only place that knows how state is managed.\nUI components consume the context interface—they don't know if state comes from\nuseState, Zustand, or a server sync.\n\n**Incorrect (UI coupled to state implementation):**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct (state management isolated in provider):**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\ndepends on the context interface, not the implementation.\n","skills/composition-patterns/rules/state-lift-state.md":"---\ntitle: Lift State into Provider Components\nimpact: HIGH\nimpactDescription: enables state sharing outside component boundaries\ntags: composition, state, context, providers\n---\n\n## Lift State into Provider Components\n\nMove state management into dedicated provider components. This allows sibling\ncomponents outside the main UI to access and modify state without prop drilling\nor awkward refs.\n\n**Incorrect (state trapped inside component):**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect (useEffect to sync state up):**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect (reading state from ref on submit):**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct (state lifted to provider):**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\nsubmit action because it's within the provider. Even though it's a one-off\ncomponent, it can still access the composer's state and actions from outside the\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\nnested inside each other—they just need to be within the same provider.\n","skills/deploy-to-vercel/SKILL.md":"---\nname: deploy-to-vercel\ndescription: Deploy applications and websites to Vercel. Use when the user requests deployment actions like \"deploy my app\", \"deploy and give me the link\", \"push this live\", or \"create a preview deployment\".\nmetadata:\n  author: vercel\n  version: \"3.0.0\"\n---\n\n# Deploy to Vercel\n\nDeploy any project to Vercel. **Always deploy as preview** (not production) unless the user explicitly asks for production.\n\nThe goal is to get the user into the best long-term setup: their project linked to Vercel with git-push deploys. Every method below tries to move the user closer to that state.\n\n## Step 1: Gather Project State\n\nRun all four checks before deciding which method to use:\n\n```bash\n# 1. Check for a git remote\ngit remote get-url origin 2>/dev/null\n\n# 2. Check if locally linked to a Vercel project (either file means linked)\ncat .vercel/project.json 2>/dev/null || cat .vercel/repo.json 2>/dev/null\n\n# 3. Check if the Vercel CLI is installed and authenticated\nvercel whoami 2>/dev/null\n\n# 4. List available teams (if authenticated)\nvercel teams list --format json 2>/dev/null\n```\n\n### Team selection\n\nIf the user belongs to multiple teams, present all available team slugs as a bulleted list and ask which one to deploy to. Once the user picks a team, proceed immediately to the next step — do not ask for additional confirmation.\n\nPass the team slug via `--scope` on all subsequent CLI commands (`vercel deploy`, `vercel link`, `vercel inspect`, etc.):\n\n```bash\nvercel deploy [path] -y --no-wait --scope <team-slug>\n```\n\nIf the project is already linked (`.vercel/project.json` or `.vercel/repo.json` exists), the `orgId` in those files determines the team — no need to ask again. If there is only one team (or just a personal account), skip the prompt and use it directly.\n\n**About the `.vercel/` directory:** A linked project has either:\n- `.vercel/project.json` — created by `vercel link` (single project linking). Contains `projectId` and `orgId`.\n- `.vercel/repo.json` — created by `vercel link --repo` (repo-based linking). Contains `orgId`, `remoteName`, and a `projects` array mapping directories to Vercel project IDs.\n\nEither file means the project is linked. Check for both.\n\n**Do NOT** use `vercel project inspect`, `vercel ls`, or `vercel link` to detect state in an unlinked directory — without a `.vercel/` config, they will interactively prompt (or with `--yes`, silently link as a side-effect). Only `vercel whoami` is safe to run anywhere.\n\n## Step 2: Choose a Deploy Method\n\n### Linked (`.vercel/` exists) + has git remote → Git Push\n\nThis is the ideal state. The project is linked and has git integration.\n\n1. **Ask the user before pushing.** Never push without explicit approval:\n   ```\n   This project is connected to Vercel via git. I can commit and push to\n   trigger a deployment. Want me to proceed?\n   ```\n\n2. **Commit and push:**\n   ```bash\n   git add .\n   git commit -m \"deploy: <description of changes>\"\n   git push\n   ```\n   Vercel automatically builds from the push. Non-production branches get preview deployments; the production branch (usually `main`) gets a production deployment.\n\n3. **Retrieve the preview URL.** If the CLI is authenticated:\n   ```bash\n   sleep 5\n   vercel ls --format json\n   ```\n   The JSON output has a `deployments` array. Find the latest entry — its `url` field is the preview URL.\n\n   If the CLI is not authenticated, tell the user to check the Vercel dashboard or the commit status checks on their git provider for the preview URL.\n\n---\n\n### Linked (`.vercel/` exists) + no git remote → `vercel deploy`\n\nThe project is linked but there's no git repo. Deploy directly with the CLI.\n\n```bash\nvercel deploy [path] -y --no-wait\n```\n\nUse `--no-wait` so the CLI returns immediately with the deployment URL instead of blocking until the build finishes (builds can take a while). Then check on the deployment status with:\n\n```bash\nvercel inspect <deployment-url>\n```\n\nFor production deploys (only if user explicitly asks):\n```bash\nvercel deploy [path] --prod -y --no-wait\n```\n\n---\n\n### Not linked + CLI is authenticated → Link first, then deploy\n\nThe CLI is working but the project isn't linked yet. This is the opportunity to get the user into the best state.\n\n1. **Ask the user which team to deploy to.** Present the team slugs from Step 1 as a bulleted list. If there's only one team (or just a personal account), skip this step.\n\n2. **Once a team is selected, proceed directly to linking.** Tell the user what will happen but do not ask for separate confirmation:\n   ```\n   Linking this project to <team name> on Vercel. This will create a Vercel\n   project to deploy to and enable automatic deployments on future git pushes.\n   ```\n\n3. **If a git remote exists**, use repo-based linking with the selected team scope:\n   ```bash\n   vercel link --repo --scope <team-slug>\n   ```\n   This reads the git remote URL and matches it to existing Vercel projects that deploy from that repo. It creates `.vercel/repo.json`. This is much more reliable than `vercel link` (without `--repo`), which tries to match by directory name and often fails when the local folder and Vercel project are named differently.\n\n   **If there is no git remote**, fall back to standard linking:\n   ```bash\n   vercel link --scope <team-slug>\n   ```\n   This prompts the user to select or create a project. It creates `.vercel/project.json`.\n\n4. **Then deploy using the best available method:**\n   - If a git remote exists → commit and push (see git push method above)\n   - If no git remote → `vercel deploy [path] -y --no-wait --scope <team-slug>`, then `vercel inspect <url>` to check status\n\n---\n\n### Not linked + CLI not authenticated → Install, auth, link, deploy\n\nThe Vercel CLI isn't set up at all.\n\n1. **Install the CLI (if not already installed):**\n   ```bash\n   npm install -g vercel\n   ```\n\n2. **Authenticate:**\n   ```bash\n   vercel login\n   ```\n   The user completes auth in their browser. If running in a non-interactive environment where login is not possible, skip to the **no-auth fallback** below.\n\n3. **Ask which team to deploy to** — present team slugs from `vercel teams list --format json` as a bulleted list. If only one team / personal account, skip. Once selected, proceed immediately.\n\n4. **Link the project** with the selected team scope (use `--repo` if a git remote exists, plain `vercel link` otherwise):\n   ```bash\n   vercel link --repo --scope <team-slug>   # if git remote exists\n   vercel link --scope <team-slug>          # if no git remote\n   ```\n\n5. **Deploy** using the best available method (git push if remote exists, otherwise `vercel deploy -y --no-wait --scope <team-slug>`, then `vercel inspect <url>` to check status).\n\n---\n\n### No-Auth Fallback — claude.ai sandbox\n\n**When to use:** Last resort when the CLI can't be installed or authenticated in the claude.ai sandbox. This requires no authentication — it returns a **Preview URL** (live site) and a **Claim URL** (transfer to your Vercel account).\n\n```bash\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh [path]\n```\n\n**Arguments:**\n- `path` - Directory to deploy, or a `.tgz` file (defaults to current directory)\n\n**Examples:**\n```bash\n# Deploy current directory\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh\n\n# Deploy specific project\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh /path/to/project\n\n# Deploy existing tarball\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh /path/to/project.tgz\n```\n\nThe script auto-detects the framework from `package.json`, packages the project (excluding `node_modules`, `.git`, `.env`), uploads it, and waits for the build to complete.\n\n**Tell the user:** \"Your deployment is ready at [previewUrl]. Claim it at [claimUrl] to manage your deployment.\"\n\n---\n\n### No-Auth Fallback — Codex sandbox\n\n**When to use:** In the Codex sandbox where the CLI may not be authenticated. Codex runs in a sandboxed environment by default — try the CLI first, and fall back to the deploy script if auth fails.\n\n1. **Check whether the Vercel CLI is installed** (no escalation needed for this check):\n   ```bash\n   command -v vercel\n   ```\n\n2. **If `vercel` is installed**, try deploying with the CLI:\n   ```bash\n   vercel deploy [path] -y --no-wait\n   ```\n\n3. **If `vercel` is not installed, or the CLI fails with \"No existing credentials found\"**, use the fallback script:\n   ```bash\n   skill_dir=\"<path-to-skill>\"\n\n   # Deploy current directory\n   bash \"$skill_dir/resources/deploy-codex.sh\"\n\n   # Deploy specific project\n   bash \"$skill_dir/resources/deploy-codex.sh\" /path/to/project\n\n   # Deploy existing tarball\n   bash \"$skill_dir/resources/deploy-codex.sh\" /path/to/project.tgz\n   ```\n\nThe script handles framework detection, packaging, and deployment. It waits for the build to complete and returns JSON with `previewUrl` and `claimUrl`.\n\n**Tell the user:** \"Your deployment is ready at [previewUrl]. Claim it at [claimUrl] to manage your deployment.\"\n\n**Escalated network access:** Only escalate the actual deploy command if sandboxing blocks the network call (`sandbox_permissions=require_escalated`). Do **not** escalate the `command -v vercel` check.\n\n---\n\n## Agent-Specific Notes\n\n### Claude Code / terminal-based agents\n\nYou have full shell access. Do NOT use the `/mnt/skills/` path. Follow the decision flow above using the CLI directly.\n\nFor the no-auth fallback, run the deploy script from the skill's installed location:\n```bash\nbash ~/.claude/skills/deploy-to-vercel/resources/deploy.sh [path]\n```\nThe path may vary depending on where the user installed the skill.\n\n### Sandboxed environments (claude.ai)\n\nYou likely cannot run `vercel login` or `git push`. Go directly to the **no-auth fallback — claude.ai sandbox**.\n\n### Codex\n\nCodex runs in a sandbox. Check if the CLI is available first, then fall back to the deploy script. Go to the **no-auth fallback — Codex sandbox**.\n\n---\n\n## Output\n\nAlways show the user the deployment URL.\n\n- **Git push:** Use `vercel ls --format json` to find the preview URL. If the CLI isn't authenticated, tell the user to check the Vercel dashboard or commit status checks.\n- **CLI deploy:** Show the URL returned by `vercel deploy --no-wait`. Use `vercel inspect <url>` to check build status and report it to the user.\n- **No-auth fallback:** Show both the preview URL and the claim URL:\n  ```\n  Deployment successful!\n\n  Preview URL: https://my-app-abc123.vercel.app\n  Claim URL:   https://vercel.com/claim-deployment?code=...\n\n  View your site at the Preview URL.\n  To transfer this deployment to your Vercel account, visit the Claim URL.\n  ```\n\n**Do not** curl or fetch the deployed URL to verify it works. Just return the link.\n\n---\n\n## Troubleshooting\n\n### Network Egress Error (claude.ai)\n\nIf deployment fails due to network restrictions on claude.ai, tell the user:\n\n```\nDeployment failed due to network restrictions. To fix this:\n\n1. Go to https://claude.ai/settings/capabilities\n2. Add *.vercel.com to the allowed domains\n3. Try deploying again\n```\n\n### Escalated Network Access (Codex / sandboxed environments)\n\nIf deployment fails due to network issues (timeouts, DNS errors, connection resets) in a sandboxed environment, rerun with escalated permissions (`sandbox_permissions=require_escalated`). Do not escalate the initial CLI availability check — only escalate the actual deploy command.\n\nExample guidance to the user:\n```\nThe deploy needs escalated network access to deploy to Vercel. I can rerun\nthe command with escalated permissions — want me to proceed?\n```\n\n### CLI Auth Failure\n\nIf `vercel login` or `vercel deploy` fails with authentication errors, fall back to the no-auth deploy script (claude.ai or Codex variant, depending on the environment).\n","skills/react-best-practices/AGENTS.md":"# React Best Practices\n\n**Version 1.0.0**  \nVercel Engineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React and Next.js codebases. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\n\n---\n\n## Table of Contents\n\n1. [Eliminating Waterfalls](#1-eliminating-waterfalls) — **CRITICAL**\n   - 1.1 [Check Cheap Conditions Before Async Flags](#11-check-cheap-conditions-before-async-flags)\n   - 1.2 [Defer Await Until Needed](#12-defer-await-until-needed)\n   - 1.3 [Dependency-Based Parallelization](#13-dependency-based-parallelization)\n   - 1.4 [Prevent Waterfall Chains in API Routes](#14-prevent-waterfall-chains-in-api-routes)\n   - 1.5 [Promise.all() for Independent Operations](#15-promiseall-for-independent-operations)\n   - 1.6 [Strategic Suspense Boundaries](#16-strategic-suspense-boundaries)\n2. [Bundle Size Optimization](#2-bundle-size-optimization) — **CRITICAL**\n   - 2.1 [Avoid Barrel File Imports](#21-avoid-barrel-file-imports)\n   - 2.2 [Conditional Module Loading](#22-conditional-module-loading)\n   - 2.3 [Defer Non-Critical Third-Party Libraries](#23-defer-non-critical-third-party-libraries)\n   - 2.4 [Dynamic Imports for Heavy Components](#24-dynamic-imports-for-heavy-components)\n   - 2.5 [Prefer Statically Analyzable Paths](#25-prefer-statically-analyzable-paths)\n   - 2.6 [Preload Based on User Intent](#26-preload-based-on-user-intent)\n3. [Server-Side Performance](#3-server-side-performance) — **HIGH**\n   - 3.1 [Authenticate Server Actions Like API Routes](#31-authenticate-server-actions-like-api-routes)\n   - 3.2 [Avoid Duplicate Serialization in RSC Props](#32-avoid-duplicate-serialization-in-rsc-props)\n   - 3.3 [Avoid Shared Module State for Request Data](#33-avoid-shared-module-state-for-request-data)\n   - 3.4 [Cross-Request LRU Caching](#34-cross-request-lru-caching)\n   - 3.5 [Hoist Static I/O to Module Level](#35-hoist-static-io-to-module-level)\n   - 3.6 [Minimize Serialization at RSC Boundaries](#36-minimize-serialization-at-rsc-boundaries)\n   - 3.7 [Parallel Data Fetching with Component Composition](#37-parallel-data-fetching-with-component-composition)\n   - 3.8 [Parallel Nested Data Fetching](#38-parallel-nested-data-fetching)\n   - 3.9 [Per-Request Deduplication with React.cache()](#39-per-request-deduplication-with-reactcache)\n   - 3.10 [Use after() for Non-Blocking Operations](#310-use-after-for-non-blocking-operations)\n4. [Client-Side Data Fetching](#4-client-side-data-fetching) — **MEDIUM-HIGH**\n   - 4.1 [Deduplicate Global Event Listeners](#41-deduplicate-global-event-listeners)\n   - 4.2 [Use Passive Event Listeners for Scrolling Performance](#42-use-passive-event-listeners-for-scrolling-performance)\n   - 4.3 [Use SWR for Automatic Deduplication](#43-use-swr-for-automatic-deduplication)\n   - 4.4 [Version and Minimize localStorage Data](#44-version-and-minimize-localstorage-data)\n5. [Re-render Optimization](#5-re-render-optimization) — **MEDIUM**\n   - 5.1 [Calculate Derived State During Rendering](#51-calculate-derived-state-during-rendering)\n   - 5.2 [Defer State Reads to Usage Point](#52-defer-state-reads-to-usage-point)\n   - 5.3 [Do not wrap a simple expression with a primitive result type in useMemo](#53-do-not-wrap-a-simple-expression-with-a-primitive-result-type-in-usememo)\n   - 5.4 [Don't Define Components Inside Components](#54-dont-define-components-inside-components)\n   - 5.5 [Extract Default Non-primitive Parameter Value from Memoized Component to Constant](#55-extract-default-non-primitive-parameter-value-from-memoized-component-to-constant)\n   - 5.6 [Extract to Memoized Components](#56-extract-to-memoized-components)\n   - 5.7 [Narrow Effect Dependencies](#57-narrow-effect-dependencies)\n   - 5.8 [Put Interaction Logic in Event Handlers](#58-put-interaction-logic-in-event-handlers)\n   - 5.9 [Split Combined Hook Computations](#59-split-combined-hook-computations)\n   - 5.10 [Subscribe to Derived State](#510-subscribe-to-derived-state)\n   - 5.11 [Use Functional setState Updates](#511-use-functional-setstate-updates)\n   - 5.12 [Use Lazy State Initialization](#512-use-lazy-state-initialization)\n   - 5.13 [Use Transitions for Non-Urgent Updates](#513-use-transitions-for-non-urgent-updates)\n   - 5.14 [Use useDeferredValue for Expensive Derived Renders](#514-use-usedeferredvalue-for-expensive-derived-renders)\n   - 5.15 [Use useRef for Transient Values](#515-use-useref-for-transient-values)\n6. [Rendering Performance](#6-rendering-performance) — **MEDIUM**\n   - 6.1 [Animate SVG Wrapper Instead of SVG Element](#61-animate-svg-wrapper-instead-of-svg-element)\n   - 6.2 [CSS content-visibility for Long Lists](#62-css-content-visibility-for-long-lists)\n   - 6.3 [Hoist Static JSX Elements](#63-hoist-static-jsx-elements)\n   - 6.4 [Optimize SVG Precision](#64-optimize-svg-precision)\n   - 6.5 [Prevent Hydration Mismatch Without Flickering](#65-prevent-hydration-mismatch-without-flickering)\n   - 6.6 [Suppress Expected Hydration Mismatches](#66-suppress-expected-hydration-mismatches)\n   - 6.7 [Use Activity Component for Show/Hide](#67-use-activity-component-for-showhide)\n   - 6.8 [Use defer or async on Script Tags](#68-use-defer-or-async-on-script-tags)\n   - 6.9 [Use Explicit Conditional Rendering](#69-use-explicit-conditional-rendering)\n   - 6.10 [Use React DOM Resource Hints](#610-use-react-dom-resource-hints)\n   - 6.11 [Use useTransition Over Manual Loading States](#611-use-usetransition-over-manual-loading-states)\n7. [JavaScript Performance](#7-javascript-performance) — **LOW-MEDIUM**\n   - 7.1 [Avoid Layout Thrashing](#71-avoid-layout-thrashing)\n   - 7.2 [Build Index Maps for Repeated Lookups](#72-build-index-maps-for-repeated-lookups)\n   - 7.3 [Cache Property Access in Loops](#73-cache-property-access-in-loops)\n   - 7.4 [Cache Repeated Function Calls](#74-cache-repeated-function-calls)\n   - 7.5 [Cache Storage API Calls](#75-cache-storage-api-calls)\n   - 7.6 [Combine Multiple Array Iterations](#76-combine-multiple-array-iterations)\n   - 7.7 [Defer Non-Critical Work with requestIdleCallback](#77-defer-non-critical-work-with-requestidlecallback)\n   - 7.8 [Early Length Check for Array Comparisons](#78-early-length-check-for-array-comparisons)\n   - 7.9 [Early Return from Functions](#79-early-return-from-functions)\n   - 7.10 [Hoist RegExp Creation](#710-hoist-regexp-creation)\n   - 7.11 [Use flatMap to Map and Filter in One Pass](#711-use-flatmap-to-map-and-filter-in-one-pass)\n   - 7.12 [Use Loop for Min/Max Instead of Sort](#712-use-loop-for-minmax-instead-of-sort)\n   - 7.13 [Use Set/Map for O(1) Lookups](#713-use-setmap-for-o1-lookups)\n   - 7.14 [Use toSorted() Instead of sort() for Immutability](#714-use-tosorted-instead-of-sort-for-immutability)\n8. [Advanced Patterns](#8-advanced-patterns) — **LOW**\n   - 8.1 [Do Not Put Effect Events in Dependency Arrays](#81-do-not-put-effect-events-in-dependency-arrays)\n   - 8.2 [Initialize App Once, Not Per Mount](#82-initialize-app-once-not-per-mount)\n   - 8.3 [Store Event Handlers in Refs](#83-store-event-handlers-in-refs)\n   - 8.4 [useEffectEvent for Stable Callback Refs](#84-useeffectevent-for-stable-callback-refs)\n\n---\n\n## 1. Eliminating Waterfalls\n\n**Impact: CRITICAL**\n\nWaterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.\n\n### 1.1 Check Cheap Conditions Before Async Flags\n\n**Impact: HIGH (avoids unnecessary async work when a synchronous guard already fails)**\n\nWhen a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n\nThis is a specialization of [Defer Await Until Needed](./async-defer-await.md) for `flag && cheapCondition` style checks.\n\n**Incorrect:**\n\n```typescript\nconst someFlag = await getFlag()\n\nif (someFlag && someCondition) {\n  // ...\n}\n```\n\n**Correct:**\n\n```typescript\nif (someCondition) {\n  const someFlag = await getFlag()\n  if (someFlag) {\n    // ...\n  }\n}\n```\n\nThis matters when `getFlag` hits the network, a feature-flag service, or `React.cache` / DB work: skipping it when `someCondition` is false removes that cost on the cold path.\n\nKeep the original order if `someCondition` is expensive, depends on the flag, or you must run side effects in a fixed order.\n\n### 1.2 Defer Await Until Needed\n\n**Impact: HIGH (avoids blocking unused code paths)**\n\nMove `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n\n**Incorrect: blocks both branches**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  const userData = await fetchUserData(userId)\n  \n  if (skipProcessing) {\n    // Returns immediately but still waited for userData\n    return { skipped: true }\n  }\n  \n  // Only this branch uses userData\n  return processUserData(userData)\n}\n```\n\n**Correct: only blocks when needed**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  if (skipProcessing) {\n    // Returns immediately without waiting\n    return { skipped: true }\n  }\n  \n  // Fetch only when needed\n  const userData = await fetchUserData(userId)\n  return processUserData(userData)\n}\n```\n\n**Another example: early return optimization**\n\n```typescript\n// Incorrect: always fetches permissions\nasync function updateResource(resourceId: string, userId: string) {\n  const permissions = await fetchPermissions(userId)\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n\n// Correct: fetches only when needed\nasync function updateResource(resourceId: string, userId: string) {\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  const permissions = await fetchPermissions(userId)\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n```\n\nThis optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.\n\nFor `await getFlag()` combined with a cheap synchronous guard (`flag && someCondition`), see [Check Cheap Conditions Before Async Flags](./async-cheap-condition-before-await.md).\n\n### 1.3 Dependency-Based Parallelization\n\n**Impact: CRITICAL (2-10× improvement)**\n\nFor operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n\n**Incorrect: profile waits for config unnecessarily**\n\n```typescript\nconst [user, config] = await Promise.all([\n  fetchUser(),\n  fetchConfig()\n])\nconst profile = await fetchProfile(user.id)\n```\n\n**Correct: config and profile run in parallel**\n\n```typescript\nimport { all } from 'better-all'\n\nconst { user, config, profile } = await all({\n  async user() { return fetchUser() },\n  async config() { return fetchConfig() },\n  async profile() {\n    return fetchProfile((await this.$.user).id)\n  }\n})\n```\n\n**Alternative without extra dependencies:**\n\n```typescript\nconst userPromise = fetchUser()\nconst profilePromise = userPromise.then(user => fetchProfile(user.id))\n\nconst [user, config, profile] = await Promise.all([\n  userPromise,\n  fetchConfig(),\n  profilePromise\n])\n```\n\nWe can also create all the promises first, and do `Promise.all()` at the end.\n\nReference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)\n\n### 1.4 Prevent Waterfall Chains in API Routes\n\n**Impact: CRITICAL (2-10× improvement)**\n\nIn API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n\n**Incorrect: config waits for auth, data waits for both**\n\n```typescript\nexport async function GET(request: Request) {\n  const session = await auth()\n  const config = await fetchConfig()\n  const data = await fetchData(session.user.id)\n  return Response.json({ data, config })\n}\n```\n\n**Correct: auth and config start immediately**\n\n```typescript\nexport async function GET(request: Request) {\n  const sessionPromise = auth()\n  const configPromise = fetchConfig()\n  const session = await sessionPromise\n  const [config, data] = await Promise.all([\n    configPromise,\n    fetchData(session.user.id)\n  ])\n  return Response.json({ data, config })\n}\n```\n\nFor operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).\n\n### 1.5 Promise.all() for Independent Operations\n\n**Impact: CRITICAL (2-10× improvement)**\n\nWhen async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n\n**Incorrect: sequential execution, 3 round trips**\n\n```typescript\nconst user = await fetchUser()\nconst posts = await fetchPosts()\nconst comments = await fetchComments()\n```\n\n**Correct: parallel execution, 1 round trip**\n\n```typescript\nconst [user, posts, comments] = await Promise.all([\n  fetchUser(),\n  fetchPosts(),\n  fetchComments()\n])\n```\n\n### 1.6 Strategic Suspense Boundaries\n\n**Impact: HIGH (faster initial paint)**\n\nInstead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n\n**Incorrect: wrapper blocked by data fetching**\n\n```tsx\nasync function Page() {\n  const data = await fetchData() // Blocks entire page\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <DataDisplay data={data} />\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n```\n\nThe entire layout waits for data even though only the middle section needs it.\n\n**Correct: wrapper shows immediately, data streams in**\n\n```tsx\nfunction Page() {\n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <Suspense fallback={<Skeleton />}>\n          <DataDisplay />\n        </Suspense>\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nasync function DataDisplay() {\n  const data = await fetchData() // Only blocks this component\n  return <div>{data.content}</div>\n}\n```\n\nSidebar, Header, and Footer render immediately. Only DataDisplay waits for data.\n\n**Alternative: share promise across components**\n\n```tsx\nfunction Page() {\n  // Start fetch immediately, but don't await\n  const dataPromise = fetchData()\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <Suspense fallback={<Skeleton />}>\n        <DataDisplay dataPromise={dataPromise} />\n        <DataSummary dataPromise={dataPromise} />\n      </Suspense>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nfunction DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Unwraps the promise\n  return <div>{data.content}</div>\n}\n\nfunction DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Reuses the same promise\n  return <div>{data.summary}</div>\n}\n```\n\nBoth components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.\n\n**When NOT to use this pattern:**\n\n- Critical data needed for layout decisions (affects positioning)\n\n- SEO-critical content above the fold\n\n- Small, fast queries where suspense overhead isn't worth it\n\n- When you want to avoid layout shift (loading → content jump)\n\n**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.\n\n---\n\n## 2. Bundle Size Optimization\n\n**Impact: CRITICAL**\n\nReducing initial bundle size improves Time to Interactive and Largest Contentful Paint.\n\n### 2.1 Avoid Barrel File Imports\n\n**Impact: CRITICAL (200-800ms import cost, slow builds)**\n\nImport directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n\nPopular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.\n\n**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.\n\n**Incorrect: imports entire library**\n\n```tsx\nimport { Check, X, Menu } from 'lucide-react'\n// Loads 1,583 modules, takes ~2.8s extra in dev\n// Runtime cost: 200-800ms on every cold start\n\nimport { Button, TextField } from '@mui/material'\n// Loads 2,225 modules, takes ~4.2s extra in dev\n```\n\n**Correct - Next.js 13.5+ (recommended):**\n\n```tsx\n// Keep the standard imports - Next.js transforms them to direct imports\nimport { Check, X, Menu } from 'lucide-react'\n// Full TypeScript support, no manual path wrangling\n```\n\nThis is the recommended approach because it preserves TypeScript type safety and editor autocompletion while still eliminating the barrel import cost.\n\n**Correct - Direct imports (non-Next.js projects):**\n\n```tsx\nimport Button from '@mui/material/Button'\nimport TextField from '@mui/material/TextField'\n// Loads only what you use\n```\n\n> **TypeScript warning:** Some libraries (notably `lucide-react`) don't ship `.d.ts` files for their deep import paths. Importing from `lucide-react/dist/esm/icons/check` resolves to an implicit `any` type, causing errors under `strict` or `noImplicitAny`. Prefer `optimizePackageImports` when available, or verify the library exports types for its subpaths before using direct imports.\n\nThese optimizations provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.\n\nLibraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.\n\nReference: [https://vercel.com/blog/how-we-optimized-package-imports-in-next-js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)\n\n### 2.2 Conditional Module Loading\n\n**Impact: HIGH (loads large data only when needed)**\n\nLoad large data or modules only when a feature is activated.\n\n**Example: lazy-load animation frames**\n\n```tsx\nfunction AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {\n  const [frames, setFrames] = useState<Frame[] | null>(null)\n\n  useEffect(() => {\n    if (enabled && !frames && typeof window !== 'undefined') {\n      import('./animation-frames.js')\n        .then(mod => setFrames(mod.frames))\n        .catch(() => setEnabled(false))\n    }\n  }, [enabled, frames, setEnabled])\n\n  if (!frames) return <Skeleton />\n  return <Canvas frames={frames} />\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.\n\n### 2.3 Defer Non-Critical Third-Party Libraries\n\n**Impact: MEDIUM (loads after hydration)**\n\nAnalytics, logging, and error tracking don't block user interaction. Load them after hydration.\n\n**Incorrect: blocks initial bundle**\n\n```tsx\nimport { Analytics } from '@vercel/analytics/react'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n**Correct: loads after hydration**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst Analytics = dynamic(\n  () => import('@vercel/analytics/react').then(m => m.Analytics),\n  { ssr: false }\n)\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n### 2.4 Dynamic Imports for Heavy Components\n\n**Impact: CRITICAL (directly affects TTI and LCP)**\n\nUse `next/dynamic` to lazy-load large components not needed on initial render.\n\n**Incorrect: Monaco bundles with main chunk ~300KB**\n\n```tsx\nimport { MonacoEditor } from './monaco-editor'\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n**Correct: Monaco loads on demand**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst MonacoEditor = dynamic(\n  () => import('./monaco-editor').then(m => m.MonacoEditor),\n  { ssr: false }\n)\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n### 2.5 Prefer Statically Analyzable Paths\n\n**Impact: HIGH (avoids accidental broad bundles and file traces)**\n\nBuild tools work best when import and file-system paths are obvious at build time. If you hide the real path inside a variable or compose it too dynamically, the tool either has to include a broad set of possible files, warn that it cannot analyze the import, or widen file tracing to stay safe.\n\nPrefer explicit maps or literal paths so the set of reachable files stays narrow and predictable. This is the same rule whether you are choosing modules with `import()` or reading files in server/build code.\n\nWhen analysis becomes too broad, the cost is real:\n\n- Larger server bundles\n\n- Slower builds\n\n- Worse cold starts\n\n- More memory use\n\n**Incorrect: the bundler cannot tell what may be imported**\n\n```ts\nconst PAGE_MODULES = {\n  home: './pages/home',\n  settings: './pages/settings',\n} as const\n\nconst Page = await import(PAGE_MODULES[pageName])\n```\n\n**Correct: use an explicit map of allowed modules**\n\n```ts\nconst PAGE_MODULES = {\n  home: () => import('./pages/home'),\n  settings: () => import('./pages/settings'),\n} as const\n\nconst Page = await PAGE_MODULES[pageName]()\n```\n\n**Incorrect: a 2-value enum still hides the final path from static analysis**\n\n```ts\nconst baseDir = path.join(process.cwd(), 'content/' + contentKind)\n```\n\n**Correct: make each final path literal at the callsite**\n\n```ts\nconst baseDir =\n  kind === ContentKind.Blog\n    ? path.join(process.cwd(), 'content/blog')\n    : path.join(process.cwd(), 'content/docs')\n```\n\nIn Next.js server code, this matters for output file tracing too. `path.join(process.cwd(), someVar)` can widen the traced file set because Next.js statically analyze `import`, `require`, and `fs` usage.\n\nReference: [https://nextjs.org/docs/app/api-reference/config/next-config-js/output](https://nextjs.org/docs/app/api-reference/config/next-config-js/output), [https://nextjs.org/learn/seo/dynamic-imports](https://nextjs.org/learn/seo/dynamic-imports), [https://vite.dev/guide/features.html](https://vite.dev/guide/features.html), [https://esbuild.github.io/api/](https://esbuild.github.io/api/), [https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars](https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars), [https://webpack.js.org/guides/dependency-management/](https://webpack.js.org/guides/dependency-management/)\n\n### 2.6 Preload Based on User Intent\n\n**Impact: MEDIUM (reduces perceived latency)**\n\nPreload heavy bundles before they're needed to reduce perceived latency.\n\n**Example: preload on hover/focus**\n\n```tsx\nfunction EditorButton({ onClick }: { onClick: () => void }) {\n  const preload = () => {\n    if (typeof window !== 'undefined') {\n      void import('./monaco-editor')\n    }\n  }\n\n  return (\n    <button\n      onMouseEnter={preload}\n      onFocus={preload}\n      onClick={onClick}\n    >\n      Open Editor\n    </button>\n  )\n}\n```\n\n**Example: preload when feature flag is enabled**\n\n```tsx\nfunction FlagsProvider({ children, flags }: Props) {\n  useEffect(() => {\n    if (flags.editorEnabled && typeof window !== 'undefined') {\n      void import('./monaco-editor').then(mod => mod.init())\n    }\n  }, [flags.editorEnabled])\n\n  return <FlagsContext.Provider value={flags}>\n    {children}\n  </FlagsContext.Provider>\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.\n\n---\n\n## 3. Server-Side Performance\n\n**Impact: HIGH**\n\nOptimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.\n\n### 3.1 Authenticate Server Actions Like API Routes\n\n**Impact: CRITICAL (prevents unauthorized access to server mutations)**\n\nServer Actions (functions with `\"use server\"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly.\n\nNext.js documentation explicitly states: \"Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation.\"\n\n**Incorrect: no authentication check**\n\n```typescript\n'use server'\n\nexport async function deleteUser(userId: string) {\n  // Anyone can call this! No auth check\n  await db.user.delete({ where: { id: userId } })\n  return { success: true }\n}\n```\n\n**Correct: authentication inside the action**\n\n```typescript\n'use server'\n\nimport { verifySession } from '@/lib/auth'\nimport { unauthorized } from '@/lib/errors'\n\nexport async function deleteUser(userId: string) {\n  // Always check auth inside the action\n  const session = await verifySession()\n  \n  if (!session) {\n    throw unauthorized('Must be logged in')\n  }\n  \n  // Check authorization too\n  if (session.user.role !== 'admin' && session.user.id !== userId) {\n    throw unauthorized('Cannot delete other users')\n  }\n  \n  await db.user.delete({ where: { id: userId } })\n  return { success: true }\n}\n```\n\n**With input validation:**\n\n```typescript\n'use server'\n\nimport { verifySession } from '@/lib/auth'\nimport { z } from 'zod'\n\nconst updateProfileSchema = z.object({\n  userId: z.string().uuid(),\n  name: z.string().min(1).max(100),\n  email: z.string().email()\n})\n\nexport async function updateProfile(data: unknown) {\n  // Validate input first\n  const validated = updateProfileSchema.parse(data)\n  \n  // Then authenticate\n  const session = await verifySession()\n  if (!session) {\n    throw new Error('Unauthorized')\n  }\n  \n  // Then authorize\n  if (session.user.id !== validated.userId) {\n    throw new Error('Can only update own profile')\n  }\n  \n  // Finally perform the mutation\n  await db.user.update({\n    where: { id: validated.userId },\n    data: {\n      name: validated.name,\n      email: validated.email\n    }\n  })\n  \n  return { success: true }\n}\n```\n\nReference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)\n\n### 3.2 Avoid Duplicate Serialization in RSC Props\n\n**Impact: LOW (reduces network payload by avoiding duplicate serialization)**\n\nRSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again. Do transformations (`.toSorted()`, `.filter()`, `.map()`) in client, not server.\n\n**Incorrect: duplicates array**\n\n```tsx\n// RSC: sends 6 strings (2 arrays × 3 items)\n<ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />\n```\n\n**Correct: sends 3 strings**\n\n```tsx\n// RSC: send once\n<ClientList usernames={usernames} />\n\n// Client: transform there\n'use client'\nconst sorted = useMemo(() => [...usernames].sort(), [usernames])\n```\n\n**Nested deduplication behavior:**\n\n```tsx\n// string[] - duplicates everything\nusernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings\n\n// object[] - duplicates array structure only\nusers={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4)\n```\n\nDeduplication works recursively. Impact varies by data type:\n\n- `string[]`, `number[]`, `boolean[]`: **HIGH impact** - array + all primitives fully duplicated\n\n- `object[]`: **LOW impact** - array duplicated, but nested objects deduplicated by reference\n\n**Operations breaking deduplication: create new references**\n\n- Arrays: `.toSorted()`, `.filter()`, `.map()`, `.slice()`, `[...arr]`\n\n- Objects: `{...obj}`, `Object.assign()`, `structuredClone()`, `JSON.parse(JSON.stringify())`\n\n**More examples:**\n\n```tsx\n// ❌ Bad\n<C users={users} active={users.filter(u => u.active)} />\n<C product={product} productName={product.name} />\n\n// ✅ Good\n<C users={users} />\n<C product={product} />\n// Do filtering/destructuring in client\n```\n\n**Exception:** Pass derived data when transformation is expensive or client doesn't need original.\n\n### 3.3 Avoid Shared Module State for Request Data\n\n**Impact: HIGH (prevents concurrency bugs and request data leaks)**\n\nFor React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race conditions, cross-request contamination, and security bugs where one user's data appears in another user's response.\n\nTreat module scope on the server as process-wide shared memory, not request-local state.\n\n**Incorrect: request data leaks across concurrent renders**\n\n```tsx\nlet currentUser: User | null = null\n\nexport default async function Page() {\n  currentUser = await auth()\n  return <Dashboard />\n}\n\nasync function Dashboard() {\n  return <div>{currentUser?.name}</div>\n}\n```\n\nIf two requests overlap, request A can set `currentUser`, then request B overwrites it before request A finishes rendering `Dashboard`.\n\n**Correct: keep request data local to the render tree**\n\n```tsx\nexport default async function Page() {\n  const user = await auth()\n  return <Dashboard user={user} />\n}\n\nfunction Dashboard({ user }: { user: User | null }) {\n  return <div>{user?.name}</div>\n}\n```\n\nSafe exceptions:\n\n- Immutable static assets or config loaded once at module scope\n\n- Shared caches intentionally designed for cross-request reuse and keyed correctly\n\n- Process-wide singletons that do not store request- or user-specific mutable data\n\nFor static assets and config, see [Hoist Static I/O to Module Level](./server-hoist-static-io.md).\n\n### 3.4 Cross-Request LRU Caching\n\n**Impact: HIGH (caches across requests)**\n\n`React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.\n\n**Implementation:**\n\n```typescript\nimport { LRUCache } from 'lru-cache'\n\nconst cache = new LRUCache<string, any>({\n  max: 1000,\n  ttl: 5 * 60 * 1000  // 5 minutes\n})\n\nexport async function getUser(id: string) {\n  const cached = cache.get(id)\n  if (cached) return cached\n\n  const user = await db.user.findUnique({ where: { id } })\n  cache.set(id, user)\n  return user\n}\n\n// Request 1: DB query, result cached\n// Request 2: cache hit, no DB query\n```\n\nUse when sequential user actions hit multiple endpoints needing the same data within seconds.\n\n**With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis.\n\n**In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching.\n\nReference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)\n\n### 3.5 Hoist Static I/O to Module Level\n\n**Impact: HIGH (avoids repeated file/network I/O per request)**\n\nWhen loading static assets (fonts, logos, images, config files) in route handlers or server functions, hoist the I/O operation to module level. Module-level code runs once when the module is first imported, not on every request. This eliminates redundant file system reads or network fetches that would otherwise run on every invocation.\n\n**Incorrect: reads font file on every request**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\n\nexport async function GET(request: Request) {\n  // Runs on EVERY request - expensive!\n  const fontData = await fetch(\n    new URL('./fonts/Inter.ttf', import.meta.url)\n  ).then(res => res.arrayBuffer())\n\n  const logoData = await fetch(\n    new URL('./images/logo.png', import.meta.url)\n  ).then(res => res.arrayBuffer())\n\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logoData} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: fontData }] }\n  )\n}\n```\n\n**Correct: loads once at module initialization**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\n\n// Module-level: runs ONCE when module is first imported\nconst fontData = fetch(\n  new URL('./fonts/Inter.ttf', import.meta.url)\n).then(res => res.arrayBuffer())\n\nconst logoData = fetch(\n  new URL('./images/logo.png', import.meta.url)\n).then(res => res.arrayBuffer())\n\nexport async function GET(request: Request) {\n  // Await the already-started promises\n  const [font, logo] = await Promise.all([fontData, logoData])\n\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logo} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: font }] }\n  )\n}\n```\n\n**Correct: synchronous fs at module level**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\nimport { readFileSync } from 'fs'\nimport { join } from 'path'\n\n// Synchronous read at module level - blocks only during module init\nconst fontData = readFileSync(\n  join(process.cwd(), 'public/fonts/Inter.ttf')\n)\n\nconst logoData = readFileSync(\n  join(process.cwd(), 'public/images/logo.png')\n)\n\nexport async function GET(request: Request) {\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logoData} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: fontData }] }\n  )\n}\n```\n\n**Incorrect: reads config on every call**\n\n```typescript\nimport fs from 'node:fs/promises'\n\nexport async function processRequest(data: Data) {\n  const config = JSON.parse(\n    await fs.readFile('./config.json', 'utf-8')\n  )\n  const template = await fs.readFile('./template.html', 'utf-8')\n\n  return render(template, data, config)\n}\n```\n\n**Correct: hoists config and template to module level**\n\n```typescript\nimport fs from 'node:fs/promises'\n\nconst configPromise = fs\n  .readFile('./config.json', 'utf-8')\n  .then(JSON.parse)\nconst templatePromise = fs.readFile('./template.html', 'utf-8')\n\nexport async function processRequest(data: Data) {\n  const [config, template] = await Promise.all([\n    configPromise,\n    templatePromise,\n  ])\n\n  return render(template, data, config)\n}\n```\n\nWhen to use this pattern:\n\n- Loading fonts for OG image generation\n\n- Loading static logos, icons, or watermarks\n\n- Reading configuration files that don't change at runtime\n\n- Loading email templates or other static templates\n\n- Any static asset that's the same across all requests\n\nWhen not to use this pattern:\n\n- Assets that vary per request or user\n\n- Files that may change during runtime (use caching with TTL instead)\n\n- Large files that would consume too much memory if kept loaded\n\n- Sensitive data that shouldn't persist in memory\n\nWith Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute), module-level caching is especially effective because multiple concurrent requests share the same function instance. The static assets stay loaded in memory across requests without cold start penalties.\n\nIn traditional serverless, each cold start re-executes module-level code, but subsequent warm invocations reuse the loaded assets until the instance is recycled.\n\n### 3.6 Minimize Serialization at RSC Boundaries\n\n**Impact: HIGH (reduces data transfer size)**\n\nThe React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.\n\n**Incorrect: serializes all 50 fields**\n\n```tsx\nasync function Page() {\n  const user = await fetchUser()  // 50 fields\n  return <Profile user={user} />\n}\n\n'use client'\nfunction Profile({ user }: { user: User }) {\n  return <div>{user.name}</div>  // uses 1 field\n}\n```\n\n**Correct: serializes only 1 field**\n\n```tsx\nasync function Page() {\n  const user = await fetchUser()\n  return <Profile name={user.name} />\n}\n\n'use client'\nfunction Profile({ name }: { name: string }) {\n  return <div>{name}</div>\n}\n```\n\n### 3.7 Parallel Data Fetching with Component Composition\n\n**Impact: CRITICAL (eliminates server-side waterfalls)**\n\nReact Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.\n\n**Incorrect: Sidebar waits for Page's fetch to complete**\n\n```tsx\nexport default async function Page() {\n  const header = await fetchHeader()\n  return (\n    <div>\n      <div>{header}</div>\n      <Sidebar />\n    </div>\n  )\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n```\n\n**Correct: both fetch simultaneously**\n\n```tsx\nasync function Header() {\n  const data = await fetchHeader()\n  return <div>{data}</div>\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n\nexport default function Page() {\n  return (\n    <div>\n      <Header />\n      <Sidebar />\n    </div>\n  )\n}\n```\n\n**Alternative with children prop:**\n\n```tsx\nasync function Header() {\n  const data = await fetchHeader()\n  return <div>{data}</div>\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n\nfunction Layout({ children }: { children: ReactNode }) {\n  return (\n    <div>\n      <Header />\n      {children}\n    </div>\n  )\n}\n\nexport default function Page() {\n  return (\n    <Layout>\n      <Sidebar />\n    </Layout>\n  )\n}\n```\n\n### 3.8 Parallel Nested Data Fetching\n\n**Impact: CRITICAL (eliminates server-side waterfalls)**\n\nWhen fetching nested data in parallel, chain dependent fetches within each item's promise so a slow item doesn't block the rest.\n\n**Incorrect: a single slow item blocks all nested fetches**\n\n```tsx\nconst chats = await Promise.all(\n  chatIds.map(id => getChat(id))\n)\n\nconst chatAuthors = await Promise.all(\n  chats.map(chat => getUser(chat.author))\n)\n```\n\nIf one `getChat(id)` out of 100 is extremely slow, the authors of the other 99 chats can't start loading even though their data is ready.\n\n**Correct: each item chains its own nested fetch**\n\n```tsx\nconst chatAuthors = await Promise.all(\n  chatIds.map(id => getChat(id).then(chat => getUser(chat.author)))\n)\n```\n\nEach item independently chains `getChat` → `getUser`, so a slow chat doesn't block author fetches for the others.\n\n### 3.9 Per-Request Deduplication with React.cache()\n\n**Impact: MEDIUM (deduplicates within request)**\n\nUse `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.\n\n**Usage:**\n\n```typescript\nimport { cache } from 'react'\n\nexport const getCurrentUser = cache(async () => {\n  const session = await auth()\n  if (!session?.user?.id) return null\n  return await db.user.findUnique({\n    where: { id: session.user.id }\n  })\n})\n```\n\nWithin a single request, multiple calls to `getCurrentUser()` execute the query only once.\n\n**Avoid inline objects as arguments:**\n\n`React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits.\n\n**Incorrect: always cache miss**\n\n```typescript\nconst getUser = cache(async (params: { uid: number }) => {\n  return await db.user.findUnique({ where: { id: params.uid } })\n})\n\n// Each call creates new object, never hits cache\ngetUser({ uid: 1 })\ngetUser({ uid: 1 })  // Cache miss, runs query again\n```\n\n**Correct: cache hit**\n\n```typescript\nconst params = { uid: 1 }\ngetUser(params)  // Query runs\ngetUser(params)  // Cache hit (same reference)\n```\n\nIf you must pass objects, pass the same reference:\n\n**Next.js-Specific Note:**\n\nIn Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks:\n\n- Database queries (Prisma, Drizzle, etc.)\n\n- Heavy computations\n\n- Authentication checks\n\n- File system operations\n\n- Any non-fetch async work\n\nUse `React.cache()` to deduplicate these operations across your component tree.\n\nReference: [https://react.dev/reference/react/cache](https://react.dev/reference/react/cache)\n\n### 3.10 Use after() for Non-Blocking Operations\n\n**Impact: MEDIUM (faster response times)**\n\nUse Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.\n\n**Incorrect: blocks response**\n\n```tsx\nimport { logUserAction } from '@/app/utils'\n\nexport async function POST(request: Request) {\n  // Perform mutation\n  await updateDatabase(request)\n  \n  // Logging blocks the response\n  const userAgent = request.headers.get('user-agent') || 'unknown'\n  await logUserAction({ userAgent })\n  \n  return new Response(JSON.stringify({ status: 'success' }), {\n    status: 200,\n    headers: { 'Content-Type': 'application/json' }\n  })\n}\n```\n\n**Correct: non-blocking**\n\n```tsx\nimport { after } from 'next/server'\nimport { headers, cookies } from 'next/headers'\nimport { logUserAction } from '@/app/utils'\n\nexport async function POST(request: Request) {\n  // Perform mutation\n  await updateDatabase(request)\n  \n  // Log after response is sent\n  after(async () => {\n    const userAgent = (await headers()).get('user-agent') || 'unknown'\n    const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous'\n    \n    logUserAction({ sessionCookie, userAgent })\n  })\n  \n  return new Response(JSON.stringify({ status: 'success' }), {\n    status: 200,\n    headers: { 'Content-Type': 'application/json' }\n  })\n}\n```\n\nThe response is sent immediately while logging happens in the background.\n\n**Common use cases:**\n\n- Analytics tracking\n\n- Audit logging\n\n- Sending notifications\n\n- Cache invalidation\n\n- Cleanup tasks\n\n**Important notes:**\n\n- `after()` runs even if the response fails or redirects\n\n- Works in Server Actions, Route Handlers, and Server Components\n\nReference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)\n\n---\n\n## 4. Client-Side Data Fetching\n\n**Impact: MEDIUM-HIGH**\n\nAutomatic deduplication and efficient data fetching patterns reduce redundant network requests.\n\n### 4.1 Deduplicate Global Event Listeners\n\n**Impact: LOW (single listener for N components)**\n\nUse `useSWRSubscription()` to share global event listeners across component instances.\n\n**Incorrect: N instances = N listeners**\n\n```tsx\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  useEffect(() => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && e.key === key) {\n        callback()\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  }, [key, callback])\n}\n```\n\nWhen using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.\n\n**Correct: N instances = 1 listener**\n\n```tsx\nimport useSWRSubscription from 'swr/subscription'\n\n// Module-level Map to track callbacks per key\nconst keyCallbacks = new Map<string, Set<() => void>>()\n\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  // Register this callback in the Map\n  useEffect(() => {\n    if (!keyCallbacks.has(key)) {\n      keyCallbacks.set(key, new Set())\n    }\n    keyCallbacks.get(key)!.add(callback)\n\n    return () => {\n      const set = keyCallbacks.get(key)\n      if (set) {\n        set.delete(callback)\n        if (set.size === 0) {\n          keyCallbacks.delete(key)\n        }\n      }\n    }\n  }, [key, callback])\n\n  useSWRSubscription('global-keydown', () => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && keyCallbacks.has(e.key)) {\n        keyCallbacks.get(e.key)!.forEach(cb => cb())\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  })\n}\n\nfunction Profile() {\n  // Multiple shortcuts will share the same listener\n  useKeyboardShortcut('p', () => { /* ... */ }) \n  useKeyboardShortcut('k', () => { /* ... */ })\n  // ...\n}\n```\n\n### 4.2 Use Passive Event Listeners for Scrolling Performance\n\n**Impact: MEDIUM (eliminates scroll delay caused by event listeners)**\n\nAdd `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n\n**Incorrect:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch)\n  document.addEventListener('wheel', handleWheel)\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Correct:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch, { passive: true })\n  document.addEventListener('wheel', handleWheel, { passive: true })\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.\n\n**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.\n\n### 4.3 Use SWR for Automatic Deduplication\n\n**Impact: MEDIUM-HIGH (automatic deduplication)**\n\nSWR enables request deduplication, caching, and revalidation across component instances.\n\n**Incorrect: no deduplication, each instance fetches**\n\n```tsx\nfunction UserList() {\n  const [users, setUsers] = useState([])\n  useEffect(() => {\n    fetch('/api/users')\n      .then(r => r.json())\n      .then(setUsers)\n  }, [])\n}\n```\n\n**Correct: multiple instances share one request**\n\n```tsx\nimport useSWR from 'swr'\n\nfunction UserList() {\n  const { data: users } = useSWR('/api/users', fetcher)\n}\n```\n\n**For immutable data:**\n\n```tsx\nimport { useImmutableSWR } from '@/lib/swr'\n\nfunction StaticContent() {\n  const { data } = useImmutableSWR('/api/config', fetcher)\n}\n```\n\n**For mutations:**\n\n```tsx\nimport { useSWRMutation } from 'swr/mutation'\n\nfunction UpdateButton() {\n  const { trigger } = useSWRMutation('/api/user', updateUser)\n  return <button onClick={() => trigger()}>Update</button>\n}\n```\n\nReference: [https://swr.vercel.app](https://swr.vercel.app)\n\n### 4.4 Version and Minimize localStorage Data\n\n**Impact: MEDIUM (prevents schema conflicts, reduces storage size)**\n\nAdd version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n\n**Incorrect:**\n\n```typescript\n// No version, stores everything, no error handling\nlocalStorage.setItem('userConfig', JSON.stringify(fullUserObject))\nconst data = localStorage.getItem('userConfig')\n```\n\n**Correct:**\n\n```typescript\nconst VERSION = 'v2'\n\nfunction saveConfig(config: { theme: string; language: string }) {\n  try {\n    localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))\n  } catch {\n    // Throws in incognito/private browsing, quota exceeded, or disabled\n  }\n}\n\nfunction loadConfig() {\n  try {\n    const data = localStorage.getItem(`userConfig:${VERSION}`)\n    return data ? JSON.parse(data) : null\n  } catch {\n    return null\n  }\n}\n\n// Migration from v1 to v2\nfunction migrate() {\n  try {\n    const v1 = localStorage.getItem('userConfig:v1')\n    if (v1) {\n      const old = JSON.parse(v1)\n      saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })\n      localStorage.removeItem('userConfig:v1')\n    }\n  } catch {}\n}\n```\n\n**Store minimal fields from server responses:**\n\n```typescript\n// User object has 20+ fields, only store what UI needs\nfunction cachePrefs(user: FullUser) {\n  try {\n    localStorage.setItem('prefs:v1', JSON.stringify({\n      theme: user.preferences.theme,\n      notifications: user.preferences.notifications\n    }))\n  } catch {}\n}\n```\n\n**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.\n\n**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.\n\n---\n\n## 5. Re-render Optimization\n\n**Impact: MEDIUM**\n\nReducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.\n\n### 5.1 Calculate Derived State During Rendering\n\n**Impact: MEDIUM (avoids redundant renders and state drift)**\n\nIf a value can be computed from current props/state, do\n\n<!-- [TRUNCATED AT 50KB] -->","skills/react-best-practices/README.md":"# React Best Practices\n\nA structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `src/` - Build scripts and utilities\n- `metadata.json` - Document metadata (version, organization, abstract)\n- __`AGENTS.md`__ - Compiled output (generated)\n- __`test-cases.json`__ - Test cases for LLM evaluation (generated)\n\n## Getting Started\n\n1. Install dependencies:\n   ```bash\n   pnpm install\n   ```\n\n2. Build AGENTS.md from rules:\n   ```bash\n   pnpm build\n   ```\n\n3. Validate rule files:\n   ```bash\n   pnpm validate\n   ```\n\n4. Extract test cases:\n   ```bash\n   pnpm extract-tests\n   ```\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `async-` for Eliminating Waterfalls (Section 1)\n   - `bundle-` for Bundle Size Optimization (Section 2)\n   - `server-` for Server-Side Performance (Section 3)\n   - `client-` for Client-Side Data Fetching (Section 4)\n   - `rerender-` for Re-render Optimization (Section 5)\n   - `rendering-` for Rendering Performance (Section 6)\n   - `js-` for JavaScript Performance (Section 7)\n   - `advanced-` for Advanced Patterns (Section 8)\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json\n\n## Rule File Structure\n\nEach rule file should follow this structure:\n\n```markdown\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description\ntags: tag1, tag2, tag3\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example\n```\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example\n```\n\nOptional explanatory text after examples.\n\nReference: [Link](https://example.com)\n\n## File Naming Convention\n\n- Files starting with `_` are special (excluded from build)\n- Rule files: `area-description.md` (e.g., `async-parallel.md`)\n- Section is automatically inferred from filename prefix\n- Rules are sorted alphabetically by title within each section\n- IDs (e.g., 1.1, 1.2) are auto-generated during build\n\n## Impact Levels\n\n- `CRITICAL` - Highest priority, major performance gains\n- `HIGH` - Significant performance improvements\n- `MEDIUM-HIGH` - Moderate-high gains\n- `MEDIUM` - Moderate performance improvements\n- `LOW-MEDIUM` - Low-medium gains\n- `LOW` - Incremental improvements\n\n## Scripts\n\n- `pnpm build` - Compile rules into AGENTS.md\n- `pnpm validate` - Validate all rule files\n- `pnpm extract-tests` - Extract test cases for LLM evaluation\n- `pnpm dev` - Build and validate\n\n## Contributing\n\nWhen adding or modifying rules:\n\n1. Use the correct filename prefix for your section\n2. Follow the `_template.md` structure\n3. Include clear bad/good examples with explanations\n4. Add appropriate tags\n5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json\n6. Rules are automatically sorted by title - no need to manage numbers!\n\n## Acknowledgments\n\nOriginally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).\n","skills/react-best-practices/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2FSKILL.md -->","skills/react-best-practices/metadata.json":"{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Vercel Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://nextjs.org\",\n    \"https://swr.vercel.app\",\n    \"https://github.com/shuding/better-all\",\n    \"https://github.com/isaacs/node-lru-cache\",\n    \"https://vercel.com/blog/how-we-optimized-package-imports-in-next-js\",\n    \"https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast\"\n  ]\n}\n","skills/react-best-practices/rules/_sections.md":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Eliminating Waterfalls (async)\n\n**Impact:** CRITICAL  \n**Description:** Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.\n\n## 2. Bundle Size Optimization (bundle)\n\n**Impact:** CRITICAL  \n**Description:** Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.\n\n## 3. Server-Side Performance (server)\n\n**Impact:** HIGH  \n**Description:** Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.\n\n## 4. Client-Side Data Fetching (client)\n\n**Impact:** MEDIUM-HIGH  \n**Description:** Automatic deduplication and efficient data fetching patterns reduce redundant network requests.\n\n## 5. Re-render Optimization (rerender)\n\n**Impact:** MEDIUM  \n**Description:** Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.\n\n## 6. Rendering Performance (rendering)\n\n**Impact:** MEDIUM  \n**Description:** Optimizing the rendering process reduces the work the browser needs to do.\n\n## 7. JavaScript Performance (js)\n\n**Impact:** LOW-MEDIUM  \n**Description:** Micro-optimizations for hot paths can add up to meaningful improvements.\n\n## 8. Advanced Patterns (advanced)\n\n**Impact:** LOW  \n**Description:** Advanced patterns for specific cases that require careful implementation.\n","skills/react-best-practices/rules/_template.md":"---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description of impact (e.g., \"20-50% improvement\")\ntags: tag1, tag2\n---\n\n## Rule Title Here\n\n**Impact: MEDIUM (optional impact description)**\n\nBrief explanation of the rule and why it matters. This should be clear and concise, explaining the performance implications.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example here\nconst bad = example()\n```\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example here\nconst good = example()\n```\n\nReference: [Link to documentation or resource](https://example.com)\n","skills/react-best-practices/rules/advanced-effect-event-deps.md":"---\ntitle: Do Not Put Effect Events in Dependency Arrays\nimpact: LOW\nimpactDescription: avoids unnecessary effect re-runs and lint errors\ntags: advanced, hooks, useEffectEvent, dependencies, effects\n---\n\n## Do Not Put Effect Events in Dependency Arrays\n\nEffect Event functions do not have a stable identity. Their identity intentionally changes on every render. Do not include the function returned by `useEffectEvent` in a `useEffect` dependency array. Keep the actual reactive values as dependencies and call the Effect Event from inside the effect body or subscriptions created by that effect.\n\n**Incorrect (Effect Event added as a dependency):**\n\n```tsx\nimport { useEffect, useEffectEvent } from 'react'\n\nfunction ChatRoom({ roomId, onConnected }: {\n  roomId: string\n  onConnected: () => void\n}) {\n  const handleConnected = useEffectEvent(onConnected)\n\n  useEffect(() => {\n    const connection = createConnection(roomId)\n    connection.on('connected', handleConnected)\n    connection.connect()\n\n    return () => connection.disconnect()\n  }, [roomId, handleConnected])\n}\n```\n\nIncluding the Effect Event in dependencies makes the effect re-run every render and triggers the React Hooks lint rule.\n\n**Correct (depend on reactive values, not the Effect Event):**\n\n```tsx\nimport { useEffect, useEffectEvent } from 'react'\n\nfunction ChatRoom({ roomId, onConnected }: {\n  roomId: string\n  onConnected: () => void\n}) {\n  const handleConnected = useEffectEvent(onConnected)\n\n  useEffect(() => {\n    const connection = createConnection(roomId)\n    connection.on('connected', handleConnected)\n    connection.connect()\n\n    return () => connection.disconnect()\n  }, [roomId])\n}\n```\n\nReference: [React useEffectEvent: Effect Event in deps](https://react.dev/reference/react/useEffectEvent#effect-event-in-deps)\n","skills/react-best-practices/rules/advanced-event-handler-refs.md":"---\ntitle: Store Event Handlers in Refs\nimpact: LOW\nimpactDescription: stable subscriptions\ntags: advanced, hooks, refs, event-handlers, optimization\n---\n\n## Store Event Handlers in Refs\n\nStore callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.\n\n**Incorrect (re-subscribes on every render):**\n\n```tsx\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  useEffect(() => {\n    window.addEventListener(event, handler)\n    return () => window.removeEventListener(event, handler)\n  }, [event, handler])\n}\n```\n\n**Correct (stable subscription):**\n\n```tsx\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  const handlerRef = useRef(handler)\n  useEffect(() => {\n    handlerRef.current = handler\n  }, [handler])\n\n  useEffect(() => {\n    const listener = (e) => handlerRef.current(e)\n    window.addEventListener(event, listener)\n    return () => window.removeEventListener(event, listener)\n  }, [event])\n}\n```\n\n**Alternative: use `useEffectEvent` if you're on latest React:**\n\n```tsx\nimport { useEffectEvent } from 'react'\n\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  const onEvent = useEffectEvent(handler)\n\n  useEffect(() => {\n    window.addEventListener(event, onEvent)\n    return () => window.removeEventListener(event, onEvent)\n  }, [event])\n}\n```\n\n`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.\n","skills/react-best-practices/rules/advanced-init-once.md":"---\ntitle: Initialize App Once, Not Per Mount\nimpact: LOW-MEDIUM\nimpactDescription: avoids duplicate init in development\ntags: initialization, useEffect, app-startup, side-effects\n---\n\n## Initialize App Once, Not Per Mount\n\nDo not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.\n\n**Incorrect (runs twice in dev, re-runs on remount):**\n\n```tsx\nfunction Comp() {\n  useEffect(() => {\n    loadFromStorage()\n    checkAuthToken()\n  }, [])\n\n  // ...\n}\n```\n\n**Correct (once per app load):**\n\n```tsx\nlet didInit = false\n\nfunction Comp() {\n  useEffect(() => {\n    if (didInit) return\n    didInit = true\n    loadFromStorage()\n    checkAuthToken()\n  }, [])\n\n  // ...\n}\n```\n\nReference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)\n","skills/react-best-practices/rules/advanced-use-latest.md":"---\ntitle: useEffectEvent for Stable Callback Refs\nimpact: LOW\nimpactDescription: prevents effect re-runs\ntags: advanced, hooks, useEffectEvent, refs, optimization\n---\n\n## useEffectEvent for Stable Callback Refs\n\nAccess latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.\n\n**Incorrect (effect re-runs on every callback change):**\n\n```tsx\nfunction SearchInput({ onSearch }: { onSearch: (q: string) => void }) {\n  const [query, setQuery] = useState('')\n\n  useEffect(() => {\n    const timeout = setTimeout(() => onSearch(query), 300)\n    return () => clearTimeout(timeout)\n  }, [query, onSearch])\n}\n```\n\n**Correct (using React's useEffectEvent):**\n\n```tsx\nimport { useEffectEvent } from 'react';\n\nfunction SearchInput({ onSearch }: { onSearch: (q: string) => void }) {\n  const [query, setQuery] = useState('')\n  const onSearchEvent = useEffectEvent(onSearch)\n\n  useEffect(() => {\n    const timeout = setTimeout(() => onSearchEvent(query), 300)\n    return () => clearTimeout(timeout)\n  }, [query])\n}\n```\n","skills/react-best-practices/rules/async-api-routes.md":"---\ntitle: Prevent Waterfall Chains in API Routes\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: api-routes, server-actions, waterfalls, parallelization\n---\n\n## Prevent Waterfall Chains in API Routes\n\nIn API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n\n**Incorrect (config waits for auth, data waits for both):**\n\n```typescript\nexport async function GET(request: Request) {\n  const session = await auth()\n  const config = await fetchConfig()\n  const data = await fetchData(session.user.id)\n  return Response.json({ data, config })\n}\n```\n\n**Correct (auth and config start immediately):**\n\n```typescript\nexport async function GET(request: Request) {\n  const sessionPromise = auth()\n  const configPromise = fetchConfig()\n  const session = await sessionPromise\n  const [config, data] = await Promise.all([\n    configPromise,\n    fetchData(session.user.id)\n  ])\n  return Response.json({ data, config })\n}\n```\n\nFor operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).\n","skills/react-best-practices/rules/async-cheap-condition-before-await.md":"---\ntitle: Check Cheap Conditions Before Async Flags\nimpact: HIGH\nimpactDescription: avoids unnecessary async work when a synchronous guard already fails\ntags: async, await, feature-flags, short-circuit, conditional\n---\n\n## Check Cheap Conditions Before Async Flags\n\nWhen a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n\nThis is a specialization of [Defer Await Until Needed](./async-defer-await.md) for `flag && cheapCondition` style checks.\n\n**Incorrect:**\n\n```typescript\nconst someFlag = await getFlag()\n\nif (someFlag && someCondition) {\n  // ...\n}\n```\n\n**Correct:**\n\n```typescript\nif (someCondition) {\n  const someFlag = await getFlag()\n  if (someFlag) {\n    // ...\n  }\n}\n```\n\nThis matters when `getFlag` hits the network, a feature-flag service, or `React.cache` / DB work: skipping it when `someCondition` is false removes that cost on the cold path.\n\nKeep the original order if `someCondition` is expensive, depends on the flag, or you must run side effects in a fixed order.\n","skills/react-best-practices/rules/async-defer-await.md":"---\ntitle: Defer Await Until Needed\nimpact: HIGH\nimpactDescription: avoids blocking unused code paths\ntags: async, await, conditional, optimization\n---\n\n## Defer Await Until Needed\n\nMove `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n\n**Incorrect (blocks both branches):**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  const userData = await fetchUserData(userId)\n  \n  if (skipProcessing) {\n    // Returns immediately but still waited for userData\n    return { skipped: true }\n  }\n  \n  // Only this branch uses userData\n  return processUserData(userData)\n}\n```\n\n**Correct (only blocks when needed):**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  if (skipProcessing) {\n    // Returns immediately without waiting\n    return { skipped: true }\n  }\n  \n  // Fetch only when needed\n  const userData = await fetchUserData(userId)\n  return processUserData(userData)\n}\n```\n\n**Another example (early return optimization):**\n\n```typescript\n// Incorrect: always fetches permissions\nasync function updateResource(resourceId: string, userId: string) {\n  const permissions = await fetchPermissions(userId)\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n\n// Correct: fetches only when needed\nasync function updateResource(resourceId: string, userId: string) {\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  const permissions = await fetchPermissions(userId)\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n```\n\nThis optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.\n\nFor `await getFlag()` combined with a cheap synchronous guard (`flag && someCondition`), see [Check Cheap Conditions Before Async Flags](./async-cheap-condition-before-await.md).\n","skills/react-best-practices/rules/async-dependencies.md":"---\ntitle: Dependency-Based Parallelization\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: async, parallelization, dependencies, better-all\n---\n\n## Dependency-Based Parallelization\n\nFor operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n\n**Incorrect (profile waits for config unnecessarily):**\n\n```typescript\nconst [user, config] = await Promise.all([\n  fetchUser(),\n  fetchConfig()\n])\nconst profile = await fetchProfile(user.id)\n```\n\n**Correct (config and profile run in parallel):**\n\n```typescript\nimport { all } from 'better-all'\n\nconst { user, config, profile } = await all({\n  async user() { return fetchUser() },\n  async config() { return fetchConfig() },\n  async profile() {\n    return fetchProfile((await this.$.user).id)\n  }\n})\n```\n\n**Alternative without extra dependencies:**\n\nWe can also create all the promises first, and do `Promise.all()` at the end.\n\n```typescript\nconst userPromise = fetchUser()\nconst profilePromise = userPromise.then(user => fetchProfile(user.id))\n\nconst [user, config, profile] = await Promise.all([\n  userPromise,\n  fetchConfig(),\n  profilePromise\n])\n```\n\nReference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)\n","skills/react-best-practices/rules/async-parallel.md":"---\ntitle: Promise.all() for Independent Operations\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: async, parallelization, promises, waterfalls\n---\n\n## Promise.all() for Independent Operations\n\nWhen async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n\n**Incorrect (sequential execution, 3 round trips):**\n\n```typescript\nconst user = await fetchUser()\nconst posts = await fetchPosts()\nconst comments = await fetchComments()\n```\n\n**Correct (parallel execution, 1 round trip):**\n\n```typescript\nconst [user, posts, comments] = await Promise.all([\n  fetchUser(),\n  fetchPosts(),\n  fetchComments()\n])\n```\n","skills/react-best-practices/rules/async-suspense-boundaries.md":"---\ntitle: Strategic Suspense Boundaries\nimpact: HIGH\nimpactDescription: faster initial paint\ntags: async, suspense, streaming, layout-shift\n---\n\n## Strategic Suspense Boundaries\n\nInstead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n\n**Incorrect (wrapper blocked by data fetching):**\n\n```tsx\nasync function Page() {\n  const data = await fetchData() // Blocks entire page\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <DataDisplay data={data} />\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n```\n\nThe entire layout waits for data even though only the middle section needs it.\n\n**Correct (wrapper shows immediately, data streams in):**\n\n```tsx\nfunction Page() {\n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <Suspense fallback={<Skeleton />}>\n          <DataDisplay />\n        </Suspense>\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nasync function DataDisplay() {\n  const data = await fetchData() // Only blocks this component\n  return <div>{data.content}</div>\n}\n```\n\nSidebar, Header, and Footer render immediately. Only DataDisplay waits for data.\n\n**Alternative (share promise across components):**\n\n```tsx\nfunction Page() {\n  // Start fetch immediately, but don't await\n  const dataPromise = fetchData()\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <Suspense fallback={<Skeleton />}>\n        <DataDisplay dataPromise={dataPromise} />\n        <DataSummary dataPromise={dataPromise} />\n      </Suspense>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nfunction DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Unwraps the promise\n  return <div>{data.content}</div>\n}\n\nfunction DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Reuses the same promise\n  return <div>{data.summary}</div>\n}\n```\n\nBoth components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.\n\n**When NOT to use this pattern:**\n\n- Critical data needed for layout decisions (affects positioning)\n- SEO-critical content above the fold\n- Small, fast queries where suspense overhead isn't worth it\n- When you want to avoid layout shift (loading → content jump)\n\n**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.\n","skills/react-best-practices/rules/bundle-analyzable-paths.md":"---\ntitle: Prefer Statically Analyzable Paths\nimpact: HIGH\nimpactDescription: avoids accidental broad bundles and file traces\ntags: bundle, nextjs, vite, webpack, rollup, esbuild, path\n---\n\n## Prefer Statically Analyzable Paths\n\nBuild tools work best when import and file-system paths are obvious at build time. If you hide the real path inside a variable or compose it too dynamically, the tool either has to include a broad set of possible files, warn that it cannot analyze the import, or widen file tracing to stay safe.\n\nPrefer explicit maps or literal paths so the set of reachable files stays narrow and predictable. This is the same rule whether you are choosing modules with `import()` or reading files in server/build code.\n\nWhen analysis becomes too broad, the cost is real:\n- Larger server bundles\n- Slower builds\n- Worse cold starts\n- More memory use\n\n### Import Paths\n\n**Incorrect (the bundler cannot tell what may be imported):**\n\n```ts\nconst PAGE_MODULES = {\n  home: './pages/home',\n  settings: './pages/settings',\n} as const\n\nconst Page = await import(PAGE_MODULES[pageName])\n```\n\n**Correct (use an explicit map of allowed modules):**\n\n```ts\nconst PAGE_MODULES = {\n  home: () => import('./pages/home'),\n  settings: () => import('./pages/settings'),\n} as const\n\nconst Page = await PAGE_MODULES[pageName]()\n```\n\n### File-System Paths\n\n**Incorrect (a 2-value enum still hides the final path from static analysis):**\n\n```ts\nconst baseDir = path.join(process.cwd(), 'content/' + contentKind)\n```\n\n**Correct (make each final path literal at the callsite):**\n\n```ts\nconst baseDir =\n  kind === ContentKind.Blog\n    ? path.join(process.cwd(), 'content/blog')\n    : path.join(process.cwd(), 'content/docs')\n```\n\nIn Next.js server code, this matters for output file tracing too. `path.join(process.cwd(), someVar)` can widen the traced file set because Next.js statically analyze `import`, `require`, and `fs` usage.\n\nReference: [Next.js output](https://nextjs.org/docs/app/api-reference/config/next-config-js/output), [Next.js dynamic imports](https://nextjs.org/learn/seo/dynamic-imports), [Vite features](https://vite.dev/guide/features.html), [esbuild API](https://esbuild.github.io/api/), [Rollup dynamic import vars](https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars), [Webpack dependency management](https://webpack.js.org/guides/dependency-management/)\n","skills/react-best-practices/rules/bundle-barrel-imports.md":"---\ntitle: Avoid Barrel File Imports\nimpact: CRITICAL\nimpactDescription: 200-800ms import cost, slow builds\ntags: bundle, imports, tree-shaking, barrel-files, performance\n---\n\n## Avoid Barrel File Imports\n\nImport directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n\nPopular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.\n\n**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.\n\n**Incorrect (imports entire library):**\n\n```tsx\nimport { Check, X, Menu } from 'lucide-react'\n// Loads 1,583 modules, takes ~2.8s extra in dev\n// Runtime cost: 200-800ms on every cold start\n\nimport { Button, TextField } from '@mui/material'\n// Loads 2,225 modules, takes ~4.2s extra in dev\n```\n\n**Correct - Next.js 13.5+ (recommended):**\n\n```js\n// next.config.js - automatically optimizes barrel imports at build time\nmodule.exports = {\n  experimental: {\n    optimizePackageImports: ['lucide-react', '@mui/material']\n  }\n}\n```\n\n```tsx\n// Keep the standard imports - Next.js transforms them to direct imports\nimport { Check, X, Menu } from 'lucide-react'\n// Full TypeScript support, no manual path wrangling\n```\n\nThis is the recommended approach because it preserves TypeScript type safety and editor autocompletion while still eliminating the barrel import cost.\n\n**Correct - Direct imports (non-Next.js projects):**\n\n```tsx\nimport Button from '@mui/material/Button'\nimport TextField from '@mui/material/TextField'\n// Loads only what you use\n```\n\n> **TypeScript warning:** Some libraries (notably `lucide-react`) don't ship `.d.ts` files for their deep import paths. Importing from `lucide-react/dist/esm/icons/check` resolves to an implicit `any` type, causing errors under `strict` or `noImplicitAny`. Prefer `optimizePackageImports` when available, or verify the library exports types for its subpaths before using direct imports.\n\nThese optimizations provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.\n\nLibraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.\n\nReference: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)\n","skills/react-best-practices/rules/bundle-conditional.md":"---\ntitle: Conditional Module Loading\nimpact: HIGH\nimpactDescription: loads large data only when needed\ntags: bundle, conditional-loading, lazy-loading\n---\n\n## Conditional Module Loading\n\nLoad large data or modules only when a feature is activated.\n\n**Example (lazy-load animation frames):**\n\n```tsx\nfunction AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {\n  const [frames, setFrames] = useState<Frame[] | null>(null)\n\n  useEffect(() => {\n    if (enabled && !frames && typeof window !== 'undefined') {\n      import('./animation-frames.js')\n        .then(mod => setFrames(mod.frames))\n        .catch(() => setEnabled(false))\n    }\n  }, [enabled, frames, setEnabled])\n\n  if (!frames) return <Skeleton />\n  return <Canvas frames={frames} />\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.\n","skills/react-best-practices/rules/bundle-defer-third-party.md":"---\ntitle: Defer Non-Critical Third-Party Libraries\nimpact: MEDIUM\nimpactDescription: loads after hydration\ntags: bundle, third-party, analytics, defer\n---\n\n## Defer Non-Critical Third-Party Libraries\n\nAnalytics, logging, and error tracking don't block user interaction. Load them after hydration.\n\n**Incorrect (blocks initial bundle):**\n\n```tsx\nimport { Analytics } from '@vercel/analytics/react'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n**Correct (loads after hydration):**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst Analytics = dynamic(\n  () => import('@vercel/analytics/react').then(m => m.Analytics),\n  { ssr: false }\n)\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n","skills/react-best-practices/rules/bundle-dynamic-imports.md":"---\ntitle: Dynamic Imports for Heavy Components\nimpact: CRITICAL\nimpactDescription: directly affects TTI and LCP\ntags: bundle, dynamic-import, code-splitting, next-dynamic\n---\n\n## Dynamic Imports for Heavy Components\n\nUse `next/dynamic` to lazy-load large components not needed on initial render.\n\n**Incorrect (Monaco bundles with main chunk ~300KB):**\n\n```tsx\nimport { MonacoEditor } from './monaco-editor'\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n**Correct (Monaco loads on demand):**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst MonacoEditor = dynamic(\n  () => import('./monaco-editor').then(m => m.MonacoEditor),\n  { ssr: false }\n)\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n","skills/react-best-practices/rules/bundle-preload.md":"---\ntitle: Preload Based on User Intent\nimpact: MEDIUM\nimpactDescription: reduces perceived latency\ntags: bundle, preload, user-intent, hover\n---\n\n## Preload Based on User Intent\n\nPreload heavy bundles before they're needed to reduce perceived latency.\n\n**Example (preload on hover/focus):**\n\n```tsx\nfunction EditorButton({ onClick }: { onClick: () => void }) {\n  const preload = () => {\n    if (typeof window !== 'undefined') {\n      void import('./monaco-editor')\n    }\n  }\n\n  return (\n    <button\n      onMouseEnter={preload}\n      onFocus={preload}\n      onClick={onClick}\n    >\n      Open Editor\n    </button>\n  )\n}\n```\n\n**Example (preload when feature flag is enabled):**\n\n```tsx\nfunction FlagsProvider({ children, flags }: Props) {\n  useEffect(() => {\n    if (flags.editorEnabled && typeof window !== 'undefined') {\n      void import('./monaco-editor').then(mod => mod.init())\n    }\n  }, [flags.editorEnabled])\n\n  return <FlagsContext.Provider value={flags}>\n    {children}\n  </FlagsContext.Provider>\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.\n","skills/react-best-practices/rules/client-event-listeners.md":"---\ntitle: Deduplicate Global Event Listeners\nimpact: LOW\nimpactDescription: single listener for N components\ntags: client, swr, event-listeners, subscription\n---\n\n## Deduplicate Global Event Listeners\n\nUse `useSWRSubscription()` to share global event listeners across component instances.\n\n**Incorrect (N instances = N listeners):**\n\n```tsx\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  useEffect(() => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && e.key === key) {\n        callback()\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  }, [key, callback])\n}\n```\n\nWhen using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.\n\n**Correct (N instances = 1 listener):**\n\n```tsx\nimport useSWRSubscription from 'swr/subscription'\n\n// Module-level Map to track callbacks per key\nconst keyCallbacks = new Map<string, Set<() => void>>()\n\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  // Register this callback in the Map\n  useEffect(() => {\n    if (!keyCallbacks.has(key)) {\n      keyCallbacks.set(key, new Set())\n    }\n    keyCallbacks.get(key)!.add(callback)\n\n    return () => {\n      const set = keyCallbacks.get(key)\n      if (set) {\n        set.delete(callback)\n        if (set.size === 0) {\n          keyCallbacks.delete(key)\n        }\n      }\n    }\n  }, [key, callback])\n\n  useSWRSubscription('global-keydown', () => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && keyCallbacks.has(e.key)) {\n        keyCallbacks.get(e.key)!.forEach(cb => cb())\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  })\n}\n\nfunction Profile() {\n  // Multiple shortcuts will share the same listener\n  useKeyboardShortcut('p', () => { /* ... */ }) \n  useKeyboardShortcut('k', () => { /* ... */ })\n  // ...\n}\n```\n","skills/react-best-practices/rules/client-localstorage-schema.md":"---\ntitle: Version and Minimize localStorage Data\nimpact: MEDIUM\nimpactDescription: prevents schema conflicts, reduces storage size\ntags: client, localStorage, storage, versioning, data-minimization\n---\n\n## Version and Minimize localStorage Data\n\nAdd version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n\n**Incorrect:**\n\n```typescript\n// No version, stores everything, no error handling\nlocalStorage.setItem('userConfig', JSON.stringify(fullUserObject))\nconst data = localStorage.getItem('userConfig')\n```\n\n**Correct:**\n\n```typescript\nconst VERSION = 'v2'\n\nfunction saveConfig(config: { theme: string; language: string }) {\n  try {\n    localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))\n  } catch {\n    // Throws in incognito/private browsing, quota exceeded, or disabled\n  }\n}\n\nfunction loadConfig() {\n  try {\n    const data = localStorage.getItem(`userConfig:${VERSION}`)\n    return data ? JSON.parse(data) : null\n  } catch {\n    return null\n  }\n}\n\n// Migration from v1 to v2\nfunction migrate() {\n  try {\n    const v1 = localStorage.getItem('userConfig:v1')\n    if (v1) {\n      const old = JSON.parse(v1)\n      saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })\n      localStorage.removeItem('userConfig:v1')\n    }\n  } catch {}\n}\n```\n\n**Store minimal fields from server responses:**\n\n```typescript\n// User object has 20+ fields, only store what UI needs\nfunction cachePrefs(user: FullUser) {\n  try {\n    localStorage.setItem('prefs:v1', JSON.stringify({\n      theme: user.preferences.theme,\n      notifications: user.preferences.notifications\n    }))\n  } catch {}\n}\n```\n\n**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.\n\n**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.\n","skills/react-best-practices/rules/client-passive-event-listeners.md":"---\ntitle: Use Passive Event Listeners for Scrolling Performance\nimpact: MEDIUM\nimpactDescription: eliminates scroll delay caused by event listeners\ntags: client, event-listeners, scrolling, performance, touch, wheel\n---\n\n## Use Passive Event Listeners for Scrolling Performance\n\nAdd `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n\n**Incorrect:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch)\n  document.addEventListener('wheel', handleWheel)\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Correct:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch, { passive: true })\n  document.addEventListener('wheel', handleWheel, { passive: true })\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.\n\n**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.\n","skills/react-best-practices/rules/client-swr-dedup.md":"---\ntitle: Use SWR for Automatic Deduplication\nimpact: MEDIUM-HIGH\nimpactDescription: automatic deduplication\ntags: client, swr, deduplication, data-fetching\n---\n\n## Use SWR for Automatic Deduplication\n\nSWR enables request deduplication, caching, and revalidation across component instances.\n\n**Incorrect (no deduplication, each instance fetches):**\n\n```tsx\nfunction UserList() {\n  const [users, setUsers] = useState([])\n  useEffect(() => {\n    fetch('/api/users')\n      .then(r => r.json())\n      .then(setUsers)\n  }, [])\n}\n```\n\n**Correct (multiple instances share one request):**\n\n```tsx\nimport useSWR from 'swr'\n\nfunction UserList() {\n  const { data: users } = useSWR('/api/users', fetcher)\n}\n```\n\n**For immutable data:**\n\n```tsx\nimport { useImmutableSWR } from '@/lib/swr'\n\nfunction StaticContent() {\n  const { data } = useImmutableSWR('/api/config', fetcher)\n}\n```\n\n**For mutations:**\n\n```tsx\nimport { useSWRMutation } from 'swr/mutation'\n\nfunction UpdateButton() {\n  const { trigger } = useSWRMutation('/api/user', updateUser)\n  return <button onClick={() => trigger()}>Update</button>\n}\n```\n\nReference: [https://swr.vercel.app](https://swr.vercel.app)\n","skills/react-best-practices/rules/js-batch-dom-css.md":"---\ntitle: Avoid Layout Thrashing\nimpact: MEDIUM\nimpactDescription: prevents forced synchronous layouts and reduces performance bottlenecks\ntags: javascript, dom, css, performance, reflow, layout-thrashing\n---\n\n## Avoid Layout Thrashing\n\nAvoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.\n\n**This is OK (browser batches style changes):**\n```typescript\nfunction updateElementStyles(element: HTMLElement) {\n  // Each line invalidates style, but browser batches the recalculation\n  element.style.width = '100px'\n  element.style.height = '200px'\n  element.style.backgroundColor = 'blue'\n  element.style.border = '1px solid black'\n}\n```\n\n**Incorrect (interleaved reads and writes force reflows):**\n```typescript\nfunction layoutThrashing(element: HTMLElement) {\n  element.style.width = '100px'\n  const width = element.offsetWidth  // Forces reflow\n  element.style.height = '200px'\n  const height = element.offsetHeight  // Forces another reflow\n}\n```\n\n**Correct (batch writes, then read once):**\n```typescript\nfunction updateElementStyles(element: HTMLElement) {\n  // Batch all writes together\n  element.style.width = '100px'\n  element.style.height = '200px'\n  element.style.backgroundColor = 'blue'\n  element.style.border = '1px solid black'\n  \n  // Read after all writes are done (single reflow)\n  const { width, height } = element.getBoundingClientRect()\n}\n```\n\n**Correct (batch reads, then writes):**\n```typescript\nfunction avoidThrashing(element: HTMLElement) {\n  // Read phase - all layout queries first\n  const rect1 = element.getBoundingClientRect()\n  const offsetWidth = element.offsetWidth\n  const offsetHeight = element.offsetHeight\n  \n  // Write phase - all style changes after\n  element.style.width = '100px'\n  element.style.height = '200px'\n}\n```\n\n**Better: use CSS classes**\n```css\n.highlighted-box {\n  width: 100px;\n  height: 200px;\n  background-color: blue;\n  border: 1px solid black;\n}\n```\n```typescript\nfunction updateElementStyles(element: HTMLElement) {\n  element.classList.add('highlighted-box')\n  \n  const { width, height } = element.getBoundingClientRect()\n}\n```\n\n**React example:**\n```tsx\n// Incorrect: interleaving style changes with layout queries\nfunction Box({ isHighlighted }: { isHighlighted: boolean }) {\n  const ref = useRef<HTMLDivElement>(null)\n  \n  useEffect(() => {\n    if (ref.current && isHighlighted) {\n      ref.current.style.width = '100px'\n      const width = ref.current.offsetWidth // Forces layout\n      ref.current.style.height = '200px'\n    }\n  }, [isHighlighted])\n  \n  return <div ref={ref}>Content</div>\n}\n\n// Correct: toggle class\nfunction Box({ isHighlighted }: { isHighlighted: boolean }) {\n  return (\n    <div className={isHighlighted ? 'highlighted-box' : ''}>\n      Content\n    </div>\n  )\n}\n```\n\nPrefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.\n\nSee [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.\n","skills/react-best-practices/rules/js-cache-function-results.md":"---\ntitle: Cache Repeated Function Calls\nimpact: MEDIUM\nimpactDescription: avoid redundant computation\ntags: javascript, cache, memoization, performance\n---\n\n## Cache Repeated Function Calls\n\nUse a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.\n\n**Incorrect (redundant computation):**\n\n```typescript\nfunction ProjectList({ projects }: { projects: Project[] }) {\n  return (\n    <div>\n      {projects.map(project => {\n        // slugify() called 100+ times for same project names\n        const slug = slugify(project.name)\n        \n        return <ProjectCard key={project.id} slug={slug} />\n      })}\n    </div>\n  )\n}\n```\n\n**Correct (cached results):**\n\n```typescript\n// Module-level cache\nconst slugifyCache = new Map<string, string>()\n\nfunction cachedSlugify(text: string): string {\n  if (slugifyCache.has(text)) {\n    return slugifyCache.get(text)!\n  }\n  const result = slugify(text)\n  slugifyCache.set(text, result)\n  return result\n}\n\nfunction ProjectList({ projects }: { projects: Project[] }) {\n  return (\n    <div>\n      {projects.map(project => {\n        // Computed only once per unique project name\n        const slug = cachedSlugify(project.name)\n        \n        return <ProjectCard key={project.id} slug={slug} />\n      })}\n    </div>\n  )\n}\n```\n\n**Simpler pattern for single-value functions:**\n\n```typescript\nlet isLoggedInCache: boolean | null = null\n\nfunction isLoggedIn(): boolean {\n  if (isLoggedInCache !== null) {\n    return isLoggedInCache\n  }\n  \n  isLoggedInCache = document.cookie.includes('auth=')\n  return isLoggedInCache\n}\n\n// Clear cache when auth changes\nfunction onAuthChange() {\n  isLoggedInCache = null\n}\n```\n\nUse a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.\n\nReference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)\n","skills/react-best-practices/rules/js-cache-property-access.md":"---\ntitle: Cache Property Access in Loops\nimpact: LOW-MEDIUM\nimpactDescription: reduces lookups\ntags: javascript, loops, optimization, caching\n---\n\n## Cache Property Access in Loops\n\nCache object property lookups in hot paths.\n\n**Incorrect (3 lookups × N iterations):**\n\n```typescript\nfor (let i = 0; i < arr.length; i++) {\n  process(obj.config.settings.value)\n}\n```\n\n**Correct (1 lookup total):**\n\n```typescript\nconst value = obj.config.settings.value\nconst len = arr.length\nfor (let i = 0; i < len; i++) {\n  process(value)\n}\n```\n","skills/react-best-practices/rules/js-cache-storage.md":"---\ntitle: Cache Storage API Calls\nimpact: LOW-MEDIUM\nimpactDescription: reduces expensive I/O\ntags: javascript, localStorage, storage, caching, performance\n---\n\n## Cache Storage API Calls\n\n`localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory.\n\n**Incorrect (reads storage on every call):**\n\n```typescript\nfunction getTheme() {\n  return localStorage.getItem('theme') ?? 'light'\n}\n// Called 10 times = 10 storage reads\n```\n\n**Correct (Map cache):**\n\n```typescript\nconst storageCache = new Map<string, string | null>()\n\nfunction getLocalStorage(key: string) {\n  if (!storageCache.has(key)) {\n    storageCache.set(key, localStorage.getItem(key))\n  }\n  return storageCache.get(key)\n}\n\nfunction setLocalStorage(key: string, value: string) {\n  localStorage.setItem(key, value)\n  storageCache.set(key, value)  // keep cache in sync\n}\n```\n\nUse a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.\n\n**Cookie caching:**\n\n```typescript\nlet cookieCache: Record<string, string> | null = null\n\nfunction getCookie(name: string) {\n  if (!cookieCache) {\n    cookieCache = Object.fromEntries(\n      document.cookie.split('; ').map(c => c.split('='))\n    )\n  }\n  return cookieCache[name]\n}\n```\n\n**Important (invalidate on external changes):**\n\nIf storage can change externally (another tab, server-set cookies), invalidate cache:\n\n```typescript\nwindow.addEventListener('storage', (e) => {\n  if (e.key) storageCache.delete(e.key)\n})\n\ndocument.addEventListener('visibilitychange', () => {\n  if (document.visibilityState === 'visible') {\n    storageCache.clear()\n  }\n})\n```\n","skills/react-best-practices/rules/js-combine-iterations.md":"---\ntitle: Combine Multiple Array Iterations\nimpact: LOW-MEDIUM\nimpactDescription: reduces iterations\ntags: javascript, arrays, loops, performance\n---\n\n## Combine Multiple Array Iterations\n\nMultiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.\n\n**Incorrect (3 iterations):**\n\n```typescript\nconst admins = users.filter(u => u.isAdmin)\nconst testers = users.filter(u => u.isTester)\nconst inactive = users.filter(u => !u.isActive)\n```\n\n**Correct (1 iteration):**\n\n```typescript\nconst admins: User[] = []\nconst testers: User[] = []\nconst inactive: User[] = []\n\nfor (const user of users) {\n  if (user.isAdmin) admins.push(user)\n  if (user.isTester) testers.push(user)\n  if (!user.isActive) inactive.push(user)\n}\n```\n","skills/react-best-practices/rules/js-early-exit.md":"---\ntitle: Early Return from Functions\nimpact: LOW-MEDIUM\nimpactDescription: avoids unnecessary computation\ntags: javascript, functions, optimization, early-return\n---\n\n## Early Return from Functions\n\nReturn early when result is determined to skip unnecessary processing.\n\n**Incorrect (processes all items even after finding answer):**\n\n```typescript\nfunction validateUsers(users: User[]) {\n  let hasError = false\n  let errorMessage = ''\n  \n  for (const user of users) {\n    if (!user.email) {\n      hasError = true\n      errorMessage = 'Email required'\n    }\n    if (!user.name) {\n      hasError = true\n      errorMessage = 'Name required'\n    }\n    // Continues checking all users even after error found\n  }\n  \n  return hasError ? { valid: false, error: errorMessage } : { valid: true }\n}\n```\n\n**Correct (returns immediately on first error):**\n\n```typescript\nfunction validateUsers(users: User[]) {\n  for (const user of users) {\n    if (!user.email) {\n      return { valid: false, error: 'Email required' }\n    }\n    if (!user.name) {\n      return { valid: false, error: 'Name required' }\n    }\n  }\n\n  return { valid: true }\n}\n```\n","skills/react-best-practices/rules/js-flatmap-filter.md":"---\ntitle: Use flatMap to Map and Filter in One Pass\nimpact: LOW-MEDIUM\nimpactDescription: eliminates intermediate array\ntags: javascript, arrays, flatMap, filter, performance\n---\n\n## Use flatMap to Map and Filter in One Pass\n\n**Impact: LOW-MEDIUM (eliminates intermediate array)**\n\nChaining `.map().filter(Boolean)` creates an intermediate array and iterates twice. Use `.flatMap()` to transform and filter in a single pass.\n\n**Incorrect (2 iterations, intermediate array):**\n\n```typescript\nconst userNames = users\n  .map(user => user.isActive ? user.name : null)\n  .filter(Boolean)\n```\n\n**Correct (1 iteration, no intermediate array):**\n\n```typescript\nconst userNames = users.flatMap(user =>\n  user.isActive ? [user.name] : []\n)\n```\n\n**More examples:**\n\n```typescript\n// Extract valid emails from responses\n// Before\nconst emails = responses\n  .map(r => r.success ? r.data.email : null)\n  .filter(Boolean)\n\n// After\nconst emails = responses.flatMap(r =>\n  r.success ? [r.data.email] : []\n)\n\n// Parse and filter valid numbers\n// Before\nconst numbers = strings\n  .map(s => parseInt(s, 10))\n  .filter(n => !isNaN(n))\n\n// After\nconst numbers = strings.flatMap(s => {\n  const n = parseInt(s, 10)\n  return isNaN(n) ? [] : [n]\n})\n```\n\n**When to use:**\n- Transforming items while filtering some out\n- Conditional mapping where some inputs produce no output\n- Parsing/validating where invalid inputs should be skipped\n","skills/react-best-practices/rules/js-hoist-regexp.md":"---\ntitle: Hoist RegExp Creation\nimpact: LOW-MEDIUM\nimpactDescription: avoids recreation\ntags: javascript, regexp, optimization, memoization\n---\n\n## Hoist RegExp Creation\n\nDon't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.\n\n**Incorrect (new RegExp every render):**\n\n```tsx\nfunction Highlighter({ text, query }: Props) {\n  const regex = new RegExp(`(${query})`, 'gi')\n  const parts = text.split(regex)\n  return <>{parts.map((part, i) => ...)}</>\n}\n```\n\n**Correct (memoize or hoist):**\n\n```tsx\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nfunction Highlighter({ text, query }: Props) {\n  const regex = useMemo(\n    () => new RegExp(`(${escapeRegex(query)})`, 'gi'),\n    [query]\n  )\n  const parts = text.split(regex)\n  return <>{parts.map((part, i) => ...)}</>\n}\n```\n\n**Warning (global regex has mutable state):**\n\nGlobal regex (`/g`) has mutable `lastIndex` state:\n\n```typescript\nconst regex = /foo/g\nregex.test('foo')  // true, lastIndex = 3\nregex.test('foo')  // false, lastIndex = 0\n```\n","skills/react-best-practices/rules/js-index-maps.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-index-maps.md -->","skills/react-best-practices/rules/js-length-check-first.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-length-check-first.md -->","skills/react-best-practices/rules/js-min-max-loop.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-min-max-loop.md -->","skills/react-best-practices/rules/js-request-idle-callback.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-request-idle-callback.md -->","skills/react-best-practices/rules/js-set-map-lookups.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-set-map-lookups.md -->","skills/react-best-practices/rules/js-tosorted-immutable.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-tosorted-immutable.md -->","skills/react-best-practices/rules/rendering-activity.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-activity.md -->","skills/react-best-practices/rules/rendering-animate-svg-wrapper.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-animate-svg-wrapper.md -->","skills/react-best-practices/rules/rendering-conditional-render.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-conditional-render.md -->","skills/react-best-practices/rules/rendering-content-visibility.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-content-visibility.md -->","skills/react-best-practices/rules/rendering-hoist-jsx.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-hoist-jsx.md -->","skills/react-best-practices/rules/rendering-hydration-no-flicker.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-hydration-no-flicker.md -->","skills/react-best-practices/rules/rendering-hydration-suppress-warning.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-hydration-suppress-warning.md -->","skills/react-best-practices/rules/rendering-resource-hints.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-resource-hints.md -->","skills/react-best-practices/rules/rendering-script-defer-async.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-script-defer-async.md -->","skills/react-best-practices/rules/rendering-svg-precision.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-svg-precision.md -->","skills/react-best-practices/rules/rendering-usetransition-loading.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-usetransition-loading.md -->","skills/react-best-practices/rules/rerender-defer-reads.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-defer-reads.md -->","skills/react-best-practices/rules/rerender-dependencies.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-dependencies.md -->","skills/react-best-practices/rules/rerender-derived-state-no-effect.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-derived-state-no-effect.md -->","skills/react-best-practices/rules/rerender-derived-state.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-derived-state.md -->","skills/react-best-practices/rules/rerender-functional-setstate.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-functional-setstate.md -->","skills/react-best-practices/rules/rerender-lazy-state-init.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-lazy-state-init.md -->","skills/react-best-practices/rules/rerender-memo-with-default-value.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-memo-with-default-value.md -->","skills/react-best-practices/rules/rerender-memo.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-memo.md -->","skills/react-best-practices/rules/rerender-move-effect-to-event.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-move-effect-to-event.md -->","skills/react-best-practices/rules/rerender-no-inline-components.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-no-inline-components.md -->","skills/react-best-practices/rules/rerender-simple-expression-in-memo.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-simple-expression-in-memo.md -->","skills/react-best-practices/rules/rerender-split-combined-hooks.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-split-combined-hooks.md -->","skills/react-best-practices/rules/rerender-transitions.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-transitions.md -->","skills/react-best-practices/rules/rerender-use-deferred-value.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-use-deferred-value.md -->","skills/react-best-practices/rules/rerender-use-ref-transient-values.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-use-ref-transient-values.md -->","skills/react-best-practices/rules/server-after-nonblocking.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-after-nonblocking.md -->","skills/react-best-practices/rules/server-auth-actions.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-auth-actions.md -->","skills/react-best-practices/rules/server-cache-lru.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-cache-lru.md -->","skills/react-best-practices/rules/server-cache-react.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-cache-react.md -->","skills/react-best-practices/rules/server-dedup-props.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-dedup-props.md -->","skills/react-best-practices/rules/server-hoist-static-io.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-hoist-static-io.md -->","skills/react-best-practices/rules/server-no-shared-module-state.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-no-shared-module-state.md -->","skills/react-best-practices/rules/server-parallel-fetching.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-parallel-fetching.md -->","skills/react-best-practices/rules/server-parallel-nested-fetching.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-parallel-nested-fetching.md -->","skills/react-best-practices/rules/server-serialization.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-serialization.md -->","skills/react-native-skills/AGENTS.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2FAGENTS.md -->","skills/react-native-skills/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2FREADME.md -->","skills/react-native-skills/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2FSKILL.md -->","skills/react-native-skills/metadata.json":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Fmetadata.json -->","skills/react-native-skills/rules/_sections.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2F_sections.md -->","skills/react-native-skills/rules/_template.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2F_template.md -->","skills/react-native-skills/rules/animation-derived-value.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fanimation-derived-value.md -->","skills/react-native-skills/rules/animation-gesture-detector-press.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fanimation-gesture-detector-press.md -->","skills/react-native-skills/rules/animation-gpu-properties.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fanimation-gpu-properties.md -->","skills/react-native-skills/rules/design-system-compound-components.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fdesign-system-compound-components.md -->","skills/react-native-skills/rules/fonts-config-plugin.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Ffonts-config-plugin.md -->","skills/react-native-skills/rules/imports-design-system-folder.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fimports-design-system-folder.md -->","skills/react-native-skills/rules/js-hoist-intl.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fjs-hoist-intl.md -->","skills/react-native-skills/rules/list-performance-callbacks.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-callbacks.md -->","skills/react-native-skills/rules/list-performance-function-references.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-function-references.md -->","skills/react-native-skills/rules/list-performance-images.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-images.md -->","skills/react-native-skills/rules/list-performance-inline-objects.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-inline-objects.md -->","skills/react-native-skills/rules/list-performance-item-expensive.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-item-expensive.md -->","skills/react-native-skills/rules/list-performance-item-memo.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-item-memo.md -->","skills/react-native-skills/rules/list-performance-item-types.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-item-types.md -->","skills/react-native-skills/rules/list-performance-virtualize.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-virtualize.md -->","skills/react-native-skills/rules/monorepo-native-deps-in-app.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fmonorepo-native-deps-in-app.md -->","skills/react-native-skills/rules/monorepo-single-dependency-versions.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fmonorepo-single-dependency-versions.md -->","skills/react-native-skills/rules/navigation-native-navigators.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fnavigation-native-navigators.md -->","skills/react-native-skills/rules/react-compiler-destructure-functions.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-compiler-destructure-functions.md -->","skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-compiler-reanimated-shared-values.md -->","skills/react-native-skills/rules/react-state-dispatcher.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-state-dispatcher.md -->","skills/react-native-skills/rules/react-state-fallback.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-state-fallback.md -->","skills/react-native-skills/rules/react-state-minimize.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-state-minimize.md -->","skills/react-native-skills/rules/rendering-no-falsy-and.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Frendering-no-falsy-and.md -->","skills/react-native-skills/rules/rendering-text-in-text-component.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Frendering-text-in-text-component.md -->","skills/react-native-skills/rules/scroll-position-no-state.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fscroll-position-no-state.md -->","skills/react-native-skills/rules/state-ground-truth.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fstate-ground-truth.md -->","skills/react-native-skills/rules/ui-expo-image.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-expo-image.md -->","skills/react-native-skills/rules/ui-image-gallery.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-image-gallery.md -->","skills/react-native-skills/rules/ui-measure-views.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-measure-views.md -->","skills/react-native-skills/rules/ui-menus.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-menus.md -->","skills/react-native-skills/rules/ui-native-modals.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-native-modals.md -->","skills/react-native-skills/rules/ui-pressable.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-pressable.md -->","skills/react-native-skills/rules/ui-safe-area-scroll.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-safe-area-scroll.md -->","skills/react-native-skills/rules/ui-scrollview-content-inset.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-scrollview-content-inset.md -->","skills/react-native-skills/rules/ui-styling.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-styling.md -->","skills/react-view-transitions/AGENTS.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2FAGENTS.md -->","skills/react-view-transitions/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2FREADME.md -->","skills/react-view-transitions/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2FSKILL.md -->","skills/react-view-transitions/metadata.json":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Fmetadata.json -->","skills/react-view-transitions/references/css-recipes.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fcss-recipes.md -->","skills/react-view-transitions/references/implementation.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fimplementation.md -->","skills/react-view-transitions/references/nextjs.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fnextjs.md -->","skills/react-view-transitions/references/patterns.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fpatterns.md -->","skills/react-view-transitions/references/troubleshooting.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Ftroubleshooting.md -->","skills/vercel-cli-with-tokens/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-cli-with-tokens%2FSKILL.md -->","skills/vercel-optimize/AGENTS.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FAGENTS.md -->","skills/vercel-optimize/CONTRIBUTING.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FCONTRIBUTING.md -->","skills/vercel-optimize/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FREADME.md -->","skills/vercel-optimize/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FSKILL.md -->","skills/vercel-optimize/metadata.json":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Fmetadata.json -->","skills/vercel-optimize/references/candidates.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fcandidates.md -->","skills/vercel-optimize/references/data-collection.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fdata-collection.md -->","skills/vercel-optimize/references/docs-library.json":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fdocs-library.json -->","skills/vercel-optimize/references/doctrine.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fdoctrine.md -->","skills/vercel-optimize/references/observability-plus.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fobservability-plus.md -->","skills/vercel-optimize/references/playbooks/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2FREADME.md -->","skills/vercel-optimize/references/playbooks/ai-application.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fai-application.md -->","skills/vercel-optimize/references/playbooks/api-service.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fapi-service.md -->","skills/vercel-optimize/references/playbooks/content-site.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fcontent-site.md -->","skills/vercel-optimize/references/playbooks/ecommerce.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fecommerce.md -->","skills/vercel-optimize/references/playbooks/marketing.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fmarketing.md -->","skills/vercel-optimize/references/playbooks/saas.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fsaas.md -->","skills/vercel-optimize/references/playbooks/sveltekit.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fsveltekit.md -->","skills/vercel-optimize/references/recommendations.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Frecommendations.md -->","skills/vercel-optimize/references/scanner-patterns.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fscanner-patterns.md -->","skills/vercel-optimize/references/scoring.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fscoring.md -->","skills/vercel-optimize/references/support-topics/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2FREADME.md -->","skills/vercel-optimize/references/support-topics/astro-edge-middleware-scope.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fastro-edge-middleware-scope.md -->","skills/vercel-optimize/references/support-topics/astro-output-mode-and-isr.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fastro-output-mode-and-isr.md -->","skills/vercel-optimize/references/support-topics/auth-preserving-parallelization.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fauth-preserving-parallelization.md -->","skills/vercel-optimize/references/support-topics/bot-protection-product-guardrails.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fbot-protection-product-guardrails.md -->","skills/vercel-optimize/references/support-topics/build-minutes-monorepo-fanout.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fbuild-minutes-monorepo-fanout.md -->","skills/vercel-optimize/references/support-topics/cache-components-static-shell-boundaries.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcache-components-static-shell-boundaries.md -->","skills/vercel-optimize/references/support-topics/cache-components-suspense-dedupe-pitfall.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcache-components-suspense-dedupe-pitfall.md -->","skills/vercel-optimize/references/support-topics/cdn-cache-auth-safety.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcdn-cache-auth-safety.md -->","skills/vercel-optimize/references/support-topics/cold-start-initialization-bundle.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcold-start-initialization-bundle.md -->","skills/vercel-optimize/references/support-topics/core-web-vitals-client-bottlenecks.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcore-web-vitals-client-bottlenecks.md -->","skills/vercel-optimize/references/support-topics/database-egress-pooling-region.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fdatabase-egress-pooling-region.md -->","skills/vercel-optimize/references/support-topics/dynamic-rendering-traps.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fdynamic-rendering-traps.md -->","skills/vercel-optimize/references/support-topics/external-api-critical-path-platform.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fexternal-api-critical-path-platform.md -->","skills/vercel-optimize/references/support-topics/external-api-critical-path.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fexternal-api-critical-path.md -->","skills/vercel-optimize/references/support-topics/fast-data-transfer-payloads.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffast-data-transfer-payloads.md -->","skills/vercel-optimize/references/support-topics/fluid-compute-caveats.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffluid-compute-caveats.md -->","skills/vercel-optimize/references/support-topics/function-duration-io-and-after.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffunction-duration-io-and-after.md -->","skills/vercel-optimize/references/support-topics/function-invocation-reduction.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffunction-invocation-reduction.md -->","skills/vercel-optimize/references/support-topics/function-region-misconfiguration-ttfb.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffunction-region-misconfiguration-ttfb.md -->","skills/vercel-optimize/references/support-topics/image-optimization-cost-control.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fimage-optimization-cost-control.md -->","skills/vercel-optimize/references/support-topics/isr-revalidation-static-generation.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fisr-revalidation-static-generation.md -->","skills/vercel-optimize/references/support-topics/middleware-proxy-edge-cost.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fmiddleware-proxy-edge-cost.md -->","skills/vercel-optimize/references/support-topics/next-fetch-revalidate-floor.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-fetch-revalidate-floor.md -->","skills/vercel-optimize/references/support-topics/next-font-cls-self-hosting.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-font-cls-self-hosting.md -->","skills/vercel-optimize/references/support-topics/next-heavy-ui-lazy-load-boundaries.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-heavy-ui-lazy-load-boundaries.md -->","skills/vercel-optimize/references/support-topics/next-image-lcp-preload-sizes.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-image-lcp-preload-sizes.md -->","skills/vercel-optimize/references/support-topics/next-route-handler-get-cache-defaults.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-route-handler-get-cache-defaults.md -->","skills/vercel-optimize/references/support-topics/next-script-third-party-strategy.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-script-third-party-strategy.md -->","skills/vercel-optimize/references/support-topics/nextjs-version-cache-semantics.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnextjs-version-cache-semantics.md -->","skills/vercel-optimize/references/support-topics/not-found-catchall-request-waste.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnot-found-catchall-request-waste.md -->","skills/vercel-optimize/references/support-topics/nuxt-route-rules-cache-isr.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnuxt-route-rules-cache-isr.md -->","skills/vercel-optimize/references/support-topics/observability-events-cost-attribution.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fobservability-events-cost-attribution.md -->","skills/vercel-optimize/references/support-topics/post-response-work-waituntil.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fpost-response-work-waituntil.md -->","skills/vercel-optimize/references/support-topics/route-error-durable-offload.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Froute-error-durable-offload.md -->","skills/vercel-optimize/references/support-topics/route-error-runtime-limits.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Froute-error-runtime-limits.md -->","skills/vercel-optimize/references/support-topics/runtime-cache-reusable-data.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fruntime-cache-reusable-data.md -->","skills/vercel-optimize/references/support-topics/sveltekit-isr-prerender-safety.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fsveltekit-isr-prerender-safety.md -->","skills/vercel-optimize/references/support-topics/sveltekit-split-cold-start-tradeoff.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fsveltekit-split-cold-start-tradeoff.md -->","skills/vercel-optimize/references/support-topics/usage-spike-triage.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fusage-spike-triage.md -->","skills/vercel-optimize/references/support-topics/use-cache-date-stamp-isr-write-amplifier.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fuse-cache-date-stamp-isr-write-amplifier.md -->","skills/vercel-optimize/references/support-topics/use-cache-remote-shared-origin-data.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fuse-cache-remote-shared-origin-data.md -->","skills/vercel-optimize/references/support-topics/workflow-resumable-stream-routes.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fworkflow-resumable-stream-routes.md -->","skills/vercel-optimize/references/verification.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fverification.md -->","skills/vercel-optimize/references/voice.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fvoice.md -->","skills/web-design-guidelines/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fweb-design-guidelines%2FSKILL.md -->","skills/writing-guidelines/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fwriting-guidelines%2FSKILL.md -->"},"files":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to AI coding agents (Claude Code, Cursor, Copilot, etc.) when working with code in this repository.\n\n## Repository Overview\n\nA collection of skills for AI coding agents working with Vercel projects. Skills are packaged instructions and scripts that extend agent capabilities.\n\n## Creating a New Skill\n\n### Directory Structure\n\n```\nskills/\n  {skill-name}/           # kebab-case directory name\n    SKILL.md              # Required: skill definition\n    scripts/              # Optional: executable scripts\n      {script-name}.sh    # Bash scripts\n      {script-name}.mjs   # Node scripts\n    references/           # Optional: supporting docs loaded on demand\n    lib/                  # Optional: shared code for scripts\n```\n\n### Naming Conventions\n\n- **Skill directory**: `kebab-case` (e.g., `vercel-deploy`, `log-monitor`)\n- **SKILL.md**: Always uppercase, always this exact filename\n- **Scripts**: `kebab-case.sh` or `kebab-case.mjs` (e.g., `deploy.sh`, `collect-signals.mjs`)\n\n### SKILL.md Format\n\n```markdown\n---\nname: {skill-name}\ndescription: {One sentence describing when to use this skill. Include trigger phrases like \"Deploy my app\", \"Check logs\", etc.}\n---\n\n# {Skill Title}\n\n{Brief description of what the skill does.}\n\n## How It Works\n\n{Numbered list explaining the skill's workflow}\n\n## Usage\n\n```bash\nbash /mnt/skills/user/{skill-name}/scripts/{script}.sh [args]\n```\n\n**Arguments:**\n- `arg1` - Description (defaults to X)\n\n**Examples:**\n{Show 2-3 common usage patterns}\n\n## Output\n\n{Show example output users will see}\n\n## Present Results to User\n\n{Template for how Claude should format results when presenting to users}\n\n## Troubleshooting\n\n{Common issues and solutions, especially network/permissions errors}\n```\n\n### Best Practices for Context Efficiency\n\nSkills are loaded on-demand — only the skill name and description are loaded at startup. The full `SKILL.md` loads into context only when the agent decides the skill is relevant. To minimize context usage:\n\n- **Keep SKILL.md under 500 lines** — put detailed reference material in separate files\n- **Write specific descriptions** — helps the agent know exactly when to activate the skill\n- **Use progressive disclosure** — reference supporting files that get read only when needed\n- **Prefer scripts over inline code** — script execution doesn't consume context (only output does)\n- **File references work one level deep** — link directly from SKILL.md to supporting files\n\n### Script Requirements\n\n- Bash scripts: use `#!/bin/bash` and `set -e`\n- Node scripts: use `#!/usr/bin/env node` and `.mjs`\n- Write status messages to stderr\n- Write machine-readable output (JSON) to stdout\n- Include a cleanup trap for temp files when scripts create them\n- Reference scripts by relative path, for example `node scripts/{script}.mjs`\n\n### End-User Installation\n\nDocument skills.sh installation for public skills:\n\n```bash\nnpx skills add vercel-labs/agent-skills --skill {skill-name}\n```\n\nFor manual installs, document the native path when the skill needs one.\n\n**Claude Code:**\n```bash\ncp -r skills/{skill-name} ~/.claude/skills/\n```\n\n**claude.ai:**\nAdd the skill to project knowledge or paste SKILL.md contents into the conversation.\n\nIf the skill requires network access, instruct users to add required domains at `claude.ai/settings/capabilities`.\n","CLAUDE.md":"AGENTS.md","skills/composition-patterns/AGENTS.md":"# React Composition Patterns\n\n**Version 1.0.0**  \nEngineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React codebases using composition. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n---\n\n## Table of Contents\n\n1. [Component Architecture](#1-component-architecture) — **HIGH**\n   - 1.1 [Avoid Boolean Prop Proliferation](#11-avoid-boolean-prop-proliferation)\n   - 1.2 [Use Compound Components](#12-use-compound-components)\n2. [State Management](#2-state-management) — **MEDIUM**\n   - 2.1 [Decouple State Management from UI](#21-decouple-state-management-from-ui)\n   - 2.2 [Define Generic Context Interfaces for Dependency Injection](#22-define-generic-context-interfaces-for-dependency-injection)\n   - 2.3 [Lift State into Provider Components](#23-lift-state-into-provider-components)\n3. [Implementation Patterns](#3-implementation-patterns) — **MEDIUM**\n   - 3.1 [Create Explicit Component Variants](#31-create-explicit-component-variants)\n   - 3.2 [Prefer Composing Children Over Render Props](#32-prefer-composing-children-over-render-props)\n4. [React 19 APIs](#4-react-19-apis) — **MEDIUM**\n   - 4.1 [React 19 API Changes](#41-react-19-api-changes)\n\n---\n\n## 1. Component Architecture\n\n**Impact: HIGH**\n\nFundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n### 1.1 Avoid Boolean Prop Proliferation\n\n**Impact: CRITICAL (prevents unmaintainable component variants)**\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\n\ncomponent behavior. Each boolean doubles possible states and creates\n\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect: boolean props create exponential complexity**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct: composition eliminates conditionals**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\n\nsharing a single monolithic parent.\n\n### 1.2 Use Compound Components\n\n**Impact: HIGH (enables flexible composition without prop drilling)**\n\nStructure complex components as compound components with a shared context. Each\n\nsubcomponent accesses shared state via context, not props. Consumers compose the\n\npieces they need.\n\n**Incorrect: monolithic component with render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct: compound components with shared context**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n\n---\n\n## 2. State Management\n\n**Impact: MEDIUM**\n\nPatterns for lifting state and managing shared context across\ncomposed components.\n\n### 2.1 Decouple State Management from UI\n\n**Impact: MEDIUM (enables swapping state implementations without changing UI)**\n\nThe provider component should be the only place that knows how state is managed.\n\nUI components consume the context interface—they don't know if state comes from\n\nuseState, Zustand, or a server sync.\n\n**Incorrect: UI coupled to state implementation**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct: state management isolated in provider**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\n\ndepends on the context interface, not the implementation.\n\n### 2.2 Define Generic Context Interfaces for Dependency Injection\n\n**Impact: HIGH (enables dependency-injectable state across use-cases)**\n\nDefine a **generic interface** for your component context with three parts:\n\n`state`, `actions`, and `meta`. This interface is a contract that any provider\n\ncan implement—enabling the same UI components to work with completely different\n\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\n\ndependency-injectable.\n\n**Incorrect: UI coupled to specific state implementation**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct: generic interface enables dependency injection**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe provider boundary is what matters—not the visual nesting. Components that\n\nneed shared state don't have to be inside the `Composer.Frame`. They just need\n\nto be within the provider.\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\n\nbox, but they can still access its state and actions. This is the power of\n\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\n\nby the provider. Swap the provider, keep the UI.\n\n### 2.3 Lift State into Provider Components\n\n**Impact: HIGH (enables state sharing outside component boundaries)**\n\nMove state management into dedicated provider components. This allows sibling\n\ncomponents outside the main UI to access and modify state without prop drilling\n\nor awkward refs.\n\n**Incorrect: state trapped inside component**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect: useEffect to sync state up**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect: reading state from ref on submit**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct: state lifted to provider**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\n\nsubmit action because it's within the provider. Even though it's a one-off\n\ncomponent, it can still access the composer's state and actions from outside the\n\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\n\nnested inside each other—they just need to be within the same provider.\n\n---\n\n## 3. Implementation Patterns\n\n**Impact: MEDIUM**\n\nSpecific techniques for implementing compound components and\ncontext providers.\n\n### 3.1 Create Explicit Component Variants\n\n**Impact: MEDIUM (self-documenting code, no hidden conditionals)**\n\nInstead of one component with many boolean props, create explicit variant\n\ncomponents. Each variant composes the pieces it needs. The code documents\n\nitself.\n\n**Incorrect: one component, many modes**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct: explicit variants**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\n\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n\n- What UI elements it includes\n\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n\n### 3.2 Prefer Composing Children Over Render Props\n\n**Impact: MEDIUM (cleaner composition, better readability)**\n\nUse `children` for composition instead of `renderX` props. Children are more\n\nreadable, compose naturally, and don't require understanding callback\n\nsignatures.\n\n**Incorrect: render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct: compound components with children**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\n\nUse children when composing static structure.\n\n---\n\n## 4. React 19 APIs\n\n**Impact: MEDIUM**\n\nReact 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n\n### 4.1 React 19 API Changes\n\n**Impact: MEDIUM (cleaner component definitions and context usage)**\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect: forwardRef in React 19**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct: ref as a regular prop**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect: useContext in React 19**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct: use instead of useContext**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n\n---\n\n## References\n\n1. [https://react.dev](https://react.dev)\n2. [https://react.dev/learn/passing-data-deeply-with-context](https://react.dev/learn/passing-data-deeply-with-context)\n3. [https://react.dev/reference/react/use](https://react.dev/reference/react/use)\n","skills/composition-patterns/README.md":"# React Composition Patterns\n\nA structured repository for React composition patterns that scale. These\npatterns help avoid boolean prop proliferation by using compound components,\nlifting state, and composing internals.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Rules\n\n### Component Architecture (CRITICAL)\n\n- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize\n  behavior\n- `architecture-compound-components.md` - Structure as compound components with\n  shared context\n\n### State Management (HIGH)\n\n- `state-lift-state.md` - Lift state into provider components\n- `state-context-interface.md` - Define clear context interfaces\n  (state/actions/meta)\n- `state-decouple-implementation.md` - Decouple state management from UI\n\n### Implementation Patterns (MEDIUM)\n\n- `patterns-children-over-render-props.md` - Prefer children over renderX props\n- `patterns-explicit-variants.md` - Create explicit component variants\n\n## Core Principles\n\n1. **Composition over configuration** — Instead of adding props, let consumers\n   compose\n2. **Lift your state** — State in providers, not trapped in components\n3. **Compose your internals** — Subcomponents access context, not props\n4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer\n   with isThread\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `architecture-` for Component Architecture\n   - `state-` for State Management\n   - `patterns-` for Implementation Patterns\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n\n## Impact Levels\n\n- `CRITICAL` - Foundational patterns, prevents unmaintainable code\n- `HIGH` - Significant maintainability improvements\n- `MEDIUM` - Good practices for cleaner code\n","skills/composition-patterns/SKILL.md":"---\nname: vercel-composition-patterns\ndescription:\n  React composition patterns that scale. Use when refactoring components with\n  boolean prop proliferation, building flexible component libraries, or\n  designing reusable APIs. Triggers on tasks involving compound components,\n  render props, context providers, or component architecture. Includes React 19\n  API changes.\nlicense: MIT\nmetadata:\n  author: vercel\n  version: '1.0.0'\n---\n\n# React Composition Patterns\n\nComposition patterns for building flexible, maintainable React components. Avoid\nboolean prop proliferation by using compound components, lifting state, and\ncomposing internals. These patterns make codebases easier for both humans and AI\nagents to work with as they scale.\n\n## When to Apply\n\nReference these guidelines when:\n\n- Refactoring components with many boolean props\n- Building reusable component libraries\n- Designing flexible component APIs\n- Reviewing component architecture\n- Working with compound components or context providers\n\n## Rule Categories by Priority\n\n| Priority | Category                | Impact | Prefix          |\n| -------- | ----------------------- | ------ | --------------- |\n| 1        | Component Architecture  | HIGH   | `architecture-` |\n| 2        | State Management        | MEDIUM | `state-`        |\n| 3        | Implementation Patterns | MEDIUM | `patterns-`     |\n| 4        | React 19 APIs           | MEDIUM | `react19-`      |\n\n## Quick Reference\n\n### 1. Component Architecture (HIGH)\n\n- `architecture-avoid-boolean-props` - Don't add boolean props to customize\n  behavior; use composition\n- `architecture-compound-components` - Structure complex components with shared\n  context\n\n### 2. State Management (MEDIUM)\n\n- `state-decouple-implementation` - Provider is the only place that knows how\n  state is managed\n- `state-context-interface` - Define generic interface with state, actions, meta\n  for dependency injection\n- `state-lift-state` - Move state into provider components for sibling access\n\n### 3. Implementation Patterns (MEDIUM)\n\n- `patterns-explicit-variants` - Create explicit variant components instead of\n  boolean modes\n- `patterns-children-over-render-props` - Use children for composition instead\n  of renderX props\n\n### 4. React 19 APIs (MEDIUM)\n\n> **⚠️ React 19+ only.** Skip this section if using React 18 or earlier.\n\n- `react19-no-forwardref` - Don't use `forwardRef`; use `use()` instead of `useContext()`\n\n## How to Use\n\nRead individual rule files for detailed explanations and code examples:\n\n```\nrules/architecture-avoid-boolean-props.md\nrules/state-context-interface.md\n```\n\nEach rule file contains:\n\n- Brief explanation of why it matters\n- Incorrect code example with explanation\n- Correct code example with explanation\n- Additional context and references\n\n## Full Compiled Document\n\nFor the complete guide with all rules expanded: `AGENTS.md`\n","skills/composition-patterns/metadata.json":"{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://react.dev/learn/passing-data-deeply-with-context\",\n    \"https://react.dev/reference/react/use\"\n  ]\n}\n","skills/composition-patterns/rules/_sections.md":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Component Architecture (architecture)\n\n**Impact:** HIGH  \n**Description:** Fundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n## 2. State Management (state)\n\n**Impact:** MEDIUM  \n**Description:** Patterns for lifting state and managing shared context across\ncomposed components.\n\n## 3. Implementation Patterns (patterns)\n\n**Impact:** MEDIUM  \n**Description:** Specific techniques for implementing compound components and\ncontext providers.\n\n## 4. React 19 APIs (react19)\n\n**Impact:** MEDIUM  \n**Description:** React 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n","skills/composition-patterns/rules/_template.md":"---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: brief description of impact\ntags: composition, components\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect:**\n\n```tsx\n// Bad code example\n```\n\n**Correct:**\n\n```tsx\n// Good code example\n```\n\nReference: [Link](https://example.com)\n","skills/composition-patterns/rules/architecture-avoid-boolean-props.md":"---\ntitle: Avoid Boolean Prop Proliferation\nimpact: CRITICAL\nimpactDescription: prevents unmaintainable component variants\ntags: composition, props, architecture\n---\n\n## Avoid Boolean Prop Proliferation\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\ncomponent behavior. Each boolean doubles possible states and creates\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect (boolean props create exponential complexity):**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct (composition eliminates conditionals):**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\nsharing a single monolithic parent.\n","skills/composition-patterns/rules/architecture-compound-components.md":"---\ntitle: Use Compound Components\nimpact: HIGH\nimpactDescription: enables flexible composition without prop drilling\ntags: composition, compound-components, architecture\n---\n\n## Use Compound Components\n\nStructure complex components as compound components with a shared context. Each\nsubcomponent accesses shared state via context, not props. Consumers compose the\npieces they need.\n\n**Incorrect (monolithic component with render props):**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct (compound components with shared context):**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n","skills/composition-patterns/rules/patterns-children-over-render-props.md":"---\ntitle: Prefer Composing Children Over Render Props\nimpact: MEDIUM\nimpactDescription: cleaner composition, better readability\ntags: composition, children, render-props\n---\n\n## Prefer Children Over Render Props\n\nUse `children` for composition instead of `renderX` props. Children are more\nreadable, compose naturally, and don't require understanding callback\nsignatures.\n\n**Incorrect (render props):**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct (compound components with children):**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\nUse children when composing static structure.\n","skills/composition-patterns/rules/patterns-explicit-variants.md":"---\ntitle: Create Explicit Component Variants\nimpact: MEDIUM\nimpactDescription: self-documenting code, no hidden conditionals\ntags: composition, variants, architecture\n---\n\n## Create Explicit Component Variants\n\nInstead of one component with many boolean props, create explicit variant\ncomponents. Each variant composes the pieces it needs. The code documents\nitself.\n\n**Incorrect (one component, many modes):**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct (explicit variants):**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n- What UI elements it includes\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n","skills/composition-patterns/rules/react19-no-forwardref.md":"---\ntitle: React 19 API Changes\nimpact: MEDIUM\nimpactDescription: cleaner component definitions and context usage\ntags: react19, refs, context, hooks\n---\n\n## React 19 API Changes\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect (forwardRef in React 19):**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct (ref as a regular prop):**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect (useContext in React 19):**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct (use instead of useContext):**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n","skills/composition-patterns/rules/state-context-interface.md":"---\ntitle: Define Generic Context Interfaces for Dependency Injection\nimpact: HIGH\nimpactDescription: enables dependency-injectable state across use-cases\ntags: composition, context, state, typescript, dependency-injection\n---\n\n## Define Generic Context Interfaces for Dependency Injection\n\nDefine a **generic interface** for your component context with three parts:\n`state`, `actions`, and `meta`. This interface is a contract that any provider\ncan implement—enabling the same UI components to work with completely different\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\ndependency-injectable.\n\n**Incorrect (UI coupled to specific state implementation):**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct (generic interface enables dependency injection):**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\nThe provider boundary is what matters—not the visual nesting. Components that\nneed shared state don't have to be inside the `Composer.Frame`. They just need\nto be within the provider.\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\nbox, but they can still access its state and actions. This is the power of\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\nby the provider. Swap the provider, keep the UI.\n","skills/composition-patterns/rules/state-decouple-implementation.md":"---\ntitle: Decouple State Management from UI\nimpact: MEDIUM\nimpactDescription: enables swapping state implementations without changing UI\ntags: composition, state, architecture\n---\n\n## Decouple State Management from UI\n\nThe provider component should be the only place that knows how state is managed.\nUI components consume the context interface—they don't know if state comes from\nuseState, Zustand, or a server sync.\n\n**Incorrect (UI coupled to state implementation):**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct (state management isolated in provider):**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\ndepends on the context interface, not the implementation.\n","skills/composition-patterns/rules/state-lift-state.md":"---\ntitle: Lift State into Provider Components\nimpact: HIGH\nimpactDescription: enables state sharing outside component boundaries\ntags: composition, state, context, providers\n---\n\n## Lift State into Provider Components\n\nMove state management into dedicated provider components. This allows sibling\ncomponents outside the main UI to access and modify state without prop drilling\nor awkward refs.\n\n**Incorrect (state trapped inside component):**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect (useEffect to sync state up):**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect (reading state from ref on submit):**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct (state lifted to provider):**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\nsubmit action because it's within the provider. Even though it's a one-off\ncomponent, it can still access the composer's state and actions from outside the\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\nnested inside each other—they just need to be within the same provider.\n","skills/deploy-to-vercel/SKILL.md":"---\nname: deploy-to-vercel\ndescription: Deploy applications and websites to Vercel. Use when the user requests deployment actions like \"deploy my app\", \"deploy and give me the link\", \"push this live\", or \"create a preview deployment\".\nmetadata:\n  author: vercel\n  version: \"3.0.0\"\n---\n\n# Deploy to Vercel\n\nDeploy any project to Vercel. **Always deploy as preview** (not production) unless the user explicitly asks for production.\n\nThe goal is to get the user into the best long-term setup: their project linked to Vercel with git-push deploys. Every method below tries to move the user closer to that state.\n\n## Step 1: Gather Project State\n\nRun all four checks before deciding which method to use:\n\n```bash\n# 1. Check for a git remote\ngit remote get-url origin 2>/dev/null\n\n# 2. Check if locally linked to a Vercel project (either file means linked)\ncat .vercel/project.json 2>/dev/null || cat .vercel/repo.json 2>/dev/null\n\n# 3. Check if the Vercel CLI is installed and authenticated\nvercel whoami 2>/dev/null\n\n# 4. List available teams (if authenticated)\nvercel teams list --format json 2>/dev/null\n```\n\n### Team selection\n\nIf the user belongs to multiple teams, present all available team slugs as a bulleted list and ask which one to deploy to. Once the user picks a team, proceed immediately to the next step — do not ask for additional confirmation.\n\nPass the team slug via `--scope` on all subsequent CLI commands (`vercel deploy`, `vercel link`, `vercel inspect`, etc.):\n\n```bash\nvercel deploy [path] -y --no-wait --scope <team-slug>\n```\n\nIf the project is already linked (`.vercel/project.json` or `.vercel/repo.json` exists), the `orgId` in those files determines the team — no need to ask again. If there is only one team (or just a personal account), skip the prompt and use it directly.\n\n**About the `.vercel/` directory:** A linked project has either:\n- `.vercel/project.json` — created by `vercel link` (single project linking). Contains `projectId` and `orgId`.\n- `.vercel/repo.json` — created by `vercel link --repo` (repo-based linking). Contains `orgId`, `remoteName`, and a `projects` array mapping directories to Vercel project IDs.\n\nEither file means the project is linked. Check for both.\n\n**Do NOT** use `vercel project inspect`, `vercel ls`, or `vercel link` to detect state in an unlinked directory — without a `.vercel/` config, they will interactively prompt (or with `--yes`, silently link as a side-effect). Only `vercel whoami` is safe to run anywhere.\n\n## Step 2: Choose a Deploy Method\n\n### Linked (`.vercel/` exists) + has git remote → Git Push\n\nThis is the ideal state. The project is linked and has git integration.\n\n1. **Ask the user before pushing.** Never push without explicit approval:\n   ```\n   This project is connected to Vercel via git. I can commit and push to\n   trigger a deployment. Want me to proceed?\n   ```\n\n2. **Commit and push:**\n   ```bash\n   git add .\n   git commit -m \"deploy: <description of changes>\"\n   git push\n   ```\n   Vercel automatically builds from the push. Non-production branches get preview deployments; the production branch (usually `main`) gets a production deployment.\n\n3. **Retrieve the preview URL.** If the CLI is authenticated:\n   ```bash\n   sleep 5\n   vercel ls --format json\n   ```\n   The JSON output has a `deployments` array. Find the latest entry — its `url` field is the preview URL.\n\n   If the CLI is not authenticated, tell the user to check the Vercel dashboard or the commit status checks on their git provider for the preview URL.\n\n---\n\n### Linked (`.vercel/` exists) + no git remote → `vercel deploy`\n\nThe project is linked but there's no git repo. Deploy directly with the CLI.\n\n```bash\nvercel deploy [path] -y --no-wait\n```\n\nUse `--no-wait` so the CLI returns immediately with the deployment URL instead of blocking until the build finishes (builds can take a while). Then check on the deployment status with:\n\n```bash\nvercel inspect <deployment-url>\n```\n\nFor production deploys (only if user explicitly asks):\n```bash\nvercel deploy [path] --prod -y --no-wait\n```\n\n---\n\n### Not linked + CLI is authenticated → Link first, then deploy\n\nThe CLI is working but the project isn't linked yet. This is the opportunity to get the user into the best state.\n\n1. **Ask the user which team to deploy to.** Present the team slugs from Step 1 as a bulleted list. If there's only one team (or just a personal account), skip this step.\n\n2. **Once a team is selected, proceed directly to linking.** Tell the user what will happen but do not ask for separate confirmation:\n   ```\n   Linking this project to <team name> on Vercel. This will create a Vercel\n   project to deploy to and enable automatic deployments on future git pushes.\n   ```\n\n3. **If a git remote exists**, use repo-based linking with the selected team scope:\n   ```bash\n   vercel link --repo --scope <team-slug>\n   ```\n   This reads the git remote URL and matches it to existing Vercel projects that deploy from that repo. It creates `.vercel/repo.json`. This is much more reliable than `vercel link` (without `--repo`), which tries to match by directory name and often fails when the local folder and Vercel project are named differently.\n\n   **If there is no git remote**, fall back to standard linking:\n   ```bash\n   vercel link --scope <team-slug>\n   ```\n   This prompts the user to select or create a project. It creates `.vercel/project.json`.\n\n4. **Then deploy using the best available method:**\n   - If a git remote exists → commit and push (see git push method above)\n   - If no git remote → `vercel deploy [path] -y --no-wait --scope <team-slug>`, then `vercel inspect <url>` to check status\n\n---\n\n### Not linked + CLI not authenticated → Install, auth, link, deploy\n\nThe Vercel CLI isn't set up at all.\n\n1. **Install the CLI (if not already installed):**\n   ```bash\n   npm install -g vercel\n   ```\n\n2. **Authenticate:**\n   ```bash\n   vercel login\n   ```\n   The user completes auth in their browser. If running in a non-interactive environment where login is not possible, skip to the **no-auth fallback** below.\n\n3. **Ask which team to deploy to** — present team slugs from `vercel teams list --format json` as a bulleted list. If only one team / personal account, skip. Once selected, proceed immediately.\n\n4. **Link the project** with the selected team scope (use `--repo` if a git remote exists, plain `vercel link` otherwise):\n   ```bash\n   vercel link --repo --scope <team-slug>   # if git remote exists\n   vercel link --scope <team-slug>          # if no git remote\n   ```\n\n5. **Deploy** using the best available method (git push if remote exists, otherwise `vercel deploy -y --no-wait --scope <team-slug>`, then `vercel inspect <url>` to check status).\n\n---\n\n### No-Auth Fallback — claude.ai sandbox\n\n**When to use:** Last resort when the CLI can't be installed or authenticated in the claude.ai sandbox. This requires no authentication — it returns a **Preview URL** (live site) and a **Claim URL** (transfer to your Vercel account).\n\n```bash\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh [path]\n```\n\n**Arguments:**\n- `path` - Directory to deploy, or a `.tgz` file (defaults to current directory)\n\n**Examples:**\n```bash\n# Deploy current directory\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh\n\n# Deploy specific project\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh /path/to/project\n\n# Deploy existing tarball\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh /path/to/project.tgz\n```\n\nThe script auto-detects the framework from `package.json`, packages the project (excluding `node_modules`, `.git`, `.env`), uploads it, and waits for the build to complete.\n\n**Tell the user:** \"Your deployment is ready at [previewUrl]. Claim it at [claimUrl] to manage your deployment.\"\n\n---\n\n### No-Auth Fallback — Codex sandbox\n\n**When to use:** In the Codex sandbox where the CLI may not be authenticated. Codex runs in a sandboxed environment by default — try the CLI first, and fall back to the deploy script if auth fails.\n\n1. **Check whether the Vercel CLI is installed** (no escalation needed for this check):\n   ```bash\n   command -v vercel\n   ```\n\n2. **If `vercel` is installed**, try deploying with the CLI:\n   ```bash\n   vercel deploy [path] -y --no-wait\n   ```\n\n3. **If `vercel` is not installed, or the CLI fails with \"No existing credentials found\"**, use the fallback script:\n   ```bash\n   skill_dir=\"<path-to-skill>\"\n\n   # Deploy current directory\n   bash \"$skill_dir/resources/deploy-codex.sh\"\n\n   # Deploy specific project\n   bash \"$skill_dir/resources/deploy-codex.sh\" /path/to/project\n\n   # Deploy existing tarball\n   bash \"$skill_dir/resources/deploy-codex.sh\" /path/to/project.tgz\n   ```\n\nThe script handles framework detection, packaging, and deployment. It waits for the build to complete and returns JSON with `previewUrl` and `claimUrl`.\n\n**Tell the user:** \"Your deployment is ready at [previewUrl]. Claim it at [claimUrl] to manage your deployment.\"\n\n**Escalated network access:** Only escalate the actual deploy command if sandboxing blocks the network call (`sandbox_permissions=require_escalated`). Do **not** escalate the `command -v vercel` check.\n\n---\n\n## Agent-Specific Notes\n\n### Claude Code / terminal-based agents\n\nYou have full shell access. Do NOT use the `/mnt/skills/` path. Follow the decision flow above using the CLI directly.\n\nFor the no-auth fallback, run the deploy script from the skill's installed location:\n```bash\nbash ~/.claude/skills/deploy-to-vercel/resources/deploy.sh [path]\n```\nThe path may vary depending on where the user installed the skill.\n\n### Sandboxed environments (claude.ai)\n\nYou likely cannot run `vercel login` or `git push`. Go directly to the **no-auth fallback — claude.ai sandbox**.\n\n### Codex\n\nCodex runs in a sandbox. Check if the CLI is available first, then fall back to the deploy script. Go to the **no-auth fallback — Codex sandbox**.\n\n---\n\n## Output\n\nAlways show the user the deployment URL.\n\n- **Git push:** Use `vercel ls --format json` to find the preview URL. If the CLI isn't authenticated, tell the user to check the Vercel dashboard or commit status checks.\n- **CLI deploy:** Show the URL returned by `vercel deploy --no-wait`. Use `vercel inspect <url>` to check build status and report it to the user.\n- **No-auth fallback:** Show both the preview URL and the claim URL:\n  ```\n  Deployment successful!\n\n  Preview URL: https://my-app-abc123.vercel.app\n  Claim URL:   https://vercel.com/claim-deployment?code=...\n\n  View your site at the Preview URL.\n  To transfer this deployment to your Vercel account, visit the Claim URL.\n  ```\n\n**Do not** curl or fetch the deployed URL to verify it works. Just return the link.\n\n---\n\n## Troubleshooting\n\n### Network Egress Error (claude.ai)\n\nIf deployment fails due to network restrictions on claude.ai, tell the user:\n\n```\nDeployment failed due to network restrictions. To fix this:\n\n1. Go to https://claude.ai/settings/capabilities\n2. Add *.vercel.com to the allowed domains\n3. Try deploying again\n```\n\n### Escalated Network Access (Codex / sandboxed environments)\n\nIf deployment fails due to network issues (timeouts, DNS errors, connection resets) in a sandboxed environment, rerun with escalated permissions (`sandbox_permissions=require_escalated`). Do not escalate the initial CLI availability check — only escalate the actual deploy command.\n\nExample guidance to the user:\n```\nThe deploy needs escalated network access to deploy to Vercel. I can rerun\nthe command with escalated permissions — want me to proceed?\n```\n\n### CLI Auth Failure\n\nIf `vercel login` or `vercel deploy` fails with authentication errors, fall back to the no-auth deploy script (claude.ai or Codex variant, depending on the environment).\n","skills/react-best-practices/AGENTS.md":"# React Best Practices\n\n**Version 1.0.0**  \nVercel Engineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React and Next.js codebases. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\n\n---\n\n## Table of Contents\n\n1. [Eliminating Waterfalls](#1-eliminating-waterfalls) — **CRITICAL**\n   - 1.1 [Check Cheap Conditions Before Async Flags](#11-check-cheap-conditions-before-async-flags)\n   - 1.2 [Defer Await Until Needed](#12-defer-await-until-needed)\n   - 1.3 [Dependency-Based Parallelization](#13-dependency-based-parallelization)\n   - 1.4 [Prevent Waterfall Chains in API Routes](#14-prevent-waterfall-chains-in-api-routes)\n   - 1.5 [Promise.all() for Independent Operations](#15-promiseall-for-independent-operations)\n   - 1.6 [Strategic Suspense Boundaries](#16-strategic-suspense-boundaries)\n2. [Bundle Size Optimization](#2-bundle-size-optimization) — **CRITICAL**\n   - 2.1 [Avoid Barrel File Imports](#21-avoid-barrel-file-imports)\n   - 2.2 [Conditional Module Loading](#22-conditional-module-loading)\n   - 2.3 [Defer Non-Critical Third-Party Libraries](#23-defer-non-critical-third-party-libraries)\n   - 2.4 [Dynamic Imports for Heavy Components](#24-dynamic-imports-for-heavy-components)\n   - 2.5 [Prefer Statically Analyzable Paths](#25-prefer-statically-analyzable-paths)\n   - 2.6 [Preload Based on User Intent](#26-preload-based-on-user-intent)\n3. [Server-Side Performance](#3-server-side-performance) — **HIGH**\n   - 3.1 [Authenticate Server Actions Like API Routes](#31-authenticate-server-actions-like-api-routes)\n   - 3.2 [Avoid Duplicate Serialization in RSC Props](#32-avoid-duplicate-serialization-in-rsc-props)\n   - 3.3 [Avoid Shared Module State for Request Data](#33-avoid-shared-module-state-for-request-data)\n   - 3.4 [Cross-Request LRU Caching](#34-cross-request-lru-caching)\n   - 3.5 [Hoist Static I/O to Module Level](#35-hoist-static-io-to-module-level)\n   - 3.6 [Minimize Serialization at RSC Boundaries](#36-minimize-serialization-at-rsc-boundaries)\n   - 3.7 [Parallel Data Fetching with Component Composition](#37-parallel-data-fetching-with-component-composition)\n   - 3.8 [Parallel Nested Data Fetching](#38-parallel-nested-data-fetching)\n   - 3.9 [Per-Request Deduplication with React.cache()](#39-per-request-deduplication-with-reactcache)\n   - 3.10 [Use after() for Non-Blocking Operations](#310-use-after-for-non-blocking-operations)\n4. [Client-Side Data Fetching](#4-client-side-data-fetching) — **MEDIUM-HIGH**\n   - 4.1 [Deduplicate Global Event Listeners](#41-deduplicate-global-event-listeners)\n   - 4.2 [Use Passive Event Listeners for Scrolling Performance](#42-use-passive-event-listeners-for-scrolling-performance)\n   - 4.3 [Use SWR for Automatic Deduplication](#43-use-swr-for-automatic-deduplication)\n   - 4.4 [Version and Minimize localStorage Data](#44-version-and-minimize-localstorage-data)\n5. [Re-render Optimization](#5-re-render-optimization) — **MEDIUM**\n   - 5.1 [Calculate Derived State During Rendering](#51-calculate-derived-state-during-rendering)\n   - 5.2 [Defer State Reads to Usage Point](#52-defer-state-reads-to-usage-point)\n   - 5.3 [Do not wrap a simple expression with a primitive result type in useMemo](#53-do-not-wrap-a-simple-expression-with-a-primitive-result-type-in-usememo)\n   - 5.4 [Don't Define Components Inside Components](#54-dont-define-components-inside-components)\n   - 5.5 [Extract Default Non-primitive Parameter Value from Memoized Component to Constant](#55-extract-default-non-primitive-parameter-value-from-memoized-component-to-constant)\n   - 5.6 [Extract to Memoized Components](#56-extract-to-memoized-components)\n   - 5.7 [Narrow Effect Dependencies](#57-narrow-effect-dependencies)\n   - 5.8 [Put Interaction Logic in Event Handlers](#58-put-interaction-logic-in-event-handlers)\n   - 5.9 [Split Combined Hook Computations](#59-split-combined-hook-computations)\n   - 5.10 [Subscribe to Derived State](#510-subscribe-to-derived-state)\n   - 5.11 [Use Functional setState Updates](#511-use-functional-setstate-updates)\n   - 5.12 [Use Lazy State Initialization](#512-use-lazy-state-initialization)\n   - 5.13 [Use Transitions for Non-Urgent Updates](#513-use-transitions-for-non-urgent-updates)\n   - 5.14 [Use useDeferredValue for Expensive Derived Renders](#514-use-usedeferredvalue-for-expensive-derived-renders)\n   - 5.15 [Use useRef for Transient Values](#515-use-useref-for-transient-values)\n6. [Rendering Performance](#6-rendering-performance) — **MEDIUM**\n   - 6.1 [Animate SVG Wrapper Instead of SVG Element](#61-animate-svg-wrapper-instead-of-svg-element)\n   - 6.2 [CSS content-visibility for Long Lists](#62-css-content-visibility-for-long-lists)\n   - 6.3 [Hoist Static JSX Elements](#63-hoist-static-jsx-elements)\n   - 6.4 [Optimize SVG Precision](#64-optimize-svg-precision)\n   - 6.5 [Prevent Hydration Mismatch Without Flickering](#65-prevent-hydration-mismatch-without-flickering)\n   - 6.6 [Suppress Expected Hydration Mismatches](#66-suppress-expected-hydration-mismatches)\n   - 6.7 [Use Activity Component for Show/Hide](#67-use-activity-component-for-showhide)\n   - 6.8 [Use defer or async on Script Tags](#68-use-defer-or-async-on-script-tags)\n   - 6.9 [Use Explicit Conditional Rendering](#69-use-explicit-conditional-rendering)\n   - 6.10 [Use React DOM Resource Hints](#610-use-react-dom-resource-hints)\n   - 6.11 [Use useTransition Over Manual Loading States](#611-use-usetransition-over-manual-loading-states)\n7. [JavaScript Performance](#7-javascript-performance) — **LOW-MEDIUM**\n   - 7.1 [Avoid Layout Thrashing](#71-avoid-layout-thrashing)\n   - 7.2 [Build Index Maps for Repeated Lookups](#72-build-index-maps-for-repeated-lookups)\n   - 7.3 [Cache Property Access in Loops](#73-cache-property-access-in-loops)\n   - 7.4 [Cache Repeated Function Calls](#74-cache-repeated-function-calls)\n   - 7.5 [Cache Storage API Calls](#75-cache-storage-api-calls)\n   - 7.6 [Combine Multiple Array Iterations](#76-combine-multiple-array-iterations)\n   - 7.7 [Defer Non-Critical Work with requestIdleCallback](#77-defer-non-critical-work-with-requestidlecallback)\n   - 7.8 [Early Length Check for Array Comparisons](#78-early-length-check-for-array-comparisons)\n   - 7.9 [Early Return from Functions](#79-early-return-from-functions)\n   - 7.10 [Hoist RegExp Creation](#710-hoist-regexp-creation)\n   - 7.11 [Use flatMap to Map and Filter in One Pass](#711-use-flatmap-to-map-and-filter-in-one-pass)\n   - 7.12 [Use Loop for Min/Max Instead of Sort](#712-use-loop-for-minmax-instead-of-sort)\n   - 7.13 [Use Set/Map for O(1) Lookups](#713-use-setmap-for-o1-lookups)\n   - 7.14 [Use toSorted() Instead of sort() for Immutability](#714-use-tosorted-instead-of-sort-for-immutability)\n8. [Advanced Patterns](#8-advanced-patterns) — **LOW**\n   - 8.1 [Do Not Put Effect Events in Dependency Arrays](#81-do-not-put-effect-events-in-dependency-arrays)\n   - 8.2 [Initialize App Once, Not Per Mount](#82-initialize-app-once-not-per-mount)\n   - 8.3 [Store Event Handlers in Refs](#83-store-event-handlers-in-refs)\n   - 8.4 [useEffectEvent for Stable Callback Refs](#84-useeffectevent-for-stable-callback-refs)\n\n---\n\n## 1. Eliminating Waterfalls\n\n**Impact: CRITICAL**\n\nWaterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.\n\n### 1.1 Check Cheap Conditions Before Async Flags\n\n**Impact: HIGH (avoids unnecessary async work when a synchronous guard already fails)**\n\nWhen a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n\nThis is a specialization of [Defer Await Until Needed](./async-defer-await.md) for `flag && cheapCondition` style checks.\n\n**Incorrect:**\n\n```typescript\nconst someFlag = await getFlag()\n\nif (someFlag && someCondition) {\n  // ...\n}\n```\n\n**Correct:**\n\n```typescript\nif (someCondition) {\n  const someFlag = await getFlag()\n  if (someFlag) {\n    // ...\n  }\n}\n```\n\nThis matters when `getFlag` hits the network, a feature-flag service, or `React.cache` / DB work: skipping it when `someCondition` is false removes that cost on the cold path.\n\nKeep the original order if `someCondition` is expensive, depends on the flag, or you must run side effects in a fixed order.\n\n### 1.2 Defer Await Until Needed\n\n**Impact: HIGH (avoids blocking unused code paths)**\n\nMove `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n\n**Incorrect: blocks both branches**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  const userData = await fetchUserData(userId)\n  \n  if (skipProcessing) {\n    // Returns immediately but still waited for userData\n    return { skipped: true }\n  }\n  \n  // Only this branch uses userData\n  return processUserData(userData)\n}\n```\n\n**Correct: only blocks when needed**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  if (skipProcessing) {\n    // Returns immediately without waiting\n    return { skipped: true }\n  }\n  \n  // Fetch only when needed\n  const userData = await fetchUserData(userId)\n  return processUserData(userData)\n}\n```\n\n**Another example: early return optimization**\n\n```typescript\n// Incorrect: always fetches permissions\nasync function updateResource(resourceId: string, userId: string) {\n  const permissions = await fetchPermissions(userId)\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n\n// Correct: fetches only when needed\nasync function updateResource(resourceId: string, userId: string) {\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  const permissions = await fetchPermissions(userId)\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n```\n\nThis optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.\n\nFor `await getFlag()` combined with a cheap synchronous guard (`flag && someCondition`), see [Check Cheap Conditions Before Async Flags](./async-cheap-condition-before-await.md).\n\n### 1.3 Dependency-Based Parallelization\n\n**Impact: CRITICAL (2-10× improvement)**\n\nFor operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n\n**Incorrect: profile waits for config unnecessarily**\n\n```typescript\nconst [user, config] = await Promise.all([\n  fetchUser(),\n  fetchConfig()\n])\nconst profile = await fetchProfile(user.id)\n```\n\n**Correct: config and profile run in parallel**\n\n```typescript\nimport { all } from 'better-all'\n\nconst { user, config, profile } = await all({\n  async user() { return fetchUser() },\n  async config() { return fetchConfig() },\n  async profile() {\n    return fetchProfile((await this.$.user).id)\n  }\n})\n```\n\n**Alternative without extra dependencies:**\n\n```typescript\nconst userPromise = fetchUser()\nconst profilePromise = userPromise.then(user => fetchProfile(user.id))\n\nconst [user, config, profile] = await Promise.all([\n  userPromise,\n  fetchConfig(),\n  profilePromise\n])\n```\n\nWe can also create all the promises first, and do `Promise.all()` at the end.\n\nReference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)\n\n### 1.4 Prevent Waterfall Chains in API Routes\n\n**Impact: CRITICAL (2-10× improvement)**\n\nIn API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n\n**Incorrect: config waits for auth, data waits for both**\n\n```typescript\nexport async function GET(request: Request) {\n  const session = await auth()\n  const config = await fetchConfig()\n  const data = await fetchData(session.user.id)\n  return Response.json({ data, config })\n}\n```\n\n**Correct: auth and config start immediately**\n\n```typescript\nexport async function GET(request: Request) {\n  const sessionPromise = auth()\n  const configPromise = fetchConfig()\n  const session = await sessionPromise\n  const [config, data] = await Promise.all([\n    configPromise,\n    fetchData(session.user.id)\n  ])\n  return Response.json({ data, config })\n}\n```\n\nFor operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).\n\n### 1.5 Promise.all() for Independent Operations\n\n**Impact: CRITICAL (2-10× improvement)**\n\nWhen async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n\n**Incorrect: sequential execution, 3 round trips**\n\n```typescript\nconst user = await fetchUser()\nconst posts = await fetchPosts()\nconst comments = await fetchComments()\n```\n\n**Correct: parallel execution, 1 round trip**\n\n```typescript\nconst [user, posts, comments] = await Promise.all([\n  fetchUser(),\n  fetchPosts(),\n  fetchComments()\n])\n```\n\n### 1.6 Strategic Suspense Boundaries\n\n**Impact: HIGH (faster initial paint)**\n\nInstead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n\n**Incorrect: wrapper blocked by data fetching**\n\n```tsx\nasync function Page() {\n  const data = await fetchData() // Blocks entire page\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <DataDisplay data={data} />\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n```\n\nThe entire layout waits for data even though only the middle section needs it.\n\n**Correct: wrapper shows immediately, data streams in**\n\n```tsx\nfunction Page() {\n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <Suspense fallback={<Skeleton />}>\n          <DataDisplay />\n        </Suspense>\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nasync function DataDisplay() {\n  const data = await fetchData() // Only blocks this component\n  return <div>{data.content}</div>\n}\n```\n\nSidebar, Header, and Footer render immediately. Only DataDisplay waits for data.\n\n**Alternative: share promise across components**\n\n```tsx\nfunction Page() {\n  // Start fetch immediately, but don't await\n  const dataPromise = fetchData()\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <Suspense fallback={<Skeleton />}>\n        <DataDisplay dataPromise={dataPromise} />\n        <DataSummary dataPromise={dataPromise} />\n      </Suspense>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nfunction DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Unwraps the promise\n  return <div>{data.content}</div>\n}\n\nfunction DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Reuses the same promise\n  return <div>{data.summary}</div>\n}\n```\n\nBoth components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.\n\n**When NOT to use this pattern:**\n\n- Critical data needed for layout decisions (affects positioning)\n\n- SEO-critical content above the fold\n\n- Small, fast queries where suspense overhead isn't worth it\n\n- When you want to avoid layout shift (loading → content jump)\n\n**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.\n\n---\n\n## 2. Bundle Size Optimization\n\n**Impact: CRITICAL**\n\nReducing initial bundle size improves Time to Interactive and Largest Contentful Paint.\n\n### 2.1 Avoid Barrel File Imports\n\n**Impact: CRITICAL (200-800ms import cost, slow builds)**\n\nImport directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n\nPopular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.\n\n**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.\n\n**Incorrect: imports entire library**\n\n```tsx\nimport { Check, X, Menu } from 'lucide-react'\n// Loads 1,583 modules, takes ~2.8s extra in dev\n// Runtime cost: 200-800ms on every cold start\n\nimport { Button, TextField } from '@mui/material'\n// Loads 2,225 modules, takes ~4.2s extra in dev\n```\n\n**Correct - Next.js 13.5+ (recommended):**\n\n```tsx\n// Keep the standard imports - Next.js transforms them to direct imports\nimport { Check, X, Menu } from 'lucide-react'\n// Full TypeScript support, no manual path wrangling\n```\n\nThis is the recommended approach because it preserves TypeScript type safety and editor autocompletion while still eliminating the barrel import cost.\n\n**Correct - Direct imports (non-Next.js projects):**\n\n```tsx\nimport Button from '@mui/material/Button'\nimport TextField from '@mui/material/TextField'\n// Loads only what you use\n```\n\n> **TypeScript warning:** Some libraries (notably `lucide-react`) don't ship `.d.ts` files for their deep import paths. Importing from `lucide-react/dist/esm/icons/check` resolves to an implicit `any` type, causing errors under `strict` or `noImplicitAny`. Prefer `optimizePackageImports` when available, or verify the library exports types for its subpaths before using direct imports.\n\nThese optimizations provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.\n\nLibraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.\n\nReference: [https://vercel.com/blog/how-we-optimized-package-imports-in-next-js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)\n\n### 2.2 Conditional Module Loading\n\n**Impact: HIGH (loads large data only when needed)**\n\nLoad large data or modules only when a feature is activated.\n\n**Example: lazy-load animation frames**\n\n```tsx\nfunction AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {\n  const [frames, setFrames] = useState<Frame[] | null>(null)\n\n  useEffect(() => {\n    if (enabled && !frames && typeof window !== 'undefined') {\n      import('./animation-frames.js')\n        .then(mod => setFrames(mod.frames))\n        .catch(() => setEnabled(false))\n    }\n  }, [enabled, frames, setEnabled])\n\n  if (!frames) return <Skeleton />\n  return <Canvas frames={frames} />\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.\n\n### 2.3 Defer Non-Critical Third-Party Libraries\n\n**Impact: MEDIUM (loads after hydration)**\n\nAnalytics, logging, and error tracking don't block user interaction. Load them after hydration.\n\n**Incorrect: blocks initial bundle**\n\n```tsx\nimport { Analytics } from '@vercel/analytics/react'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n**Correct: loads after hydration**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst Analytics = dynamic(\n  () => import('@vercel/analytics/react').then(m => m.Analytics),\n  { ssr: false }\n)\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n### 2.4 Dynamic Imports for Heavy Components\n\n**Impact: CRITICAL (directly affects TTI and LCP)**\n\nUse `next/dynamic` to lazy-load large components not needed on initial render.\n\n**Incorrect: Monaco bundles with main chunk ~300KB**\n\n```tsx\nimport { MonacoEditor } from './monaco-editor'\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n**Correct: Monaco loads on demand**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst MonacoEditor = dynamic(\n  () => import('./monaco-editor').then(m => m.MonacoEditor),\n  { ssr: false }\n)\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n### 2.5 Prefer Statically Analyzable Paths\n\n**Impact: HIGH (avoids accidental broad bundles and file traces)**\n\nBuild tools work best when import and file-system paths are obvious at build time. If you hide the real path inside a variable or compose it too dynamically, the tool either has to include a broad set of possible files, warn that it cannot analyze the import, or widen file tracing to stay safe.\n\nPrefer explicit maps or literal paths so the set of reachable files stays narrow and predictable. This is the same rule whether you are choosing modules with `import()` or reading files in server/build code.\n\nWhen analysis becomes too broad, the cost is real:\n\n- Larger server bundles\n\n- Slower builds\n\n- Worse cold starts\n\n- More memory use\n\n**Incorrect: the bundler cannot tell what may be imported**\n\n```ts\nconst PAGE_MODULES = {\n  home: './pages/home',\n  settings: './pages/settings',\n} as const\n\nconst Page = await import(PAGE_MODULES[pageName])\n```\n\n**Correct: use an explicit map of allowed modules**\n\n```ts\nconst PAGE_MODULES = {\n  home: () => import('./pages/home'),\n  settings: () => import('./pages/settings'),\n} as const\n\nconst Page = await PAGE_MODULES[pageName]()\n```\n\n**Incorrect: a 2-value enum still hides the final path from static analysis**\n\n```ts\nconst baseDir = path.join(process.cwd(), 'content/' + contentKind)\n```\n\n**Correct: make each final path literal at the callsite**\n\n```ts\nconst baseDir =\n  kind === ContentKind.Blog\n    ? path.join(process.cwd(), 'content/blog')\n    : path.join(process.cwd(), 'content/docs')\n```\n\nIn Next.js server code, this matters for output file tracing too. `path.join(process.cwd(), someVar)` can widen the traced file set because Next.js statically analyze `import`, `require`, and `fs` usage.\n\nReference: [https://nextjs.org/docs/app/api-reference/config/next-config-js/output](https://nextjs.org/docs/app/api-reference/config/next-config-js/output), [https://nextjs.org/learn/seo/dynamic-imports](https://nextjs.org/learn/seo/dynamic-imports), [https://vite.dev/guide/features.html](https://vite.dev/guide/features.html), [https://esbuild.github.io/api/](https://esbuild.github.io/api/), [https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars](https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars), [https://webpack.js.org/guides/dependency-management/](https://webpack.js.org/guides/dependency-management/)\n\n### 2.6 Preload Based on User Intent\n\n**Impact: MEDIUM (reduces perceived latency)**\n\nPreload heavy bundles before they're needed to reduce perceived latency.\n\n**Example: preload on hover/focus**\n\n```tsx\nfunction EditorButton({ onClick }: { onClick: () => void }) {\n  const preload = () => {\n    if (typeof window !== 'undefined') {\n      void import('./monaco-editor')\n    }\n  }\n\n  return (\n    <button\n      onMouseEnter={preload}\n      onFocus={preload}\n      onClick={onClick}\n    >\n      Open Editor\n    </button>\n  )\n}\n```\n\n**Example: preload when feature flag is enabled**\n\n```tsx\nfunction FlagsProvider({ children, flags }: Props) {\n  useEffect(() => {\n    if (flags.editorEnabled && typeof window !== 'undefined') {\n      void import('./monaco-editor').then(mod => mod.init())\n    }\n  }, [flags.editorEnabled])\n\n  return <FlagsContext.Provider value={flags}>\n    {children}\n  </FlagsContext.Provider>\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.\n\n---\n\n## 3. Server-Side Performance\n\n**Impact: HIGH**\n\nOptimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.\n\n### 3.1 Authenticate Server Actions Like API Routes\n\n**Impact: CRITICAL (prevents unauthorized access to server mutations)**\n\nServer Actions (functions with `\"use server\"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly.\n\nNext.js documentation explicitly states: \"Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation.\"\n\n**Incorrect: no authentication check**\n\n```typescript\n'use server'\n\nexport async function deleteUser(userId: string) {\n  // Anyone can call this! No auth check\n  await db.user.delete({ where: { id: userId } })\n  return { success: true }\n}\n```\n\n**Correct: authentication inside the action**\n\n```typescript\n'use server'\n\nimport { verifySession } from '@/lib/auth'\nimport { unauthorized } from '@/lib/errors'\n\nexport async function deleteUser(userId: string) {\n  // Always check auth inside the action\n  const session = await verifySession()\n  \n  if (!session) {\n    throw unauthorized('Must be logged in')\n  }\n  \n  // Check authorization too\n  if (session.user.role !== 'admin' && session.user.id !== userId) {\n    throw unauthorized('Cannot delete other users')\n  }\n  \n  await db.user.delete({ where: { id: userId } })\n  return { success: true }\n}\n```\n\n**With input validation:**\n\n```typescript\n'use server'\n\nimport { verifySession } from '@/lib/auth'\nimport { z } from 'zod'\n\nconst updateProfileSchema = z.object({\n  userId: z.string().uuid(),\n  name: z.string().min(1).max(100),\n  email: z.string().email()\n})\n\nexport async function updateProfile(data: unknown) {\n  // Validate input first\n  const validated = updateProfileSchema.parse(data)\n  \n  // Then authenticate\n  const session = await verifySession()\n  if (!session) {\n    throw new Error('Unauthorized')\n  }\n  \n  // Then authorize\n  if (session.user.id !== validated.userId) {\n    throw new Error('Can only update own profile')\n  }\n  \n  // Finally perform the mutation\n  await db.user.update({\n    where: { id: validated.userId },\n    data: {\n      name: validated.name,\n      email: validated.email\n    }\n  })\n  \n  return { success: true }\n}\n```\n\nReference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)\n\n### 3.2 Avoid Duplicate Serialization in RSC Props\n\n**Impact: LOW (reduces network payload by avoiding duplicate serialization)**\n\nRSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again. Do transformations (`.toSorted()`, `.filter()`, `.map()`) in client, not server.\n\n**Incorrect: duplicates array**\n\n```tsx\n// RSC: sends 6 strings (2 arrays × 3 items)\n<ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />\n```\n\n**Correct: sends 3 strings**\n\n```tsx\n// RSC: send once\n<ClientList usernames={usernames} />\n\n// Client: transform there\n'use client'\nconst sorted = useMemo(() => [...usernames].sort(), [usernames])\n```\n\n**Nested deduplication behavior:**\n\n```tsx\n// string[] - duplicates everything\nusernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings\n\n// object[] - duplicates array structure only\nusers={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4)\n```\n\nDeduplication works recursively. Impact varies by data type:\n\n- `string[]`, `number[]`, `boolean[]`: **HIGH impact** - array + all primitives fully duplicated\n\n- `object[]`: **LOW impact** - array duplicated, but nested objects deduplicated by reference\n\n**Operations breaking deduplication: create new references**\n\n- Arrays: `.toSorted()`, `.filter()`, `.map()`, `.slice()`, `[...arr]`\n\n- Objects: `{...obj}`, `Object.assign()`, `structuredClone()`, `JSON.parse(JSON.stringify())`\n\n**More examples:**\n\n```tsx\n// ❌ Bad\n<C users={users} active={users.filter(u => u.active)} />\n<C product={product} productName={product.name} />\n\n// ✅ Good\n<C users={users} />\n<C product={product} />\n// Do filtering/destructuring in client\n```\n\n**Exception:** Pass derived data when transformation is expensive or client doesn't need original.\n\n### 3.3 Avoid Shared Module State for Request Data\n\n**Impact: HIGH (prevents concurrency bugs and request data leaks)**\n\nFor React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race conditions, cross-request contamination, and security bugs where one user's data appears in another user's response.\n\nTreat module scope on the server as process-wide shared memory, not request-local state.\n\n**Incorrect: request data leaks across concurrent renders**\n\n```tsx\nlet currentUser: User | null = null\n\nexport default async function Page() {\n  currentUser = await auth()\n  return <Dashboard />\n}\n\nasync function Dashboard() {\n  return <div>{currentUser?.name}</div>\n}\n```\n\nIf two requests overlap, request A can set `currentUser`, then request B overwrites it before request A finishes rendering `Dashboard`.\n\n**Correct: keep request data local to the render tree**\n\n```tsx\nexport default async function Page() {\n  const user = await auth()\n  return <Dashboard user={user} />\n}\n\nfunction Dashboard({ user }: { user: User | null }) {\n  return <div>{user?.name}</div>\n}\n```\n\nSafe exceptions:\n\n- Immutable static assets or config loaded once at module scope\n\n- Shared caches intentionally designed for cross-request reuse and keyed correctly\n\n- Process-wide singletons that do not store request- or user-specific mutable data\n\nFor static assets and config, see [Hoist Static I/O to Module Level](./server-hoist-static-io.md).\n\n### 3.4 Cross-Request LRU Caching\n\n**Impact: HIGH (caches across requests)**\n\n`React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.\n\n**Implementation:**\n\n```typescript\nimport { LRUCache } from 'lru-cache'\n\nconst cache = new LRUCache<string, any>({\n  max: 1000,\n  ttl: 5 * 60 * 1000  // 5 minutes\n})\n\nexport async function getUser(id: string) {\n  const cached = cache.get(id)\n  if (cached) return cached\n\n  const user = await db.user.findUnique({ where: { id } })\n  cache.set(id, user)\n  return user\n}\n\n// Request 1: DB query, result cached\n// Request 2: cache hit, no DB query\n```\n\nUse when sequential user actions hit multiple endpoints needing the same data within seconds.\n\n**With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis.\n\n**In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching.\n\nReference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)\n\n### 3.5 Hoist Static I/O to Module Level\n\n**Impact: HIGH (avoids repeated file/network I/O per request)**\n\nWhen loading static assets (fonts, logos, images, config files) in route handlers or server functions, hoist the I/O operation to module level. Module-level code runs once when the module is first imported, not on every request. This eliminates redundant file system reads or network fetches that would otherwise run on every invocation.\n\n**Incorrect: reads font file on every request**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\n\nexport async function GET(request: Request) {\n  // Runs on EVERY request - expensive!\n  const fontData = await fetch(\n    new URL('./fonts/Inter.ttf', import.meta.url)\n  ).then(res => res.arrayBuffer())\n\n  const logoData = await fetch(\n    new URL('./images/logo.png', import.meta.url)\n  ).then(res => res.arrayBuffer())\n\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logoData} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: fontData }] }\n  )\n}\n```\n\n**Correct: loads once at module initialization**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\n\n// Module-level: runs ONCE when module is first imported\nconst fontData = fetch(\n  new URL('./fonts/Inter.ttf', import.meta.url)\n).then(res => res.arrayBuffer())\n\nconst logoData = fetch(\n  new URL('./images/logo.png', import.meta.url)\n).then(res => res.arrayBuffer())\n\nexport async function GET(request: Request) {\n  // Await the already-started promises\n  const [font, logo] = await Promise.all([fontData, logoData])\n\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logo} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: font }] }\n  )\n}\n```\n\n**Correct: synchronous fs at module level**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\nimport { readFileSync } from 'fs'\nimport { join } from 'path'\n\n// Synchronous read at module level - blocks only during module init\nconst fontData = readFileSync(\n  join(process.cwd(), 'public/fonts/Inter.ttf')\n)\n\nconst logoData = readFileSync(\n  join(process.cwd(), 'public/images/logo.png')\n)\n\nexport async function GET(request: Request) {\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logoData} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: fontData }] }\n  )\n}\n```\n\n**Incorrect: reads config on every call**\n\n```typescript\nimport fs from 'node:fs/promises'\n\nexport async function processRequest(data: Data) {\n  const config = JSON.parse(\n    await fs.readFile('./config.json', 'utf-8')\n  )\n  const template = await fs.readFile('./template.html', 'utf-8')\n\n  return render(template, data, config)\n}\n```\n\n**Correct: hoists config and template to module level**\n\n```typescript\nimport fs from 'node:fs/promises'\n\nconst configPromise = fs\n  .readFile('./config.json', 'utf-8')\n  .then(JSON.parse)\nconst templatePromise = fs.readFile('./template.html', 'utf-8')\n\nexport async function processRequest(data: Data) {\n  const [config, template] = await Promise.all([\n    configPromise,\n    templatePromise,\n  ])\n\n  return render(template, data, config)\n}\n```\n\nWhen to use this pattern:\n\n- Loading fonts for OG image generation\n\n- Loading static logos, icons, or watermarks\n\n- Reading configuration files that don't change at runtime\n\n- Loading email templates or other static templates\n\n- Any static asset that's the same across all requests\n\nWhen not to use this pattern:\n\n- Assets that vary per request or user\n\n- Files that may change during runtime (use caching with TTL instead)\n\n- Large files that would consume too much memory if kept loaded\n\n- Sensitive data that shouldn't persist in memory\n\nWith Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute), module-level caching is especially effective because multiple concurrent requests share the same function instance. The static assets stay loaded in memory across requests without cold start penalties.\n\nIn traditional serverless, each cold start re-executes module-level code, but subsequent warm invocations reuse the loaded assets until the instance is recycled.\n\n### 3.6 Minimize Serialization at RSC Boundaries\n\n**Impact: HIGH (reduces data transfer size)**\n\nThe React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.\n\n**Incorrect: serializes all 50 fields**\n\n```tsx\nasync function Page() {\n  const user = await fetchUser()  // 50 fields\n  return <Profile user={user} />\n}\n\n'use client'\nfunction Profile({ user }: { user: User }) {\n  return <div>{user.name}</div>  // uses 1 field\n}\n```\n\n**Correct: serializes only 1 field**\n\n```tsx\nasync function Page() {\n  const user = await fetchUser()\n  return <Profile name={user.name} />\n}\n\n'use client'\nfunction Profile({ name }: { name: string }) {\n  return <div>{name}</div>\n}\n```\n\n### 3.7 Parallel Data Fetching with Component Composition\n\n**Impact: CRITICAL (eliminates server-side waterfalls)**\n\nReact Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.\n\n**Incorrect: Sidebar waits for Page's fetch to complete**\n\n```tsx\nexport default async function Page() {\n  const header = await fetchHeader()\n  return (\n    <div>\n      <div>{header}</div>\n      <Sidebar />\n    </div>\n  )\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n```\n\n**Correct: both fetch simultaneously**\n\n```tsx\nasync function Header() {\n  const data = await fetchHeader()\n  return <div>{data}</div>\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n\nexport default function Page() {\n  return (\n    <div>\n      <Header />\n      <Sidebar />\n    </div>\n  )\n}\n```\n\n**Alternative with children prop:**\n\n```tsx\nasync function Header() {\n  const data = await fetchHeader()\n  return <div>{data}</div>\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n\nfunction Layout({ children }: { children: ReactNode }) {\n  return (\n    <div>\n      <Header />\n      {children}\n    </div>\n  )\n}\n\nexport default function Page() {\n  return (\n    <Layout>\n      <Sidebar />\n    </Layout>\n  )\n}\n```\n\n### 3.8 Parallel Nested Data Fetching\n\n**Impact: CRITICAL (eliminates server-side waterfalls)**\n\nWhen fetching nested data in parallel, chain dependent fetches within each item's promise so a slow item doesn't block the rest.\n\n**Incorrect: a single slow item blocks all nested fetches**\n\n```tsx\nconst chats = await Promise.all(\n  chatIds.map(id => getChat(id))\n)\n\nconst chatAuthors = await Promise.all(\n  chats.map(chat => getUser(chat.author))\n)\n```\n\nIf one `getChat(id)` out of 100 is extremely slow, the authors of the other 99 chats can't start loading even though their data is ready.\n\n**Correct: each item chains its own nested fetch**\n\n```tsx\nconst chatAuthors = await Promise.all(\n  chatIds.map(id => getChat(id).then(chat => getUser(chat.author)))\n)\n```\n\nEach item independently chains `getChat` → `getUser`, so a slow chat doesn't block author fetches for the others.\n\n### 3.9 Per-Request Deduplication with React.cache()\n\n**Impact: MEDIUM (deduplicates within request)**\n\nUse `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.\n\n**Usage:**\n\n```typescript\nimport { cache } from 'react'\n\nexport const getCurrentUser = cache(async () => {\n  const session = await auth()\n  if (!session?.user?.id) return null\n  return await db.user.findUnique({\n    where: { id: session.user.id }\n  })\n})\n```\n\nWithin a single request, multiple calls to `getCurrentUser()` execute the query only once.\n\n**Avoid inline objects as arguments:**\n\n`React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits.\n\n**Incorrect: always cache miss**\n\n```typescript\nconst getUser = cache(async (params: { uid: number }) => {\n  return await db.user.findUnique({ where: { id: params.uid } })\n})\n\n// Each call creates new object, never hits cache\ngetUser({ uid: 1 })\ngetUser({ uid: 1 })  // Cache miss, runs query again\n```\n\n**Correct: cache hit**\n\n```typescript\nconst params = { uid: 1 }\ngetUser(params)  // Query runs\ngetUser(params)  // Cache hit (same reference)\n```\n\nIf you must pass objects, pass the same reference:\n\n**Next.js-Specific Note:**\n\nIn Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks:\n\n- Database queries (Prisma, Drizzle, etc.)\n\n- Heavy computations\n\n- Authentication checks\n\n- File system operations\n\n- Any non-fetch async work\n\nUse `React.cache()` to deduplicate these operations across your component tree.\n\nReference: [https://react.dev/reference/react/cache](https://react.dev/reference/react/cache)\n\n### 3.10 Use after() for Non-Blocking Operations\n\n**Impact: MEDIUM (faster response times)**\n\nUse Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.\n\n**Incorrect: blocks response**\n\n```tsx\nimport { logUserAction } from '@/app/utils'\n\nexport async function POST(request: Request) {\n  // Perform mutation\n  await updateDatabase(request)\n  \n  // Logging blocks the response\n  const userAgent = request.headers.get('user-agent') || 'unknown'\n  await logUserAction({ userAgent })\n  \n  return new Response(JSON.stringify({ status: 'success' }), {\n    status: 200,\n    headers: { 'Content-Type': 'application/json' }\n  })\n}\n```\n\n**Correct: non-blocking**\n\n```tsx\nimport { after } from 'next/server'\nimport { headers, cookies } from 'next/headers'\nimport { logUserAction } from '@/app/utils'\n\nexport async function POST(request: Request) {\n  // Perform mutation\n  await updateDatabase(request)\n  \n  // Log after response is sent\n  after(async () => {\n    const userAgent = (await headers()).get('user-agent') || 'unknown'\n    const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous'\n    \n    logUserAction({ sessionCookie, userAgent })\n  })\n  \n  return new Response(JSON.stringify({ status: 'success' }), {\n    status: 200,\n    headers: { 'Content-Type': 'application/json' }\n  })\n}\n```\n\nThe response is sent immediately while logging happens in the background.\n\n**Common use cases:**\n\n- Analytics tracking\n\n- Audit logging\n\n- Sending notifications\n\n- Cache invalidation\n\n- Cleanup tasks\n\n**Important notes:**\n\n- `after()` runs even if the response fails or redirects\n\n- Works in Server Actions, Route Handlers, and Server Components\n\nReference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)\n\n---\n\n## 4. Client-Side Data Fetching\n\n**Impact: MEDIUM-HIGH**\n\nAutomatic deduplication and efficient data fetching patterns reduce redundant network requests.\n\n### 4.1 Deduplicate Global Event Listeners\n\n**Impact: LOW (single listener for N components)**\n\nUse `useSWRSubscription()` to share global event listeners across component instances.\n\n**Incorrect: N instances = N listeners**\n\n```tsx\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  useEffect(() => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && e.key === key) {\n        callback()\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  }, [key, callback])\n}\n```\n\nWhen using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.\n\n**Correct: N instances = 1 listener**\n\n```tsx\nimport useSWRSubscription from 'swr/subscription'\n\n// Module-level Map to track callbacks per key\nconst keyCallbacks = new Map<string, Set<() => void>>()\n\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  // Register this callback in the Map\n  useEffect(() => {\n    if (!keyCallbacks.has(key)) {\n      keyCallbacks.set(key, new Set())\n    }\n    keyCallbacks.get(key)!.add(callback)\n\n    return () => {\n      const set = keyCallbacks.get(key)\n      if (set) {\n        set.delete(callback)\n        if (set.size === 0) {\n          keyCallbacks.delete(key)\n        }\n      }\n    }\n  }, [key, callback])\n\n  useSWRSubscription('global-keydown', () => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && keyCallbacks.has(e.key)) {\n        keyCallbacks.get(e.key)!.forEach(cb => cb())\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  })\n}\n\nfunction Profile() {\n  // Multiple shortcuts will share the same listener\n  useKeyboardShortcut('p', () => { /* ... */ }) \n  useKeyboardShortcut('k', () => { /* ... */ })\n  // ...\n}\n```\n\n### 4.2 Use Passive Event Listeners for Scrolling Performance\n\n**Impact: MEDIUM (eliminates scroll delay caused by event listeners)**\n\nAdd `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n\n**Incorrect:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch)\n  document.addEventListener('wheel', handleWheel)\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Correct:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch, { passive: true })\n  document.addEventListener('wheel', handleWheel, { passive: true })\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.\n\n**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.\n\n### 4.3 Use SWR for Automatic Deduplication\n\n**Impact: MEDIUM-HIGH (automatic deduplication)**\n\nSWR enables request deduplication, caching, and revalidation across component instances.\n\n**Incorrect: no deduplication, each instance fetches**\n\n```tsx\nfunction UserList() {\n  const [users, setUsers] = useState([])\n  useEffect(() => {\n    fetch('/api/users')\n      .then(r => r.json())\n      .then(setUsers)\n  }, [])\n}\n```\n\n**Correct: multiple instances share one request**\n\n```tsx\nimport useSWR from 'swr'\n\nfunction UserList() {\n  const { data: users } = useSWR('/api/users', fetcher)\n}\n```\n\n**For immutable data:**\n\n```tsx\nimport { useImmutableSWR } from '@/lib/swr'\n\nfunction StaticContent() {\n  const { data } = useImmutableSWR('/api/config', fetcher)\n}\n```\n\n**For mutations:**\n\n```tsx\nimport { useSWRMutation } from 'swr/mutation'\n\nfunction UpdateButton() {\n  const { trigger } = useSWRMutation('/api/user', updateUser)\n  return <button onClick={() => trigger()}>Update</button>\n}\n```\n\nReference: [https://swr.vercel.app](https://swr.vercel.app)\n\n### 4.4 Version and Minimize localStorage Data\n\n**Impact: MEDIUM (prevents schema conflicts, reduces storage size)**\n\nAdd version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n\n**Incorrect:**\n\n```typescript\n// No version, stores everything, no error handling\nlocalStorage.setItem('userConfig', JSON.stringify(fullUserObject))\nconst data = localStorage.getItem('userConfig')\n```\n\n**Correct:**\n\n```typescript\nconst VERSION = 'v2'\n\nfunction saveConfig(config: { theme: string; language: string }) {\n  try {\n    localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))\n  } catch {\n    // Throws in incognito/private browsing, quota exceeded, or disabled\n  }\n}\n\nfunction loadConfig() {\n  try {\n    const data = localStorage.getItem(`userConfig:${VERSION}`)\n    return data ? JSON.parse(data) : null\n  } catch {\n    return null\n  }\n}\n\n// Migration from v1 to v2\nfunction migrate() {\n  try {\n    const v1 = localStorage.getItem('userConfig:v1')\n    if (v1) {\n      const old = JSON.parse(v1)\n      saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })\n      localStorage.removeItem('userConfig:v1')\n    }\n  } catch {}\n}\n```\n\n**Store minimal fields from server responses:**\n\n```typescript\n// User object has 20+ fields, only store what UI needs\nfunction cachePrefs(user: FullUser) {\n  try {\n    localStorage.setItem('prefs:v1', JSON.stringify({\n      theme: user.preferences.theme,\n      notifications: user.preferences.notifications\n    }))\n  } catch {}\n}\n```\n\n**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.\n\n**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.\n\n---\n\n## 5. Re-render Optimization\n\n**Impact: MEDIUM**\n\nReducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.\n\n### 5.1 Calculate Derived State During Rendering\n\n**Impact: MEDIUM (avoids redundant renders and state drift)**\n\nIf a value can be computed from current props/state, do\n\n<!-- [TRUNCATED AT 50KB] -->","skills/react-best-practices/README.md":"# React Best Practices\n\nA structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `src/` - Build scripts and utilities\n- `metadata.json` - Document metadata (version, organization, abstract)\n- __`AGENTS.md`__ - Compiled output (generated)\n- __`test-cases.json`__ - Test cases for LLM evaluation (generated)\n\n## Getting Started\n\n1. Install dependencies:\n   ```bash\n   pnpm install\n   ```\n\n2. Build AGENTS.md from rules:\n   ```bash\n   pnpm build\n   ```\n\n3. Validate rule files:\n   ```bash\n   pnpm validate\n   ```\n\n4. Extract test cases:\n   ```bash\n   pnpm extract-tests\n   ```\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `async-` for Eliminating Waterfalls (Section 1)\n   - `bundle-` for Bundle Size Optimization (Section 2)\n   - `server-` for Server-Side Performance (Section 3)\n   - `client-` for Client-Side Data Fetching (Section 4)\n   - `rerender-` for Re-render Optimization (Section 5)\n   - `rendering-` for Rendering Performance (Section 6)\n   - `js-` for JavaScript Performance (Section 7)\n   - `advanced-` for Advanced Patterns (Section 8)\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json\n\n## Rule File Structure\n\nEach rule file should follow this structure:\n\n```markdown\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description\ntags: tag1, tag2, tag3\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example\n```\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example\n```\n\nOptional explanatory text after examples.\n\nReference: [Link](https://example.com)\n\n## File Naming Convention\n\n- Files starting with `_` are special (excluded from build)\n- Rule files: `area-description.md` (e.g., `async-parallel.md`)\n- Section is automatically inferred from filename prefix\n- Rules are sorted alphabetically by title within each section\n- IDs (e.g., 1.1, 1.2) are auto-generated during build\n\n## Impact Levels\n\n- `CRITICAL` - Highest priority, major performance gains\n- `HIGH` - Significant performance improvements\n- `MEDIUM-HIGH` - Moderate-high gains\n- `MEDIUM` - Moderate performance improvements\n- `LOW-MEDIUM` - Low-medium gains\n- `LOW` - Incremental improvements\n\n## Scripts\n\n- `pnpm build` - Compile rules into AGENTS.md\n- `pnpm validate` - Validate all rule files\n- `pnpm extract-tests` - Extract test cases for LLM evaluation\n- `pnpm dev` - Build and validate\n\n## Contributing\n\nWhen adding or modifying rules:\n\n1. Use the correct filename prefix for your section\n2. Follow the `_template.md` structure\n3. Include clear bad/good examples with explanations\n4. Add appropriate tags\n5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json\n6. Rules are automatically sorted by title - no need to manage numbers!\n\n## Acknowledgments\n\nOriginally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).\n","skills/react-best-practices/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2FSKILL.md -->","skills/react-best-practices/metadata.json":"{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Vercel Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://nextjs.org\",\n    \"https://swr.vercel.app\",\n    \"https://github.com/shuding/better-all\",\n    \"https://github.com/isaacs/node-lru-cache\",\n    \"https://vercel.com/blog/how-we-optimized-package-imports-in-next-js\",\n    \"https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast\"\n  ]\n}\n","skills/react-best-practices/rules/_sections.md":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Eliminating Waterfalls (async)\n\n**Impact:** CRITICAL  \n**Description:** Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.\n\n## 2. Bundle Size Optimization (bundle)\n\n**Impact:** CRITICAL  \n**Description:** Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.\n\n## 3. Server-Side Performance (server)\n\n**Impact:** HIGH  \n**Description:** Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.\n\n## 4. Client-Side Data Fetching (client)\n\n**Impact:** MEDIUM-HIGH  \n**Description:** Automatic deduplication and efficient data fetching patterns reduce redundant network requests.\n\n## 5. Re-render Optimization (rerender)\n\n**Impact:** MEDIUM  \n**Description:** Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.\n\n## 6. Rendering Performance (rendering)\n\n**Impact:** MEDIUM  \n**Description:** Optimizing the rendering process reduces the work the browser needs to do.\n\n## 7. JavaScript Performance (js)\n\n**Impact:** LOW-MEDIUM  \n**Description:** Micro-optimizations for hot paths can add up to meaningful improvements.\n\n## 8. Advanced Patterns (advanced)\n\n**Impact:** LOW  \n**Description:** Advanced patterns for specific cases that require careful implementation.\n","skills/react-best-practices/rules/_template.md":"---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description of impact (e.g., \"20-50% improvement\")\ntags: tag1, tag2\n---\n\n## Rule Title Here\n\n**Impact: MEDIUM (optional impact description)**\n\nBrief explanation of the rule and why it matters. This should be clear and concise, explaining the performance implications.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example here\nconst bad = example()\n```\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example here\nconst good = example()\n```\n\nReference: [Link to documentation or resource](https://example.com)\n","skills/react-best-practices/rules/advanced-effect-event-deps.md":"---\ntitle: Do Not Put Effect Events in Dependency Arrays\nimpact: LOW\nimpactDescription: avoids unnecessary effect re-runs and lint errors\ntags: advanced, hooks, useEffectEvent, dependencies, effects\n---\n\n## Do Not Put Effect Events in Dependency Arrays\n\nEffect Event functions do not have a stable identity. Their identity intentionally changes on every render. Do not include the function returned by `useEffectEvent` in a `useEffect` dependency array. Keep the actual reactive values as dependencies and call the Effect Event from inside the effect body or subscriptions created by that effect.\n\n**Incorrect (Effect Event added as a dependency):**\n\n```tsx\nimport { useEffect, useEffectEvent } from 'react'\n\nfunction ChatRoom({ roomId, onConnected }: {\n  roomId: string\n  onConnected: () => void\n}) {\n  const handleConnected = useEffectEvent(onConnected)\n\n  useEffect(() => {\n    const connection = createConnection(roomId)\n    connection.on('connected', handleConnected)\n    connection.connect()\n\n    return () => connection.disconnect()\n  }, [roomId, handleConnected])\n}\n```\n\nIncluding the Effect Event in dependencies makes the effect re-run every render and triggers the React Hooks lint rule.\n\n**Correct (depend on reactive values, not the Effect Event):**\n\n```tsx\nimport { useEffect, useEffectEvent } from 'react'\n\nfunction ChatRoom({ roomId, onConnected }: {\n  roomId: string\n  onConnected: () => void\n}) {\n  const handleConnected = useEffectEvent(onConnected)\n\n  useEffect(() => {\n    const connection = createConnection(roomId)\n    connection.on('connected', handleConnected)\n    connection.connect()\n\n    return () => connection.disconnect()\n  }, [roomId])\n}\n```\n\nReference: [React useEffectEvent: Effect Event in deps](https://react.dev/reference/react/useEffectEvent#effect-event-in-deps)\n","skills/react-best-practices/rules/advanced-event-handler-refs.md":"---\ntitle: Store Event Handlers in Refs\nimpact: LOW\nimpactDescription: stable subscriptions\ntags: advanced, hooks, refs, event-handlers, optimization\n---\n\n## Store Event Handlers in Refs\n\nStore callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.\n\n**Incorrect (re-subscribes on every render):**\n\n```tsx\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  useEffect(() => {\n    window.addEventListener(event, handler)\n    return () => window.removeEventListener(event, handler)\n  }, [event, handler])\n}\n```\n\n**Correct (stable subscription):**\n\n```tsx\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  const handlerRef = useRef(handler)\n  useEffect(() => {\n    handlerRef.current = handler\n  }, [handler])\n\n  useEffect(() => {\n    const listener = (e) => handlerRef.current(e)\n    window.addEventListener(event, listener)\n    return () => window.removeEventListener(event, listener)\n  }, [event])\n}\n```\n\n**Alternative: use `useEffectEvent` if you're on latest React:**\n\n```tsx\nimport { useEffectEvent } from 'react'\n\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  const onEvent = useEffectEvent(handler)\n\n  useEffect(() => {\n    window.addEventListener(event, onEvent)\n    return () => window.removeEventListener(event, onEvent)\n  }, [event])\n}\n```\n\n`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.\n","skills/react-best-practices/rules/advanced-init-once.md":"---\ntitle: Initialize App Once, Not Per Mount\nimpact: LOW-MEDIUM\nimpactDescription: avoids duplicate init in development\ntags: initialization, useEffect, app-startup, side-effects\n---\n\n## Initialize App Once, Not Per Mount\n\nDo not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.\n\n**Incorrect (runs twice in dev, re-runs on remount):**\n\n```tsx\nfunction Comp() {\n  useEffect(() => {\n    loadFromStorage()\n    checkAuthToken()\n  }, [])\n\n  // ...\n}\n```\n\n**Correct (once per app load):**\n\n```tsx\nlet didInit = false\n\nfunction Comp() {\n  useEffect(() => {\n    if (didInit) return\n    didInit = true\n    loadFromStorage()\n    checkAuthToken()\n  }, [])\n\n  // ...\n}\n```\n\nReference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)\n","skills/react-best-practices/rules/advanced-use-latest.md":"---\ntitle: useEffectEvent for Stable Callback Refs\nimpact: LOW\nimpactDescription: prevents effect re-runs\ntags: advanced, hooks, useEffectEvent, refs, optimization\n---\n\n## useEffectEvent for Stable Callback Refs\n\nAccess latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.\n\n**Incorrect (effect re-runs on every callback change):**\n\n```tsx\nfunction SearchInput({ onSearch }: { onSearch: (q: string) => void }) {\n  const [query, setQuery] = useState('')\n\n  useEffect(() => {\n    const timeout = setTimeout(() => onSearch(query), 300)\n    return () => clearTimeout(timeout)\n  }, [query, onSearch])\n}\n```\n\n**Correct (using React's useEffectEvent):**\n\n```tsx\nimport { useEffectEvent } from 'react';\n\nfunction SearchInput({ onSearch }: { onSearch: (q: string) => void }) {\n  const [query, setQuery] = useState('')\n  const onSearchEvent = useEffectEvent(onSearch)\n\n  useEffect(() => {\n    const timeout = setTimeout(() => onSearchEvent(query), 300)\n    return () => clearTimeout(timeout)\n  }, [query])\n}\n```\n","skills/react-best-practices/rules/async-api-routes.md":"---\ntitle: Prevent Waterfall Chains in API Routes\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: api-routes, server-actions, waterfalls, parallelization\n---\n\n## Prevent Waterfall Chains in API Routes\n\nIn API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n\n**Incorrect (config waits for auth, data waits for both):**\n\n```typescript\nexport async function GET(request: Request) {\n  const session = await auth()\n  const config = await fetchConfig()\n  const data = await fetchData(session.user.id)\n  return Response.json({ data, config })\n}\n```\n\n**Correct (auth and config start immediately):**\n\n```typescript\nexport async function GET(request: Request) {\n  const sessionPromise = auth()\n  const configPromise = fetchConfig()\n  const session = await sessionPromise\n  const [config, data] = await Promise.all([\n    configPromise,\n    fetchData(session.user.id)\n  ])\n  return Response.json({ data, config })\n}\n```\n\nFor operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).\n","skills/react-best-practices/rules/async-cheap-condition-before-await.md":"---\ntitle: Check Cheap Conditions Before Async Flags\nimpact: HIGH\nimpactDescription: avoids unnecessary async work when a synchronous guard already fails\ntags: async, await, feature-flags, short-circuit, conditional\n---\n\n## Check Cheap Conditions Before Async Flags\n\nWhen a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n\nThis is a specialization of [Defer Await Until Needed](./async-defer-await.md) for `flag && cheapCondition` style checks.\n\n**Incorrect:**\n\n```typescript\nconst someFlag = await getFlag()\n\nif (someFlag && someCondition) {\n  // ...\n}\n```\n\n**Correct:**\n\n```typescript\nif (someCondition) {\n  const someFlag = await getFlag()\n  if (someFlag) {\n    // ...\n  }\n}\n```\n\nThis matters when `getFlag` hits the network, a feature-flag service, or `React.cache` / DB work: skipping it when `someCondition` is false removes that cost on the cold path.\n\nKeep the original order if `someCondition` is expensive, depends on the flag, or you must run side effects in a fixed order.\n","skills/react-best-practices/rules/async-defer-await.md":"---\ntitle: Defer Await Until Needed\nimpact: HIGH\nimpactDescription: avoids blocking unused code paths\ntags: async, await, conditional, optimization\n---\n\n## Defer Await Until Needed\n\nMove `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n\n**Incorrect (blocks both branches):**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  const userData = await fetchUserData(userId)\n  \n  if (skipProcessing) {\n    // Returns immediately but still waited for userData\n    return { skipped: true }\n  }\n  \n  // Only this branch uses userData\n  return processUserData(userData)\n}\n```\n\n**Correct (only blocks when needed):**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  if (skipProcessing) {\n    // Returns immediately without waiting\n    return { skipped: true }\n  }\n  \n  // Fetch only when needed\n  const userData = await fetchUserData(userId)\n  return processUserData(userData)\n}\n```\n\n**Another example (early return optimization):**\n\n```typescript\n// Incorrect: always fetches permissions\nasync function updateResource(resourceId: string, userId: string) {\n  const permissions = await fetchPermissions(userId)\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n\n// Correct: fetches only when needed\nasync function updateResource(resourceId: string, userId: string) {\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  const permissions = await fetchPermissions(userId)\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n```\n\nThis optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.\n\nFor `await getFlag()` combined with a cheap synchronous guard (`flag && someCondition`), see [Check Cheap Conditions Before Async Flags](./async-cheap-condition-before-await.md).\n","skills/react-best-practices/rules/async-dependencies.md":"---\ntitle: Dependency-Based Parallelization\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: async, parallelization, dependencies, better-all\n---\n\n## Dependency-Based Parallelization\n\nFor operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n\n**Incorrect (profile waits for config unnecessarily):**\n\n```typescript\nconst [user, config] = await Promise.all([\n  fetchUser(),\n  fetchConfig()\n])\nconst profile = await fetchProfile(user.id)\n```\n\n**Correct (config and profile run in parallel):**\n\n```typescript\nimport { all } from 'better-all'\n\nconst { user, config, profile } = await all({\n  async user() { return fetchUser() },\n  async config() { return fetchConfig() },\n  async profile() {\n    return fetchProfile((await this.$.user).id)\n  }\n})\n```\n\n**Alternative without extra dependencies:**\n\nWe can also create all the promises first, and do `Promise.all()` at the end.\n\n```typescript\nconst userPromise = fetchUser()\nconst profilePromise = userPromise.then(user => fetchProfile(user.id))\n\nconst [user, config, profile] = await Promise.all([\n  userPromise,\n  fetchConfig(),\n  profilePromise\n])\n```\n\nReference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)\n","skills/react-best-practices/rules/async-parallel.md":"---\ntitle: Promise.all() for Independent Operations\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: async, parallelization, promises, waterfalls\n---\n\n## Promise.all() for Independent Operations\n\nWhen async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n\n**Incorrect (sequential execution, 3 round trips):**\n\n```typescript\nconst user = await fetchUser()\nconst posts = await fetchPosts()\nconst comments = await fetchComments()\n```\n\n**Correct (parallel execution, 1 round trip):**\n\n```typescript\nconst [user, posts, comments] = await Promise.all([\n  fetchUser(),\n  fetchPosts(),\n  fetchComments()\n])\n```\n","skills/react-best-practices/rules/async-suspense-boundaries.md":"---\ntitle: Strategic Suspense Boundaries\nimpact: HIGH\nimpactDescription: faster initial paint\ntags: async, suspense, streaming, layout-shift\n---\n\n## Strategic Suspense Boundaries\n\nInstead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n\n**Incorrect (wrapper blocked by data fetching):**\n\n```tsx\nasync function Page() {\n  const data = await fetchData() // Blocks entire page\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <DataDisplay data={data} />\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n```\n\nThe entire layout waits for data even though only the middle section needs it.\n\n**Correct (wrapper shows immediately, data streams in):**\n\n```tsx\nfunction Page() {\n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <Suspense fallback={<Skeleton />}>\n          <DataDisplay />\n        </Suspense>\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nasync function DataDisplay() {\n  const data = await fetchData() // Only blocks this component\n  return <div>{data.content}</div>\n}\n```\n\nSidebar, Header, and Footer render immediately. Only DataDisplay waits for data.\n\n**Alternative (share promise across components):**\n\n```tsx\nfunction Page() {\n  // Start fetch immediately, but don't await\n  const dataPromise = fetchData()\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <Suspense fallback={<Skeleton />}>\n        <DataDisplay dataPromise={dataPromise} />\n        <DataSummary dataPromise={dataPromise} />\n      </Suspense>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nfunction DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Unwraps the promise\n  return <div>{data.content}</div>\n}\n\nfunction DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Reuses the same promise\n  return <div>{data.summary}</div>\n}\n```\n\nBoth components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.\n\n**When NOT to use this pattern:**\n\n- Critical data needed for layout decisions (affects positioning)\n- SEO-critical content above the fold\n- Small, fast queries where suspense overhead isn't worth it\n- When you want to avoid layout shift (loading → content jump)\n\n**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.\n","skills/react-best-practices/rules/bundle-analyzable-paths.md":"---\ntitle: Prefer Statically Analyzable Paths\nimpact: HIGH\nimpactDescription: avoids accidental broad bundles and file traces\ntags: bundle, nextjs, vite, webpack, rollup, esbuild, path\n---\n\n## Prefer Statically Analyzable Paths\n\nBuild tools work best when import and file-system paths are obvious at build time. If you hide the real path inside a variable or compose it too dynamically, the tool either has to include a broad set of possible files, warn that it cannot analyze the import, or widen file tracing to stay safe.\n\nPrefer explicit maps or literal paths so the set of reachable files stays narrow and predictable. This is the same rule whether you are choosing modules with `import()` or reading files in server/build code.\n\nWhen analysis becomes too broad, the cost is real:\n- Larger server bundles\n- Slower builds\n- Worse cold starts\n- More memory use\n\n### Import Paths\n\n**Incorrect (the bundler cannot tell what may be imported):**\n\n```ts\nconst PAGE_MODULES = {\n  home: './pages/home',\n  settings: './pages/settings',\n} as const\n\nconst Page = await import(PAGE_MODULES[pageName])\n```\n\n**Correct (use an explicit map of allowed modules):**\n\n```ts\nconst PAGE_MODULES = {\n  home: () => import('./pages/home'),\n  settings: () => import('./pages/settings'),\n} as const\n\nconst Page = await PAGE_MODULES[pageName]()\n```\n\n### File-System Paths\n\n**Incorrect (a 2-value enum still hides the final path from static analysis):**\n\n```ts\nconst baseDir = path.join(process.cwd(), 'content/' + contentKind)\n```\n\n**Correct (make each final path literal at the callsite):**\n\n```ts\nconst baseDir =\n  kind === ContentKind.Blog\n    ? path.join(process.cwd(), 'content/blog')\n    : path.join(process.cwd(), 'content/docs')\n```\n\nIn Next.js server code, this matters for output file tracing too. `path.join(process.cwd(), someVar)` can widen the traced file set because Next.js statically analyze `import`, `require`, and `fs` usage.\n\nReference: [Next.js output](https://nextjs.org/docs/app/api-reference/config/next-config-js/output), [Next.js dynamic imports](https://nextjs.org/learn/seo/dynamic-imports), [Vite features](https://vite.dev/guide/features.html), [esbuild API](https://esbuild.github.io/api/), [Rollup dynamic import vars](https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars), [Webpack dependency management](https://webpack.js.org/guides/dependency-management/)\n","skills/react-best-practices/rules/bundle-barrel-imports.md":"---\ntitle: Avoid Barrel File Imports\nimpact: CRITICAL\nimpactDescription: 200-800ms import cost, slow builds\ntags: bundle, imports, tree-shaking, barrel-files, performance\n---\n\n## Avoid Barrel File Imports\n\nImport directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n\nPopular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.\n\n**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.\n\n**Incorrect (imports entire library):**\n\n```tsx\nimport { Check, X, Menu } from 'lucide-react'\n// Loads 1,583 modules, takes ~2.8s extra in dev\n// Runtime cost: 200-800ms on every cold start\n\nimport { Button, TextField } from '@mui/material'\n// Loads 2,225 modules, takes ~4.2s extra in dev\n```\n\n**Correct - Next.js 13.5+ (recommended):**\n\n```js\n// next.config.js - automatically optimizes barrel imports at build time\nmodule.exports = {\n  experimental: {\n    optimizePackageImports: ['lucide-react', '@mui/material']\n  }\n}\n```\n\n```tsx\n// Keep the standard imports - Next.js transforms them to direct imports\nimport { Check, X, Menu } from 'lucide-react'\n// Full TypeScript support, no manual path wrangling\n```\n\nThis is the recommended approach because it preserves TypeScript type safety and editor autocompletion while still eliminating the barrel import cost.\n\n**Correct - Direct imports (non-Next.js projects):**\n\n```tsx\nimport Button from '@mui/material/Button'\nimport TextField from '@mui/material/TextField'\n// Loads only what you use\n```\n\n> **TypeScript warning:** Some libraries (notably `lucide-react`) don't ship `.d.ts` files for their deep import paths. Importing from `lucide-react/dist/esm/icons/check` resolves to an implicit `any` type, causing errors under `strict` or `noImplicitAny`. Prefer `optimizePackageImports` when available, or verify the library exports types for its subpaths before using direct imports.\n\nThese optimizations provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.\n\nLibraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.\n\nReference: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)\n","skills/react-best-practices/rules/bundle-conditional.md":"---\ntitle: Conditional Module Loading\nimpact: HIGH\nimpactDescription: loads large data only when needed\ntags: bundle, conditional-loading, lazy-loading\n---\n\n## Conditional Module Loading\n\nLoad large data or modules only when a feature is activated.\n\n**Example (lazy-load animation frames):**\n\n```tsx\nfunction AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {\n  const [frames, setFrames] = useState<Frame[] | null>(null)\n\n  useEffect(() => {\n    if (enabled && !frames && typeof window !== 'undefined') {\n      import('./animation-frames.js')\n        .then(mod => setFrames(mod.frames))\n        .catch(() => setEnabled(false))\n    }\n  }, [enabled, frames, setEnabled])\n\n  if (!frames) return <Skeleton />\n  return <Canvas frames={frames} />\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.\n","skills/react-best-practices/rules/bundle-defer-third-party.md":"---\ntitle: Defer Non-Critical Third-Party Libraries\nimpact: MEDIUM\nimpactDescription: loads after hydration\ntags: bundle, third-party, analytics, defer\n---\n\n## Defer Non-Critical Third-Party Libraries\n\nAnalytics, logging, and error tracking don't block user interaction. Load them after hydration.\n\n**Incorrect (blocks initial bundle):**\n\n```tsx\nimport { Analytics } from '@vercel/analytics/react'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n**Correct (loads after hydration):**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst Analytics = dynamic(\n  () => import('@vercel/analytics/react').then(m => m.Analytics),\n  { ssr: false }\n)\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n","skills/react-best-practices/rules/bundle-dynamic-imports.md":"---\ntitle: Dynamic Imports for Heavy Components\nimpact: CRITICAL\nimpactDescription: directly affects TTI and LCP\ntags: bundle, dynamic-import, code-splitting, next-dynamic\n---\n\n## Dynamic Imports for Heavy Components\n\nUse `next/dynamic` to lazy-load large components not needed on initial render.\n\n**Incorrect (Monaco bundles with main chunk ~300KB):**\n\n```tsx\nimport { MonacoEditor } from './monaco-editor'\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n**Correct (Monaco loads on demand):**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst MonacoEditor = dynamic(\n  () => import('./monaco-editor').then(m => m.MonacoEditor),\n  { ssr: false }\n)\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n","skills/react-best-practices/rules/bundle-preload.md":"---\ntitle: Preload Based on User Intent\nimpact: MEDIUM\nimpactDescription: reduces perceived latency\ntags: bundle, preload, user-intent, hover\n---\n\n## Preload Based on User Intent\n\nPreload heavy bundles before they're needed to reduce perceived latency.\n\n**Example (preload on hover/focus):**\n\n```tsx\nfunction EditorButton({ onClick }: { onClick: () => void }) {\n  const preload = () => {\n    if (typeof window !== 'undefined') {\n      void import('./monaco-editor')\n    }\n  }\n\n  return (\n    <button\n      onMouseEnter={preload}\n      onFocus={preload}\n      onClick={onClick}\n    >\n      Open Editor\n    </button>\n  )\n}\n```\n\n**Example (preload when feature flag is enabled):**\n\n```tsx\nfunction FlagsProvider({ children, flags }: Props) {\n  useEffect(() => {\n    if (flags.editorEnabled && typeof window !== 'undefined') {\n      void import('./monaco-editor').then(mod => mod.init())\n    }\n  }, [flags.editorEnabled])\n\n  return <FlagsContext.Provider value={flags}>\n    {children}\n  </FlagsContext.Provider>\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.\n","skills/react-best-practices/rules/client-event-listeners.md":"---\ntitle: Deduplicate Global Event Listeners\nimpact: LOW\nimpactDescription: single listener for N components\ntags: client, swr, event-listeners, subscription\n---\n\n## Deduplicate Global Event Listeners\n\nUse `useSWRSubscription()` to share global event listeners across component instances.\n\n**Incorrect (N instances = N listeners):**\n\n```tsx\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  useEffect(() => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && e.key === key) {\n        callback()\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  }, [key, callback])\n}\n```\n\nWhen using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.\n\n**Correct (N instances = 1 listener):**\n\n```tsx\nimport useSWRSubscription from 'swr/subscription'\n\n// Module-level Map to track callbacks per key\nconst keyCallbacks = new Map<string, Set<() => void>>()\n\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  // Register this callback in the Map\n  useEffect(() => {\n    if (!keyCallbacks.has(key)) {\n      keyCallbacks.set(key, new Set())\n    }\n    keyCallbacks.get(key)!.add(callback)\n\n    return () => {\n      const set = keyCallbacks.get(key)\n      if (set) {\n        set.delete(callback)\n        if (set.size === 0) {\n          keyCallbacks.delete(key)\n        }\n      }\n    }\n  }, [key, callback])\n\n  useSWRSubscription('global-keydown', () => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && keyCallbacks.has(e.key)) {\n        keyCallbacks.get(e.key)!.forEach(cb => cb())\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  })\n}\n\nfunction Profile() {\n  // Multiple shortcuts will share the same listener\n  useKeyboardShortcut('p', () => { /* ... */ }) \n  useKeyboardShortcut('k', () => { /* ... */ })\n  // ...\n}\n```\n","skills/react-best-practices/rules/client-localstorage-schema.md":"---\ntitle: Version and Minimize localStorage Data\nimpact: MEDIUM\nimpactDescription: prevents schema conflicts, reduces storage size\ntags: client, localStorage, storage, versioning, data-minimization\n---\n\n## Version and Minimize localStorage Data\n\nAdd version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n\n**Incorrect:**\n\n```typescript\n// No version, stores everything, no error handling\nlocalStorage.setItem('userConfig', JSON.stringify(fullUserObject))\nconst data = localStorage.getItem('userConfig')\n```\n\n**Correct:**\n\n```typescript\nconst VERSION = 'v2'\n\nfunction saveConfig(config: { theme: string; language: string }) {\n  try {\n    localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))\n  } catch {\n    // Throws in incognito/private browsing, quota exceeded, or disabled\n  }\n}\n\nfunction loadConfig() {\n  try {\n    const data = localStorage.getItem(`userConfig:${VERSION}`)\n    return data ? JSON.parse(data) : null\n  } catch {\n    return null\n  }\n}\n\n// Migration from v1 to v2\nfunction migrate() {\n  try {\n    const v1 = localStorage.getItem('userConfig:v1')\n    if (v1) {\n      const old = JSON.parse(v1)\n      saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })\n      localStorage.removeItem('userConfig:v1')\n    }\n  } catch {}\n}\n```\n\n**Store minimal fields from server responses:**\n\n```typescript\n// User object has 20+ fields, only store what UI needs\nfunction cachePrefs(user: FullUser) {\n  try {\n    localStorage.setItem('prefs:v1', JSON.stringify({\n      theme: user.preferences.theme,\n      notifications: user.preferences.notifications\n    }))\n  } catch {}\n}\n```\n\n**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.\n\n**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.\n","skills/react-best-practices/rules/client-passive-event-listeners.md":"---\ntitle: Use Passive Event Listeners for Scrolling Performance\nimpact: MEDIUM\nimpactDescription: eliminates scroll delay caused by event listeners\ntags: client, event-listeners, scrolling, performance, touch, wheel\n---\n\n## Use Passive Event Listeners for Scrolling Performance\n\nAdd `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n\n**Incorrect:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch)\n  document.addEventListener('wheel', handleWheel)\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Correct:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch, { passive: true })\n  document.addEventListener('wheel', handleWheel, { passive: true })\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.\n\n**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.\n","skills/react-best-practices/rules/client-swr-dedup.md":"---\ntitle: Use SWR for Automatic Deduplication\nimpact: MEDIUM-HIGH\nimpactDescription: automatic deduplication\ntags: client, swr, deduplication, data-fetching\n---\n\n## Use SWR for Automatic Deduplication\n\nSWR enables request deduplication, caching, and revalidation across component instances.\n\n**Incorrect (no deduplication, each instance fetches):**\n\n```tsx\nfunction UserList() {\n  const [users, setUsers] = useState([])\n  useEffect(() => {\n    fetch('/api/users')\n      .then(r => r.json())\n      .then(setUsers)\n  }, [])\n}\n```\n\n**Correct (multiple instances share one request):**\n\n```tsx\nimport useSWR from 'swr'\n\nfunction UserList() {\n  const { data: users } = useSWR('/api/users', fetcher)\n}\n```\n\n**For immutable data:**\n\n```tsx\nimport { useImmutableSWR } from '@/lib/swr'\n\nfunction StaticContent() {\n  const { data } = useImmutableSWR('/api/config', fetcher)\n}\n```\n\n**For mutations:**\n\n```tsx\nimport { useSWRMutation } from 'swr/mutation'\n\nfunction UpdateButton() {\n  const { trigger } = useSWRMutation('/api/user', updateUser)\n  return <button onClick={() => trigger()}>Update</button>\n}\n```\n\nReference: [https://swr.vercel.app](https://swr.vercel.app)\n","skills/react-best-practices/rules/js-batch-dom-css.md":"---\ntitle: Avoid Layout Thrashing\nimpact: MEDIUM\nimpactDescription: prevents forced synchronous layouts and reduces performance bottlenecks\ntags: javascript, dom, css, performance, reflow, layout-thrashing\n---\n\n## Avoid Layout Thrashing\n\nAvoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.\n\n**This is OK (browser batches style changes):**\n```typescript\nfunction updateElementStyles(element: HTMLElement) {\n  // Each line invalidates style, but browser batches the recalculation\n  element.style.width = '100px'\n  element.style.height = '200px'\n  element.style.backgroundColor = 'blue'\n  element.style.border = '1px solid black'\n}\n```\n\n**Incorrect (interleaved reads and writes force reflows):**\n```typescript\nfunction layoutThrashing(element: HTMLElement) {\n  element.style.width = '100px'\n  const width = element.offsetWidth  // Forces reflow\n  element.style.height = '200px'\n  const height = element.offsetHeight  // Forces another reflow\n}\n```\n\n**Correct (batch writes, then read once):**\n```typescript\nfunction updateElementStyles(element: HTMLElement) {\n  // Batch all writes together\n  element.style.width = '100px'\n  element.style.height = '200px'\n  element.style.backgroundColor = 'blue'\n  element.style.border = '1px solid black'\n  \n  // Read after all writes are done (single reflow)\n  const { width, height } = element.getBoundingClientRect()\n}\n```\n\n**Correct (batch reads, then writes):**\n```typescript\nfunction avoidThrashing(element: HTMLElement) {\n  // Read phase - all layout queries first\n  const rect1 = element.getBoundingClientRect()\n  const offsetWidth = element.offsetWidth\n  const offsetHeight = element.offsetHeight\n  \n  // Write phase - all style changes after\n  element.style.width = '100px'\n  element.style.height = '200px'\n}\n```\n\n**Better: use CSS classes**\n```css\n.highlighted-box {\n  width: 100px;\n  height: 200px;\n  background-color: blue;\n  border: 1px solid black;\n}\n```\n```typescript\nfunction updateElementStyles(element: HTMLElement) {\n  element.classList.add('highlighted-box')\n  \n  const { width, height } = element.getBoundingClientRect()\n}\n```\n\n**React example:**\n```tsx\n// Incorrect: interleaving style changes with layout queries\nfunction Box({ isHighlighted }: { isHighlighted: boolean }) {\n  const ref = useRef<HTMLDivElement>(null)\n  \n  useEffect(() => {\n    if (ref.current && isHighlighted) {\n      ref.current.style.width = '100px'\n      const width = ref.current.offsetWidth // Forces layout\n      ref.current.style.height = '200px'\n    }\n  }, [isHighlighted])\n  \n  return <div ref={ref}>Content</div>\n}\n\n// Correct: toggle class\nfunction Box({ isHighlighted }: { isHighlighted: boolean }) {\n  return (\n    <div className={isHighlighted ? 'highlighted-box' : ''}>\n      Content\n    </div>\n  )\n}\n```\n\nPrefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.\n\nSee [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.\n","skills/react-best-practices/rules/js-cache-function-results.md":"---\ntitle: Cache Repeated Function Calls\nimpact: MEDIUM\nimpactDescription: avoid redundant computation\ntags: javascript, cache, memoization, performance\n---\n\n## Cache Repeated Function Calls\n\nUse a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.\n\n**Incorrect (redundant computation):**\n\n```typescript\nfunction ProjectList({ projects }: { projects: Project[] }) {\n  return (\n    <div>\n      {projects.map(project => {\n        // slugify() called 100+ times for same project names\n        const slug = slugify(project.name)\n        \n        return <ProjectCard key={project.id} slug={slug} />\n      })}\n    </div>\n  )\n}\n```\n\n**Correct (cached results):**\n\n```typescript\n// Module-level cache\nconst slugifyCache = new Map<string, string>()\n\nfunction cachedSlugify(text: string): string {\n  if (slugifyCache.has(text)) {\n    return slugifyCache.get(text)!\n  }\n  const result = slugify(text)\n  slugifyCache.set(text, result)\n  return result\n}\n\nfunction ProjectList({ projects }: { projects: Project[] }) {\n  return (\n    <div>\n      {projects.map(project => {\n        // Computed only once per unique project name\n        const slug = cachedSlugify(project.name)\n        \n        return <ProjectCard key={project.id} slug={slug} />\n      })}\n    </div>\n  )\n}\n```\n\n**Simpler pattern for single-value functions:**\n\n```typescript\nlet isLoggedInCache: boolean | null = null\n\nfunction isLoggedIn(): boolean {\n  if (isLoggedInCache !== null) {\n    return isLoggedInCache\n  }\n  \n  isLoggedInCache = document.cookie.includes('auth=')\n  return isLoggedInCache\n}\n\n// Clear cache when auth changes\nfunction onAuthChange() {\n  isLoggedInCache = null\n}\n```\n\nUse a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.\n\nReference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)\n","skills/react-best-practices/rules/js-cache-property-access.md":"---\ntitle: Cache Property Access in Loops\nimpact: LOW-MEDIUM\nimpactDescription: reduces lookups\ntags: javascript, loops, optimization, caching\n---\n\n## Cache Property Access in Loops\n\nCache object property lookups in hot paths.\n\n**Incorrect (3 lookups × N iterations):**\n\n```typescript\nfor (let i = 0; i < arr.length; i++) {\n  process(obj.config.settings.value)\n}\n```\n\n**Correct (1 lookup total):**\n\n```typescript\nconst value = obj.config.settings.value\nconst len = arr.length\nfor (let i = 0; i < len; i++) {\n  process(value)\n}\n```\n","skills/react-best-practices/rules/js-cache-storage.md":"---\ntitle: Cache Storage API Calls\nimpact: LOW-MEDIUM\nimpactDescription: reduces expensive I/O\ntags: javascript, localStorage, storage, caching, performance\n---\n\n## Cache Storage API Calls\n\n`localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory.\n\n**Incorrect (reads storage on every call):**\n\n```typescript\nfunction getTheme() {\n  return localStorage.getItem('theme') ?? 'light'\n}\n// Called 10 times = 10 storage reads\n```\n\n**Correct (Map cache):**\n\n```typescript\nconst storageCache = new Map<string, string | null>()\n\nfunction getLocalStorage(key: string) {\n  if (!storageCache.has(key)) {\n    storageCache.set(key, localStorage.getItem(key))\n  }\n  return storageCache.get(key)\n}\n\nfunction setLocalStorage(key: string, value: string) {\n  localStorage.setItem(key, value)\n  storageCache.set(key, value)  // keep cache in sync\n}\n```\n\nUse a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.\n\n**Cookie caching:**\n\n```typescript\nlet cookieCache: Record<string, string> | null = null\n\nfunction getCookie(name: string) {\n  if (!cookieCache) {\n    cookieCache = Object.fromEntries(\n      document.cookie.split('; ').map(c => c.split('='))\n    )\n  }\n  return cookieCache[name]\n}\n```\n\n**Important (invalidate on external changes):**\n\nIf storage can change externally (another tab, server-set cookies), invalidate cache:\n\n```typescript\nwindow.addEventListener('storage', (e) => {\n  if (e.key) storageCache.delete(e.key)\n})\n\ndocument.addEventListener('visibilitychange', () => {\n  if (document.visibilityState === 'visible') {\n    storageCache.clear()\n  }\n})\n```\n","skills/react-best-practices/rules/js-combine-iterations.md":"---\ntitle: Combine Multiple Array Iterations\nimpact: LOW-MEDIUM\nimpactDescription: reduces iterations\ntags: javascript, arrays, loops, performance\n---\n\n## Combine Multiple Array Iterations\n\nMultiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.\n\n**Incorrect (3 iterations):**\n\n```typescript\nconst admins = users.filter(u => u.isAdmin)\nconst testers = users.filter(u => u.isTester)\nconst inactive = users.filter(u => !u.isActive)\n```\n\n**Correct (1 iteration):**\n\n```typescript\nconst admins: User[] = []\nconst testers: User[] = []\nconst inactive: User[] = []\n\nfor (const user of users) {\n  if (user.isAdmin) admins.push(user)\n  if (user.isTester) testers.push(user)\n  if (!user.isActive) inactive.push(user)\n}\n```\n","skills/react-best-practices/rules/js-early-exit.md":"---\ntitle: Early Return from Functions\nimpact: LOW-MEDIUM\nimpactDescription: avoids unnecessary computation\ntags: javascript, functions, optimization, early-return\n---\n\n## Early Return from Functions\n\nReturn early when result is determined to skip unnecessary processing.\n\n**Incorrect (processes all items even after finding answer):**\n\n```typescript\nfunction validateUsers(users: User[]) {\n  let hasError = false\n  let errorMessage = ''\n  \n  for (const user of users) {\n    if (!user.email) {\n      hasError = true\n      errorMessage = 'Email required'\n    }\n    if (!user.name) {\n      hasError = true\n      errorMessage = 'Name required'\n    }\n    // Continues checking all users even after error found\n  }\n  \n  return hasError ? { valid: false, error: errorMessage } : { valid: true }\n}\n```\n\n**Correct (returns immediately on first error):**\n\n```typescript\nfunction validateUsers(users: User[]) {\n  for (const user of users) {\n    if (!user.email) {\n      return { valid: false, error: 'Email required' }\n    }\n    if (!user.name) {\n      return { valid: false, error: 'Name required' }\n    }\n  }\n\n  return { valid: true }\n}\n```\n","skills/react-best-practices/rules/js-flatmap-filter.md":"---\ntitle: Use flatMap to Map and Filter in One Pass\nimpact: LOW-MEDIUM\nimpactDescription: eliminates intermediate array\ntags: javascript, arrays, flatMap, filter, performance\n---\n\n## Use flatMap to Map and Filter in One Pass\n\n**Impact: LOW-MEDIUM (eliminates intermediate array)**\n\nChaining `.map().filter(Boolean)` creates an intermediate array and iterates twice. Use `.flatMap()` to transform and filter in a single pass.\n\n**Incorrect (2 iterations, intermediate array):**\n\n```typescript\nconst userNames = users\n  .map(user => user.isActive ? user.name : null)\n  .filter(Boolean)\n```\n\n**Correct (1 iteration, no intermediate array):**\n\n```typescript\nconst userNames = users.flatMap(user =>\n  user.isActive ? [user.name] : []\n)\n```\n\n**More examples:**\n\n```typescript\n// Extract valid emails from responses\n// Before\nconst emails = responses\n  .map(r => r.success ? r.data.email : null)\n  .filter(Boolean)\n\n// After\nconst emails = responses.flatMap(r =>\n  r.success ? [r.data.email] : []\n)\n\n// Parse and filter valid numbers\n// Before\nconst numbers = strings\n  .map(s => parseInt(s, 10))\n  .filter(n => !isNaN(n))\n\n// After\nconst numbers = strings.flatMap(s => {\n  const n = parseInt(s, 10)\n  return isNaN(n) ? [] : [n]\n})\n```\n\n**When to use:**\n- Transforming items while filtering some out\n- Conditional mapping where some inputs produce no output\n- Parsing/validating where invalid inputs should be skipped\n","skills/react-best-practices/rules/js-hoist-regexp.md":"---\ntitle: Hoist RegExp Creation\nimpact: LOW-MEDIUM\nimpactDescription: avoids recreation\ntags: javascript, regexp, optimization, memoization\n---\n\n## Hoist RegExp Creation\n\nDon't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.\n\n**Incorrect (new RegExp every render):**\n\n```tsx\nfunction Highlighter({ text, query }: Props) {\n  const regex = new RegExp(`(${query})`, 'gi')\n  const parts = text.split(regex)\n  return <>{parts.map((part, i) => ...)}</>\n}\n```\n\n**Correct (memoize or hoist):**\n\n```tsx\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nfunction Highlighter({ text, query }: Props) {\n  const regex = useMemo(\n    () => new RegExp(`(${escapeRegex(query)})`, 'gi'),\n    [query]\n  )\n  const parts = text.split(regex)\n  return <>{parts.map((part, i) => ...)}</>\n}\n```\n\n**Warning (global regex has mutable state):**\n\nGlobal regex (`/g`) has mutable `lastIndex` state:\n\n```typescript\nconst regex = /foo/g\nregex.test('foo')  // true, lastIndex = 3\nregex.test('foo')  // false, lastIndex = 0\n```\n","skills/react-best-practices/rules/js-index-maps.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-index-maps.md -->","skills/react-best-practices/rules/js-length-check-first.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-length-check-first.md -->","skills/react-best-practices/rules/js-min-max-loop.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-min-max-loop.md -->","skills/react-best-practices/rules/js-request-idle-callback.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-request-idle-callback.md -->","skills/react-best-practices/rules/js-set-map-lookups.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-set-map-lookups.md -->","skills/react-best-practices/rules/js-tosorted-immutable.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-tosorted-immutable.md -->","skills/react-best-practices/rules/rendering-activity.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-activity.md -->","skills/react-best-practices/rules/rendering-animate-svg-wrapper.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-animate-svg-wrapper.md -->","skills/react-best-practices/rules/rendering-conditional-render.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-conditional-render.md -->","skills/react-best-practices/rules/rendering-content-visibility.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-content-visibility.md -->","skills/react-best-practices/rules/rendering-hoist-jsx.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-hoist-jsx.md -->","skills/react-best-practices/rules/rendering-hydration-no-flicker.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-hydration-no-flicker.md -->","skills/react-best-practices/rules/rendering-hydration-suppress-warning.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-hydration-suppress-warning.md -->","skills/react-best-practices/rules/rendering-resource-hints.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-resource-hints.md -->","skills/react-best-practices/rules/rendering-script-defer-async.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-script-defer-async.md -->","skills/react-best-practices/rules/rendering-svg-precision.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-svg-precision.md -->","skills/react-best-practices/rules/rendering-usetransition-loading.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-usetransition-loading.md -->","skills/react-best-practices/rules/rerender-defer-reads.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-defer-reads.md -->","skills/react-best-practices/rules/rerender-dependencies.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-dependencies.md -->","skills/react-best-practices/rules/rerender-derived-state-no-effect.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-derived-state-no-effect.md -->","skills/react-best-practices/rules/rerender-derived-state.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-derived-state.md -->","skills/react-best-practices/rules/rerender-functional-setstate.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-functional-setstate.md -->","skills/react-best-practices/rules/rerender-lazy-state-init.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-lazy-state-init.md -->","skills/react-best-practices/rules/rerender-memo-with-default-value.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-memo-with-default-value.md -->","skills/react-best-practices/rules/rerender-memo.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-memo.md -->","skills/react-best-practices/rules/rerender-move-effect-to-event.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-move-effect-to-event.md -->","skills/react-best-practices/rules/rerender-no-inline-components.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-no-inline-components.md -->","skills/react-best-practices/rules/rerender-simple-expression-in-memo.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-simple-expression-in-memo.md -->","skills/react-best-practices/rules/rerender-split-combined-hooks.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-split-combined-hooks.md -->","skills/react-best-practices/rules/rerender-transitions.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-transitions.md -->","skills/react-best-practices/rules/rerender-use-deferred-value.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-use-deferred-value.md -->","skills/react-best-practices/rules/rerender-use-ref-transient-values.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-use-ref-transient-values.md -->","skills/react-best-practices/rules/server-after-nonblocking.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-after-nonblocking.md -->","skills/react-best-practices/rules/server-auth-actions.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-auth-actions.md -->","skills/react-best-practices/rules/server-cache-lru.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-cache-lru.md -->","skills/react-best-practices/rules/server-cache-react.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-cache-react.md -->","skills/react-best-practices/rules/server-dedup-props.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-dedup-props.md -->","skills/react-best-practices/rules/server-hoist-static-io.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-hoist-static-io.md -->","skills/react-best-practices/rules/server-no-shared-module-state.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-no-shared-module-state.md -->","skills/react-best-practices/rules/server-parallel-fetching.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-parallel-fetching.md -->","skills/react-best-practices/rules/server-parallel-nested-fetching.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-parallel-nested-fetching.md -->","skills/react-best-practices/rules/server-serialization.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-serialization.md -->","skills/react-native-skills/AGENTS.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2FAGENTS.md -->","skills/react-native-skills/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2FREADME.md -->","skills/react-native-skills/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2FSKILL.md -->","skills/react-native-skills/metadata.json":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Fmetadata.json -->","skills/react-native-skills/rules/_sections.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2F_sections.md -->","skills/react-native-skills/rules/_template.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2F_template.md -->","skills/react-native-skills/rules/animation-derived-value.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fanimation-derived-value.md -->","skills/react-native-skills/rules/animation-gesture-detector-press.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fanimation-gesture-detector-press.md -->","skills/react-native-skills/rules/animation-gpu-properties.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fanimation-gpu-properties.md -->","skills/react-native-skills/rules/design-system-compound-components.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fdesign-system-compound-components.md -->","skills/react-native-skills/rules/fonts-config-plugin.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Ffonts-config-plugin.md -->","skills/react-native-skills/rules/imports-design-system-folder.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fimports-design-system-folder.md -->","skills/react-native-skills/rules/js-hoist-intl.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fjs-hoist-intl.md -->","skills/react-native-skills/rules/list-performance-callbacks.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-callbacks.md -->","skills/react-native-skills/rules/list-performance-function-references.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-function-references.md -->","skills/react-native-skills/rules/list-performance-images.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-images.md -->","skills/react-native-skills/rules/list-performance-inline-objects.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-inline-objects.md -->","skills/react-native-skills/rules/list-performance-item-expensive.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-item-expensive.md -->","skills/react-native-skills/rules/list-performance-item-memo.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-item-memo.md -->","skills/react-native-skills/rules/list-performance-item-types.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-item-types.md -->","skills/react-native-skills/rules/list-performance-virtualize.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-virtualize.md -->","skills/react-native-skills/rules/monorepo-native-deps-in-app.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fmonorepo-native-deps-in-app.md -->","skills/react-native-skills/rules/monorepo-single-dependency-versions.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fmonorepo-single-dependency-versions.md -->","skills/react-native-skills/rules/navigation-native-navigators.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fnavigation-native-navigators.md -->","skills/react-native-skills/rules/react-compiler-destructure-functions.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-compiler-destructure-functions.md -->","skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-compiler-reanimated-shared-values.md -->","skills/react-native-skills/rules/react-state-dispatcher.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-state-dispatcher.md -->","skills/react-native-skills/rules/react-state-fallback.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-state-fallback.md -->","skills/react-native-skills/rules/react-state-minimize.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-state-minimize.md -->","skills/react-native-skills/rules/rendering-no-falsy-and.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Frendering-no-falsy-and.md -->","skills/react-native-skills/rules/rendering-text-in-text-component.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Frendering-text-in-text-component.md -->","skills/react-native-skills/rules/scroll-position-no-state.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fscroll-position-no-state.md -->","skills/react-native-skills/rules/state-ground-truth.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fstate-ground-truth.md -->","skills/react-native-skills/rules/ui-expo-image.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-expo-image.md -->","skills/react-native-skills/rules/ui-image-gallery.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-image-gallery.md -->","skills/react-native-skills/rules/ui-measure-views.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-measure-views.md -->","skills/react-native-skills/rules/ui-menus.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-menus.md -->","skills/react-native-skills/rules/ui-native-modals.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-native-modals.md -->","skills/react-native-skills/rules/ui-pressable.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-pressable.md -->","skills/react-native-skills/rules/ui-safe-area-scroll.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-safe-area-scroll.md -->","skills/react-native-skills/rules/ui-scrollview-content-inset.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-scrollview-content-inset.md -->","skills/react-native-skills/rules/ui-styling.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-styling.md -->","skills/react-view-transitions/AGENTS.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2FAGENTS.md -->","skills/react-view-transitions/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2FREADME.md -->","skills/react-view-transitions/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2FSKILL.md -->","skills/react-view-transitions/metadata.json":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Fmetadata.json -->","skills/react-view-transitions/references/css-recipes.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fcss-recipes.md -->","skills/react-view-transitions/references/implementation.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fimplementation.md -->","skills/react-view-transitions/references/nextjs.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fnextjs.md -->","skills/react-view-transitions/references/patterns.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fpatterns.md -->","skills/react-view-transitions/references/troubleshooting.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Ftroubleshooting.md -->","skills/vercel-cli-with-tokens/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-cli-with-tokens%2FSKILL.md -->","skills/vercel-optimize/AGENTS.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FAGENTS.md -->","skills/vercel-optimize/CONTRIBUTING.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FCONTRIBUTING.md -->","skills/vercel-optimize/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FREADME.md -->","skills/vercel-optimize/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FSKILL.md -->","skills/vercel-optimize/metadata.json":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Fmetadata.json -->","skills/vercel-optimize/references/candidates.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fcandidates.md -->","skills/vercel-optimize/references/data-collection.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fdata-collection.md -->","skills/vercel-optimize/references/docs-library.json":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fdocs-library.json -->","skills/vercel-optimize/references/doctrine.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fdoctrine.md -->","skills/vercel-optimize/references/observability-plus.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fobservability-plus.md -->","skills/vercel-optimize/references/playbooks/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2FREADME.md -->","skills/vercel-optimize/references/playbooks/ai-application.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fai-application.md -->","skills/vercel-optimize/references/playbooks/api-service.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fapi-service.md -->","skills/vercel-optimize/references/playbooks/content-site.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fcontent-site.md -->","skills/vercel-optimize/references/playbooks/ecommerce.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fecommerce.md -->","skills/vercel-optimize/references/playbooks/marketing.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fmarketing.md -->","skills/vercel-optimize/references/playbooks/saas.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fsaas.md -->","skills/vercel-optimize/references/playbooks/sveltekit.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fsveltekit.md -->","skills/vercel-optimize/references/recommendations.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Frecommendations.md -->","skills/vercel-optimize/references/scanner-patterns.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fscanner-patterns.md -->","skills/vercel-optimize/references/scoring.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fscoring.md -->","skills/vercel-optimize/references/support-topics/README.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2FREADME.md -->","skills/vercel-optimize/references/support-topics/astro-edge-middleware-scope.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fastro-edge-middleware-scope.md -->","skills/vercel-optimize/references/support-topics/astro-output-mode-and-isr.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fastro-output-mode-and-isr.md -->","skills/vercel-optimize/references/support-topics/auth-preserving-parallelization.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fauth-preserving-parallelization.md -->","skills/vercel-optimize/references/support-topics/bot-protection-product-guardrails.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fbot-protection-product-guardrails.md -->","skills/vercel-optimize/references/support-topics/build-minutes-monorepo-fanout.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fbuild-minutes-monorepo-fanout.md -->","skills/vercel-optimize/references/support-topics/cache-components-static-shell-boundaries.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcache-components-static-shell-boundaries.md -->","skills/vercel-optimize/references/support-topics/cache-components-suspense-dedupe-pitfall.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcache-components-suspense-dedupe-pitfall.md -->","skills/vercel-optimize/references/support-topics/cdn-cache-auth-safety.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcdn-cache-auth-safety.md -->","skills/vercel-optimize/references/support-topics/cold-start-initialization-bundle.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcold-start-initialization-bundle.md -->","skills/vercel-optimize/references/support-topics/core-web-vitals-client-bottlenecks.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcore-web-vitals-client-bottlenecks.md -->","skills/vercel-optimize/references/support-topics/database-egress-pooling-region.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fdatabase-egress-pooling-region.md -->","skills/vercel-optimize/references/support-topics/dynamic-rendering-traps.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fdynamic-rendering-traps.md -->","skills/vercel-optimize/references/support-topics/external-api-critical-path-platform.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fexternal-api-critical-path-platform.md -->","skills/vercel-optimize/references/support-topics/external-api-critical-path.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fexternal-api-critical-path.md -->","skills/vercel-optimize/references/support-topics/fast-data-transfer-payloads.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffast-data-transfer-payloads.md -->","skills/vercel-optimize/references/support-topics/fluid-compute-caveats.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffluid-compute-caveats.md -->","skills/vercel-optimize/references/support-topics/function-duration-io-and-after.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffunction-duration-io-and-after.md -->","skills/vercel-optimize/references/support-topics/function-invocation-reduction.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffunction-invocation-reduction.md -->","skills/vercel-optimize/references/support-topics/function-region-misconfiguration-ttfb.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffunction-region-misconfiguration-ttfb.md -->","skills/vercel-optimize/references/support-topics/image-optimization-cost-control.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fimage-optimization-cost-control.md -->","skills/vercel-optimize/references/support-topics/isr-revalidation-static-generation.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fisr-revalidation-static-generation.md -->","skills/vercel-optimize/references/support-topics/middleware-proxy-edge-cost.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fmiddleware-proxy-edge-cost.md -->","skills/vercel-optimize/references/support-topics/next-fetch-revalidate-floor.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-fetch-revalidate-floor.md -->","skills/vercel-optimize/references/support-topics/next-font-cls-self-hosting.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-font-cls-self-hosting.md -->","skills/vercel-optimize/references/support-topics/next-heavy-ui-lazy-load-boundaries.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-heavy-ui-lazy-load-boundaries.md -->","skills/vercel-optimize/references/support-topics/next-image-lcp-preload-sizes.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-image-lcp-preload-sizes.md -->","skills/vercel-optimize/references/support-topics/next-route-handler-get-cache-defaults.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-route-handler-get-cache-defaults.md -->","skills/vercel-optimize/references/support-topics/next-script-third-party-strategy.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-script-third-party-strategy.md -->","skills/vercel-optimize/references/support-topics/nextjs-version-cache-semantics.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnextjs-version-cache-semantics.md -->","skills/vercel-optimize/references/support-topics/not-found-catchall-request-waste.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnot-found-catchall-request-waste.md -->","skills/vercel-optimize/references/support-topics/nuxt-route-rules-cache-isr.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnuxt-route-rules-cache-isr.md -->","skills/vercel-optimize/references/support-topics/observability-events-cost-attribution.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fobservability-events-cost-attribution.md -->","skills/vercel-optimize/references/support-topics/post-response-work-waituntil.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fpost-response-work-waituntil.md -->","skills/vercel-optimize/references/support-topics/route-error-durable-offload.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Froute-error-durable-offload.md -->","skills/vercel-optimize/references/support-topics/route-error-runtime-limits.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Froute-error-runtime-limits.md -->","skills/vercel-optimize/references/support-topics/runtime-cache-reusable-data.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fruntime-cache-reusable-data.md -->","skills/vercel-optimize/references/support-topics/sveltekit-isr-prerender-safety.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fsveltekit-isr-prerender-safety.md -->","skills/vercel-optimize/references/support-topics/sveltekit-split-cold-start-tradeoff.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fsveltekit-split-cold-start-tradeoff.md -->","skills/vercel-optimize/references/support-topics/usage-spike-triage.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fusage-spike-triage.md -->","skills/vercel-optimize/references/support-topics/use-cache-date-stamp-isr-write-amplifier.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fuse-cache-date-stamp-isr-write-amplifier.md -->","skills/vercel-optimize/references/support-topics/use-cache-remote-shared-origin-data.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fuse-cache-remote-shared-origin-data.md -->","skills/vercel-optimize/references/support-topics/workflow-resumable-stream-routes.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fworkflow-resumable-stream-routes.md -->","skills/vercel-optimize/references/verification.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fverification.md -->","skills/vercel-optimize/references/voice.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fvoice.md -->","skills/web-design-guidelines/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fweb-design-guidelines%2FSKILL.md -->","skills/writing-guidelines/SKILL.md":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fwriting-guidelines%2FSKILL.md -->"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md\n\nThis file provides guidance to AI coding agents (Claude Code, Cursor, Copilot, etc.) when working with code in this repository.\n\n## Repository Overview\n\nA collection of skills for AI coding agents working with Vercel projects. Skills are packaged instructions and scripts that extend agent capabilities.\n\n## Creating a New Skill\n\n### Directory Structure\n\n```\nskills/\n  {skill-name}/           # kebab-case directory name\n    SKILL.md              # Required: skill definition\n    scripts/              # Optional: executable scripts\n      {script-name}.sh    # Bash scripts\n      {script-name}.mjs   # Node scripts\n    references/           # Optional: supporting docs loaded on demand\n    lib/                  # Optional: shared code for scripts\n```\n\n### Naming Conventions\n\n- **Skill directory**: `kebab-case` (e.g., `vercel-deploy`, `log-monitor`)\n- **SKILL.md**: Always uppercase, always this exact filename\n- **Scripts**: `kebab-case.sh` or `kebab-case.mjs` (e.g., `deploy.sh`, `collect-signals.mjs`)\n\n### SKILL.md Format\n\n```markdown\n---\nname: {skill-name}\ndescription: {One sentence describing when to use this skill. Include trigger phrases like \"Deploy my app\", \"Check logs\", etc.}\n---\n\n# {Skill Title}\n\n{Brief description of what the skill does.}\n\n## How It Works\n\n{Numbered list explaining the skill's workflow}\n\n## Usage\n\n```bash\nbash /mnt/skills/user/{skill-name}/scripts/{script}.sh [args]\n```\n\n**Arguments:**\n- `arg1` - Description (defaults to X)\n\n**Examples:**\n{Show 2-3 common usage patterns}\n\n## Output\n\n{Show example output users will see}\n\n## Present Results to User\n\n{Template for how Claude should format results when presenting to users}\n\n## Troubleshooting\n\n{Common issues and solutions, especially network/permissions errors}\n```\n\n### Best Practices for Context Efficiency\n\nSkills are loaded on-demand — only the skill name and description are loaded at startup. The full `SKILL.md` loads into context only when the agent decides the skill is relevant. To minimize context usage:\n\n- **Keep SKILL.md under 500 lines** — put detailed reference material in separate files\n- **Write specific descriptions** — helps the agent know exactly when to activate the skill\n- **Use progressive disclosure** — reference supporting files that get read only when needed\n- **Prefer scripts over inline code** — script execution doesn't consume context (only output does)\n- **File references work one level deep** — link directly from SKILL.md to supporting files\n\n### Script Requirements\n\n- Bash scripts: use `#!/bin/bash` and `set -e`\n- Node scripts: use `#!/usr/bin/env node` and `.mjs`\n- Write status messages to stderr\n- Write machine-readable output (JSON) to stdout\n- Include a cleanup trap for temp files when scripts create them\n- Reference scripts by relative path, for example `node scripts/{script}.mjs`\n\n### End-User Installation\n\nDocument skills.sh installation for public skills:\n\n```bash\nnpx skills add vercel-labs/agent-skills --skill {skill-name}\n```\n\nFor manual installs, document the native path when the skill needs one.\n\n**Claude Code:**\n```bash\ncp -r skills/{skill-name} ~/.claude/skills/\n```\n\n**claude.ai:**\nAdd the skill to project knowledge or paste SKILL.md contents into the conversation.\n\nIf the skill requires network access, instruct users to add required domains at `claude.ai/settings/capabilities`.\n","isInternal":false,"tokens":770,"sizeBytes":3367},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"AGENTS.md","isInternal":false,"tokens":3,"sizeBytes":9},{"name":"AGENTS.md","path":"skills/composition-patterns/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/AGENTS.md","title":"Composition-patterns Skill","category":"anthropic-skill","format":"markdown","content":"# React Composition Patterns\n\n**Version 1.0.0**  \nEngineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React codebases using composition. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n---\n\n## Table of Contents\n\n1. [Component Architecture](#1-component-architecture) — **HIGH**\n   - 1.1 [Avoid Boolean Prop Proliferation](#11-avoid-boolean-prop-proliferation)\n   - 1.2 [Use Compound Components](#12-use-compound-components)\n2. [State Management](#2-state-management) — **MEDIUM**\n   - 2.1 [Decouple State Management from UI](#21-decouple-state-management-from-ui)\n   - 2.2 [Define Generic Context Interfaces for Dependency Injection](#22-define-generic-context-interfaces-for-dependency-injection)\n   - 2.3 [Lift State into Provider Components](#23-lift-state-into-provider-components)\n3. [Implementation Patterns](#3-implementation-patterns) — **MEDIUM**\n   - 3.1 [Create Explicit Component Variants](#31-create-explicit-component-variants)\n   - 3.2 [Prefer Composing Children Over Render Props](#32-prefer-composing-children-over-render-props)\n4. [React 19 APIs](#4-react-19-apis) — **MEDIUM**\n   - 4.1 [React 19 API Changes](#41-react-19-api-changes)\n\n---\n\n## 1. Component Architecture\n\n**Impact: HIGH**\n\nFundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n### 1.1 Avoid Boolean Prop Proliferation\n\n**Impact: CRITICAL (prevents unmaintainable component variants)**\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\n\ncomponent behavior. Each boolean doubles possible states and creates\n\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect: boolean props create exponential complexity**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct: composition eliminates conditionals**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\n\nsharing a single monolithic parent.\n\n### 1.2 Use Compound Components\n\n**Impact: HIGH (enables flexible composition without prop drilling)**\n\nStructure complex components as compound components with a shared context. Each\n\nsubcomponent accesses shared state via context, not props. Consumers compose the\n\npieces they need.\n\n**Incorrect: monolithic component with render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct: compound components with shared context**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n\n---\n\n## 2. State Management\n\n**Impact: MEDIUM**\n\nPatterns for lifting state and managing shared context across\ncomposed components.\n\n### 2.1 Decouple State Management from UI\n\n**Impact: MEDIUM (enables swapping state implementations without changing UI)**\n\nThe provider component should be the only place that knows how state is managed.\n\nUI components consume the context interface—they don't know if state comes from\n\nuseState, Zustand, or a server sync.\n\n**Incorrect: UI coupled to state implementation**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct: state management isolated in provider**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\n\ndepends on the context interface, not the implementation.\n\n### 2.2 Define Generic Context Interfaces for Dependency Injection\n\n**Impact: HIGH (enables dependency-injectable state across use-cases)**\n\nDefine a **generic interface** for your component context with three parts:\n\n`state`, `actions`, and `meta`. This interface is a contract that any provider\n\ncan implement—enabling the same UI components to work with completely different\n\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\n\ndependency-injectable.\n\n**Incorrect: UI coupled to specific state implementation**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct: generic interface enables dependency injection**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe provider boundary is what matters—not the visual nesting. Components that\n\nneed shared state don't have to be inside the `Composer.Frame`. They just need\n\nto be within the provider.\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\n\nbox, but they can still access its state and actions. This is the power of\n\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\n\nby the provider. Swap the provider, keep the UI.\n\n### 2.3 Lift State into Provider Components\n\n**Impact: HIGH (enables state sharing outside component boundaries)**\n\nMove state management into dedicated provider components. This allows sibling\n\ncomponents outside the main UI to access and modify state without prop drilling\n\nor awkward refs.\n\n**Incorrect: state trapped inside component**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect: useEffect to sync state up**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect: reading state from ref on submit**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct: state lifted to provider**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\n\nsubmit action because it's within the provider. Even though it's a one-off\n\ncomponent, it can still access the composer's state and actions from outside the\n\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\n\nnested inside each other—they just need to be within the same provider.\n\n---\n\n## 3. Implementation Patterns\n\n**Impact: MEDIUM**\n\nSpecific techniques for implementing compound components and\ncontext providers.\n\n### 3.1 Create Explicit Component Variants\n\n**Impact: MEDIUM (self-documenting code, no hidden conditionals)**\n\nInstead of one component with many boolean props, create explicit variant\n\ncomponents. Each variant composes the pieces it needs. The code documents\n\nitself.\n\n**Incorrect: one component, many modes**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct: explicit variants**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\n\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n\n- What UI elements it includes\n\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n\n### 3.2 Prefer Composing Children Over Render Props\n\n**Impact: MEDIUM (cleaner composition, better readability)**\n\nUse `children` for composition instead of `renderX` props. Children are more\n\nreadable, compose naturally, and don't require understanding callback\n\nsignatures.\n\n**Incorrect: render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct: compound components with children**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\n\nUse children when composing static structure.\n\n---\n\n## 4. React 19 APIs\n\n**Impact: MEDIUM**\n\nReact 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n\n### 4.1 React 19 API Changes\n\n**Impact: MEDIUM (cleaner component definitions and context usage)**\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect: forwardRef in React 19**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct: ref as a regular prop**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect: useContext in React 19**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct: use instead of useContext**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n\n---\n\n## References\n\n1. [https://react.dev](https://react.dev)\n2. [https://react.dev/learn/passing-data-deeply-with-context](https://react.dev/learn/passing-data-deeply-with-context)\n3. [https://react.dev/reference/react/use](https://react.dev/reference/react/use)\n","isInternal":false,"tokens":5063,"sizeBytes":22627},{"name":"metadata.json","path":"skills/composition-patterns/metadata.json","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/metadata.json","title":"Composition-patterns Skill","category":"anthropic-skill","format":"json","content":"{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://react.dev/learn/passing-data-deeply-with-context\",\n    \"https://react.dev/reference/react/use\"\n  ]\n}\n","frontmatter":{"version":"1.0.0","organization":"Engineering","date":"January 2026","abstract":"Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.","references":["https://react.dev","https://react.dev/learn/passing-data-deeply-with-context","https://react.dev/reference/react/use"]},"isInternal":false,"tokens":122,"sizeBytes":530},{"name":"README.md","path":"skills/composition-patterns/README.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/README.md","title":"Composition-patterns Skill","category":"anthropic-skill","format":"markdown","content":"# React Composition Patterns\n\nA structured repository for React composition patterns that scale. These\npatterns help avoid boolean prop proliferation by using compound components,\nlifting state, and composing internals.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Rules\n\n### Component Architecture (CRITICAL)\n\n- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize\n  behavior\n- `architecture-compound-components.md` - Structure as compound components with\n  shared context\n\n### State Management (HIGH)\n\n- `state-lift-state.md` - Lift state into provider components\n- `state-context-interface.md` - Define clear context interfaces\n  (state/actions/meta)\n- `state-decouple-implementation.md` - Decouple state management from UI\n\n### Implementation Patterns (MEDIUM)\n\n- `patterns-children-over-render-props.md` - Prefer children over renderX props\n- `patterns-explicit-variants.md` - Create explicit component variants\n\n## Core Principles\n\n1. **Composition over configuration** — Instead of adding props, let consumers\n   compose\n2. **Lift your state** — State in providers, not trapped in components\n3. **Compose your internals** — Subcomponents access context, not props\n4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer\n   with isThread\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `architecture-` for Component Architecture\n   - `state-` for State Management\n   - `patterns-` for Implementation Patterns\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n\n## Impact Levels\n\n- `CRITICAL` - Foundational patterns, prevents unmaintainable code\n- `HIGH` - Significant maintainability improvements\n- `MEDIUM` - Good practices for cleaner code\n","isInternal":false,"tokens":476,"sizeBytes":2140},{"name":"_sections.md","path":"skills/composition-patterns/rules/_sections.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/_sections.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Component Architecture (architecture)\n\n**Impact:** HIGH  \n**Description:** Fundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n## 2. State Management (state)\n\n**Impact:** MEDIUM  \n**Description:** Patterns for lifting state and managing shared context across\ncomposed components.\n\n## 3. Implementation Patterns (patterns)\n\n**Impact:** MEDIUM  \n**Description:** Specific techniques for implementing compound components and\ncontext providers.\n\n## 4. React 19 APIs (react19)\n\n**Impact:** MEDIUM  \n**Description:** React 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n","isInternal":false,"tokens":176,"sizeBytes":829},{"name":"_template.md","path":"skills/composition-patterns/rules/_template.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/_template.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: brief description of impact\ntags: composition, components\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect:**\n\n```tsx\n// Bad code example\n```\n\n**Correct:**\n\n```tsx\n// Good code example\n```\n\nReference: [Link](https://example.com)\n","frontmatter":{"title":"Rule Title Here","impact":"MEDIUM","impactDescription":"brief description of impact","tags":"composition, components"},"isInternal":false,"tokens":80,"sizeBytes":329},{"name":"architecture-avoid-boolean-props.md","path":"skills/composition-patterns/rules/architecture-avoid-boolean-props.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/architecture-avoid-boolean-props.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Avoid Boolean Prop Proliferation\nimpact: CRITICAL\nimpactDescription: prevents unmaintainable component variants\ntags: composition, props, architecture\n---\n\n## Avoid Boolean Prop Proliferation\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\ncomponent behavior. Each boolean doubles possible states and creates\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect (boolean props create exponential complexity):**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct (composition eliminates conditionals):**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\nsharing a single monolithic parent.\n","frontmatter":{"title":"Avoid Boolean Prop Proliferation","impact":"CRITICAL","impactDescription":"prevents unmaintainable component variants","tags":"composition, props, architecture"},"isInternal":false,"tokens":513,"sizeBytes":2267},{"name":"architecture-compound-components.md","path":"skills/composition-patterns/rules/architecture-compound-components.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/architecture-compound-components.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Compound Components\nimpact: HIGH\nimpactDescription: enables flexible composition without prop drilling\ntags: composition, compound-components, architecture\n---\n\n## Use Compound Components\n\nStructure complex components as compound components with a shared context. Each\nsubcomponent accesses shared state via context, not props. Consumers compose the\npieces they need.\n\n**Incorrect (monolithic component with render props):**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct (compound components with shared context):**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n","frontmatter":{"title":"Use Compound Components","impact":"HIGH","impactDescription":"enables flexible composition without prop drilling","tags":"composition, compound-components, architecture"},"isInternal":false,"tokens":578,"sizeBytes":2600},{"name":"patterns-children-over-render-props.md","path":"skills/composition-patterns/rules/patterns-children-over-render-props.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/patterns-children-over-render-props.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Prefer Composing Children Over Render Props\nimpact: MEDIUM\nimpactDescription: cleaner composition, better readability\ntags: composition, children, render-props\n---\n\n## Prefer Children Over Render Props\n\nUse `children` for composition instead of `renderX` props. Children are more\nreadable, compose naturally, and don't require understanding callback\nsignatures.\n\n**Incorrect (render props):**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct (compound components with children):**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\nUse children when composing static structure.\n","frontmatter":{"title":"Prefer Composing Children Over Render Props","impact":"MEDIUM","impactDescription":"cleaner composition, better readability","tags":"composition, children, render-props"},"isInternal":false,"tokens":428,"sizeBytes":1886},{"name":"patterns-explicit-variants.md","path":"skills/composition-patterns/rules/patterns-explicit-variants.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/patterns-explicit-variants.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Create Explicit Component Variants\nimpact: MEDIUM\nimpactDescription: self-documenting code, no hidden conditionals\ntags: composition, variants, architecture\n---\n\n## Create Explicit Component Variants\n\nInstead of one component with many boolean props, create explicit variant\ncomponents. Each variant composes the pieces it needs. The code documents\nitself.\n\n**Incorrect (one component, many modes):**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct (explicit variants):**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n- What UI elements it includes\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n","frontmatter":{"title":"Create Explicit Component Variants","impact":"MEDIUM","impactDescription":"self-documenting code, no hidden conditionals","tags":"composition, variants, architecture"},"isInternal":false,"tokens":503,"sizeBytes":2395},{"name":"react19-no-forwardref.md","path":"skills/composition-patterns/rules/react19-no-forwardref.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/react19-no-forwardref.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: React 19 API Changes\nimpact: MEDIUM\nimpactDescription: cleaner component definitions and context usage\ntags: react19, refs, context, hooks\n---\n\n## React 19 API Changes\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect (forwardRef in React 19):**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct (ref as a regular prop):**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect (useContext in React 19):**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct (use instead of useContext):**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n","frontmatter":{"title":"React 19 API Changes","impact":"MEDIUM","impactDescription":"cleaner component definitions and context usage","tags":"react19, refs, context, hooks"},"isInternal":false,"tokens":255,"sizeBytes":953},{"name":"state-context-interface.md","path":"skills/composition-patterns/rules/state-context-interface.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/state-context-interface.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Define Generic Context Interfaces for Dependency Injection\nimpact: HIGH\nimpactDescription: enables dependency-injectable state across use-cases\ntags: composition, context, state, typescript, dependency-injection\n---\n\n## Define Generic Context Interfaces for Dependency Injection\n\nDefine a **generic interface** for your component context with three parts:\n`state`, `actions`, and `meta`. This interface is a contract that any provider\ncan implement—enabling the same UI components to work with completely different\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\ndependency-injectable.\n\n**Incorrect (UI coupled to specific state implementation):**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct (generic interface enables dependency injection):**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\nThe provider boundary is what matters—not the visual nesting. Components that\nneed shared state don't have to be inside the `Composer.Frame`. They just need\nto be within the provider.\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\nbox, but they can still access its state and actions. This is the power of\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\nby the provider. Swap the provider, keep the UI.\n","frontmatter":{"title":"Define Generic Context Interfaces for Dependency Injection","impact":"HIGH","impactDescription":"enables dependency-injectable state across use-cases","tags":"composition, context, state, typescript, dependency-injection"},"isInternal":false,"tokens":1076,"sizeBytes":4974},{"name":"state-decouple-implementation.md","path":"skills/composition-patterns/rules/state-decouple-implementation.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/state-decouple-implementation.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Decouple State Management from UI\nimpact: MEDIUM\nimpactDescription: enables swapping state implementations without changing UI\ntags: composition, state, architecture\n---\n\n## Decouple State Management from UI\n\nThe provider component should be the only place that knows how state is managed.\nUI components consume the context interface—they don't know if state comes from\nuseState, Zustand, or a server sync.\n\n**Incorrect (UI coupled to state implementation):**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct (state management isolated in provider):**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\ndepends on the context interface, not the implementation.\n","frontmatter":{"title":"Decouple State Management from UI","impact":"MEDIUM","impactDescription":"enables swapping state implementations without changing UI","tags":"composition, state, architecture"},"isInternal":false,"tokens":579,"sizeBytes":2699},{"name":"state-lift-state.md","path":"skills/composition-patterns/rules/state-lift-state.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/rules/state-lift-state.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Lift State into Provider Components\nimpact: HIGH\nimpactDescription: enables state sharing outside component boundaries\ntags: composition, state, context, providers\n---\n\n## Lift State into Provider Components\n\nMove state management into dedicated provider components. This allows sibling\ncomponents outside the main UI to access and modify state without prop drilling\nor awkward refs.\n\n**Incorrect (state trapped inside component):**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect (useEffect to sync state up):**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect (reading state from ref on submit):**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct (state lifted to provider):**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\nsubmit action because it's within the provider. Even though it's a one-off\ncomponent, it can still access the composer's state and actions from outside the\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\nnested inside each other—they just need to be within the same provider.\n","frontmatter":{"title":"Lift State into Provider Components","impact":"HIGH","impactDescription":"enables state sharing outside component boundaries","tags":"composition, state, context, providers"},"isInternal":false,"tokens":686,"sizeBytes":3224},{"name":"SKILL.md","path":"skills/composition-patterns/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/composition-patterns/SKILL.md","title":"vercel-composition-patterns","category":"anthropic-skill","format":"markdown","content":"---\nname: vercel-composition-patterns\ndescription:\n  React composition patterns that scale. Use when refactoring components with\n  boolean prop proliferation, building flexible component libraries, or\n  designing reusable APIs. Triggers on tasks involving compound components,\n  render props, context providers, or component architecture. Includes React 19\n  API changes.\nlicense: MIT\nmetadata:\n  author: vercel\n  version: '1.0.0'\n---\n\n# React Composition Patterns\n\nComposition patterns for building flexible, maintainable React components. Avoid\nboolean prop proliferation by using compound components, lifting state, and\ncomposing internals. These patterns make codebases easier for both humans and AI\nagents to work with as they scale.\n\n## When to Apply\n\nReference these guidelines when:\n\n- Refactoring components with many boolean props\n- Building reusable component libraries\n- Designing flexible component APIs\n- Reviewing component architecture\n- Working with compound components or context providers\n\n## Rule Categories by Priority\n\n| Priority | Category                | Impact | Prefix          |\n| -------- | ----------------------- | ------ | --------------- |\n| 1        | Component Architecture  | HIGH   | `architecture-` |\n| 2        | State Management        | MEDIUM | `state-`        |\n| 3        | Implementation Patterns | MEDIUM | `patterns-`     |\n| 4        | React 19 APIs           | MEDIUM | `react19-`      |\n\n## Quick Reference\n\n### 1. Component Architecture (HIGH)\n\n- `architecture-avoid-boolean-props` - Don't add boolean props to customize\n  behavior; use composition\n- `architecture-compound-components` - Structure complex components with shared\n  context\n\n### 2. State Management (MEDIUM)\n\n- `state-decouple-implementation` - Provider is the only place that knows how\n  state is managed\n- `state-context-interface` - Define generic interface with state, actions, meta\n  for dependency injection\n- `state-lift-state` - Move state into provider components for sibling access\n\n### 3. Implementation Patterns (MEDIUM)\n\n- `patterns-explicit-variants` - Create explicit variant components instead of\n  boolean modes\n- `patterns-children-over-render-props` - Use children for composition instead\n  of renderX props\n\n### 4. React 19 APIs (MEDIUM)\n\n> **⚠️ React 19+ only.** Skip this section if using React 18 or earlier.\n\n- `react19-no-forwardref` - Don't use `forwardRef`; use `use()` instead of `useContext()`\n\n## How to Use\n\nRead individual rule files for detailed explanations and code examples:\n\n```\nrules/architecture-avoid-boolean-props.md\nrules/state-context-interface.md\n```\n\nEach rule file contains:\n\n- Brief explanation of why it matters\n- Incorrect code example with explanation\n- Correct code example with explanation\n- Additional context and references\n\n## Full Compiled Document\n\nFor the complete guide with all rules expanded: `AGENTS.md`\n","frontmatter":{"name":"vercel-composition-patterns","description":"React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.","license":"MIT","metadata":{"author":"vercel","version":"1.0.0"}},"isInternal":false,"tokens":630,"sizeBytes":2886},{"name":"SKILL.md","path":"skills/deploy-to-vercel/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/deploy-to-vercel/SKILL.md","title":"deploy-to-vercel","category":"anthropic-skill","format":"markdown","content":"---\nname: deploy-to-vercel\ndescription: Deploy applications and websites to Vercel. Use when the user requests deployment actions like \"deploy my app\", \"deploy and give me the link\", \"push this live\", or \"create a preview deployment\".\nmetadata:\n  author: vercel\n  version: \"3.0.0\"\n---\n\n# Deploy to Vercel\n\nDeploy any project to Vercel. **Always deploy as preview** (not production) unless the user explicitly asks for production.\n\nThe goal is to get the user into the best long-term setup: their project linked to Vercel with git-push deploys. Every method below tries to move the user closer to that state.\n\n## Step 1: Gather Project State\n\nRun all four checks before deciding which method to use:\n\n```bash\n# 1. Check for a git remote\ngit remote get-url origin 2>/dev/null\n\n# 2. Check if locally linked to a Vercel project (either file means linked)\ncat .vercel/project.json 2>/dev/null || cat .vercel/repo.json 2>/dev/null\n\n# 3. Check if the Vercel CLI is installed and authenticated\nvercel whoami 2>/dev/null\n\n# 4. List available teams (if authenticated)\nvercel teams list --format json 2>/dev/null\n```\n\n### Team selection\n\nIf the user belongs to multiple teams, present all available team slugs as a bulleted list and ask which one to deploy to. Once the user picks a team, proceed immediately to the next step — do not ask for additional confirmation.\n\nPass the team slug via `--scope` on all subsequent CLI commands (`vercel deploy`, `vercel link`, `vercel inspect`, etc.):\n\n```bash\nvercel deploy [path] -y --no-wait --scope <team-slug>\n```\n\nIf the project is already linked (`.vercel/project.json` or `.vercel/repo.json` exists), the `orgId` in those files determines the team — no need to ask again. If there is only one team (or just a personal account), skip the prompt and use it directly.\n\n**About the `.vercel/` directory:** A linked project has either:\n- `.vercel/project.json` — created by `vercel link` (single project linking). Contains `projectId` and `orgId`.\n- `.vercel/repo.json` — created by `vercel link --repo` (repo-based linking). Contains `orgId`, `remoteName`, and a `projects` array mapping directories to Vercel project IDs.\n\nEither file means the project is linked. Check for both.\n\n**Do NOT** use `vercel project inspect`, `vercel ls`, or `vercel link` to detect state in an unlinked directory — without a `.vercel/` config, they will interactively prompt (or with `--yes`, silently link as a side-effect). Only `vercel whoami` is safe to run anywhere.\n\n## Step 2: Choose a Deploy Method\n\n### Linked (`.vercel/` exists) + has git remote → Git Push\n\nThis is the ideal state. The project is linked and has git integration.\n\n1. **Ask the user before pushing.** Never push without explicit approval:\n   ```\n   This project is connected to Vercel via git. I can commit and push to\n   trigger a deployment. Want me to proceed?\n   ```\n\n2. **Commit and push:**\n   ```bash\n   git add .\n   git commit -m \"deploy: <description of changes>\"\n   git push\n   ```\n   Vercel automatically builds from the push. Non-production branches get preview deployments; the production branch (usually `main`) gets a production deployment.\n\n3. **Retrieve the preview URL.** If the CLI is authenticated:\n   ```bash\n   sleep 5\n   vercel ls --format json\n   ```\n   The JSON output has a `deployments` array. Find the latest entry — its `url` field is the preview URL.\n\n   If the CLI is not authenticated, tell the user to check the Vercel dashboard or the commit status checks on their git provider for the preview URL.\n\n---\n\n### Linked (`.vercel/` exists) + no git remote → `vercel deploy`\n\nThe project is linked but there's no git repo. Deploy directly with the CLI.\n\n```bash\nvercel deploy [path] -y --no-wait\n```\n\nUse `--no-wait` so the CLI returns immediately with the deployment URL instead of blocking until the build finishes (builds can take a while). Then check on the deployment status with:\n\n```bash\nvercel inspect <deployment-url>\n```\n\nFor production deploys (only if user explicitly asks):\n```bash\nvercel deploy [path] --prod -y --no-wait\n```\n\n---\n\n### Not linked + CLI is authenticated → Link first, then deploy\n\nThe CLI is working but the project isn't linked yet. This is the opportunity to get the user into the best state.\n\n1. **Ask the user which team to deploy to.** Present the team slugs from Step 1 as a bulleted list. If there's only one team (or just a personal account), skip this step.\n\n2. **Once a team is selected, proceed directly to linking.** Tell the user what will happen but do not ask for separate confirmation:\n   ```\n   Linking this project to <team name> on Vercel. This will create a Vercel\n   project to deploy to and enable automatic deployments on future git pushes.\n   ```\n\n3. **If a git remote exists**, use repo-based linking with the selected team scope:\n   ```bash\n   vercel link --repo --scope <team-slug>\n   ```\n   This reads the git remote URL and matches it to existing Vercel projects that deploy from that repo. It creates `.vercel/repo.json`. This is much more reliable than `vercel link` (without `--repo`), which tries to match by directory name and often fails when the local folder and Vercel project are named differently.\n\n   **If there is no git remote**, fall back to standard linking:\n   ```bash\n   vercel link --scope <team-slug>\n   ```\n   This prompts the user to select or create a project. It creates `.vercel/project.json`.\n\n4. **Then deploy using the best available method:**\n   - If a git remote exists → commit and push (see git push method above)\n   - If no git remote → `vercel deploy [path] -y --no-wait --scope <team-slug>`, then `vercel inspect <url>` to check status\n\n---\n\n### Not linked + CLI not authenticated → Install, auth, link, deploy\n\nThe Vercel CLI isn't set up at all.\n\n1. **Install the CLI (if not already installed):**\n   ```bash\n   npm install -g vercel\n   ```\n\n2. **Authenticate:**\n   ```bash\n   vercel login\n   ```\n   The user completes auth in their browser. If running in a non-interactive environment where login is not possible, skip to the **no-auth fallback** below.\n\n3. **Ask which team to deploy to** — present team slugs from `vercel teams list --format json` as a bulleted list. If only one team / personal account, skip. Once selected, proceed immediately.\n\n4. **Link the project** with the selected team scope (use `--repo` if a git remote exists, plain `vercel link` otherwise):\n   ```bash\n   vercel link --repo --scope <team-slug>   # if git remote exists\n   vercel link --scope <team-slug>          # if no git remote\n   ```\n\n5. **Deploy** using the best available method (git push if remote exists, otherwise `vercel deploy -y --no-wait --scope <team-slug>`, then `vercel inspect <url>` to check status).\n\n---\n\n### No-Auth Fallback — claude.ai sandbox\n\n**When to use:** Last resort when the CLI can't be installed or authenticated in the claude.ai sandbox. This requires no authentication — it returns a **Preview URL** (live site) and a **Claim URL** (transfer to your Vercel account).\n\n```bash\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh [path]\n```\n\n**Arguments:**\n- `path` - Directory to deploy, or a `.tgz` file (defaults to current directory)\n\n**Examples:**\n```bash\n# Deploy current directory\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh\n\n# Deploy specific project\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh /path/to/project\n\n# Deploy existing tarball\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh /path/to/project.tgz\n```\n\nThe script auto-detects the framework from `package.json`, packages the project (excluding `node_modules`, `.git`, `.env`), uploads it, and waits for the build to complete.\n\n**Tell the user:** \"Your deployment is ready at [previewUrl]. Claim it at [claimUrl] to manage your deployment.\"\n\n---\n\n### No-Auth Fallback — Codex sandbox\n\n**When to use:** In the Codex sandbox where the CLI may not be authenticated. Codex runs in a sandboxed environment by default — try the CLI first, and fall back to the deploy script if auth fails.\n\n1. **Check whether the Vercel CLI is installed** (no escalation needed for this check):\n   ```bash\n   command -v vercel\n   ```\n\n2. **If `vercel` is installed**, try deploying with the CLI:\n   ```bash\n   vercel deploy [path] -y --no-wait\n   ```\n\n3. **If `vercel` is not installed, or the CLI fails with \"No existing credentials found\"**, use the fallback script:\n   ```bash\n   skill_dir=\"<path-to-skill>\"\n\n   # Deploy current directory\n   bash \"$skill_dir/resources/deploy-codex.sh\"\n\n   # Deploy specific project\n   bash \"$skill_dir/resources/deploy-codex.sh\" /path/to/project\n\n   # Deploy existing tarball\n   bash \"$skill_dir/resources/deploy-codex.sh\" /path/to/project.tgz\n   ```\n\nThe script handles framework detection, packaging, and deployment. It waits for the build to complete and returns JSON with `previewUrl` and `claimUrl`.\n\n**Tell the user:** \"Your deployment is ready at [previewUrl]. Claim it at [claimUrl] to manage your deployment.\"\n\n**Escalated network access:** Only escalate the actual deploy command if sandboxing blocks the network call (`sandbox_permissions=require_escalated`). Do **not** escalate the `command -v vercel` check.\n\n---\n\n## Agent-Specific Notes\n\n### Claude Code / terminal-based agents\n\nYou have full shell access. Do NOT use the `/mnt/skills/` path. Follow the decision flow above using the CLI directly.\n\nFor the no-auth fallback, run the deploy script from the skill's installed location:\n```bash\nbash ~/.claude/skills/deploy-to-vercel/resources/deploy.sh [path]\n```\nThe path may vary depending on where the user installed the skill.\n\n### Sandboxed environments (claude.ai)\n\nYou likely cannot run `vercel login` or `git push`. Go directly to the **no-auth fallback — claude.ai sandbox**.\n\n### Codex\n\nCodex runs in a sandbox. Check if the CLI is available first, then fall back to the deploy script. Go to the **no-auth fallback — Codex sandbox**.\n\n---\n\n## Output\n\nAlways show the user the deployment URL.\n\n- **Git push:** Use `vercel ls --format json` to find the preview URL. If the CLI isn't authenticated, tell the user to check the Vercel dashboard or commit status checks.\n- **CLI deploy:** Show the URL returned by `vercel deploy --no-wait`. Use `vercel inspect <url>` to check build status and report it to the user.\n- **No-auth fallback:** Show both the preview URL and the claim URL:\n  ```\n  Deployment successful!\n\n  Preview URL: https://my-app-abc123.vercel.app\n  Claim URL:   https://vercel.com/claim-deployment?code=...\n\n  View your site at the Preview URL.\n  To transfer this deployment to your Vercel account, visit the Claim URL.\n  ```\n\n**Do not** curl or fetch the deployed URL to verify it works. Just return the link.\n\n---\n\n## Troubleshooting\n\n### Network Egress Error (claude.ai)\n\nIf deployment fails due to network restrictions on claude.ai, tell the user:\n\n```\nDeployment failed due to network restrictions. To fix this:\n\n1. Go to https://claude.ai/settings/capabilities\n2. Add *.vercel.com to the allowed domains\n3. Try deploying again\n```\n\n### Escalated Network Access (Codex / sandboxed environments)\n\nIf deployment fails due to network issues (timeouts, DNS errors, connection resets) in a sandboxed environment, rerun with escalated permissions (`sandbox_permissions=require_escalated`). Do not escalate the initial CLI availability check — only escalate the actual deploy command.\n\nExample guidance to the user:\n```\nThe deploy needs escalated network access to deploy to Vercel. I can rerun\nthe command with escalated permissions — want me to proceed?\n```\n\n### CLI Auth Failure\n\nIf `vercel login` or `vercel deploy` fails with authentication errors, fall back to the no-auth deploy script (claude.ai or Codex variant, depending on the environment).\n","frontmatter":{"name":"deploy-to-vercel","description":"Deploy applications and websites to Vercel. Use when the user requests deployment actions like \"deploy my app\", \"deploy and give me the link\", \"push this live\", or \"create a preview deployment\".","metadata":{"author":"vercel","version":"3.0.0"}},"isInternal":false,"tokens":2898,"sizeBytes":11786},{"name":"AGENTS.md","path":"skills/react-best-practices/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/AGENTS.md","title":"React-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# React Best Practices\n\n**Version 1.0.0**  \nVercel Engineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React and Next.js codebases. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\n\n---\n\n## Table of Contents\n\n1. [Eliminating Waterfalls](#1-eliminating-waterfalls) — **CRITICAL**\n   - 1.1 [Check Cheap Conditions Before Async Flags](#11-check-cheap-conditions-before-async-flags)\n   - 1.2 [Defer Await Until Needed](#12-defer-await-until-needed)\n   - 1.3 [Dependency-Based Parallelization](#13-dependency-based-parallelization)\n   - 1.4 [Prevent Waterfall Chains in API Routes](#14-prevent-waterfall-chains-in-api-routes)\n   - 1.5 [Promise.all() for Independent Operations](#15-promiseall-for-independent-operations)\n   - 1.6 [Strategic Suspense Boundaries](#16-strategic-suspense-boundaries)\n2. [Bundle Size Optimization](#2-bundle-size-optimization) — **CRITICAL**\n   - 2.1 [Avoid Barrel File Imports](#21-avoid-barrel-file-imports)\n   - 2.2 [Conditional Module Loading](#22-conditional-module-loading)\n   - 2.3 [Defer Non-Critical Third-Party Libraries](#23-defer-non-critical-third-party-libraries)\n   - 2.4 [Dynamic Imports for Heavy Components](#24-dynamic-imports-for-heavy-components)\n   - 2.5 [Prefer Statically Analyzable Paths](#25-prefer-statically-analyzable-paths)\n   - 2.6 [Preload Based on User Intent](#26-preload-based-on-user-intent)\n3. [Server-Side Performance](#3-server-side-performance) — **HIGH**\n   - 3.1 [Authenticate Server Actions Like API Routes](#31-authenticate-server-actions-like-api-routes)\n   - 3.2 [Avoid Duplicate Serialization in RSC Props](#32-avoid-duplicate-serialization-in-rsc-props)\n   - 3.3 [Avoid Shared Module State for Request Data](#33-avoid-shared-module-state-for-request-data)\n   - 3.4 [Cross-Request LRU Caching](#34-cross-request-lru-caching)\n   - 3.5 [Hoist Static I/O to Module Level](#35-hoist-static-io-to-module-level)\n   - 3.6 [Minimize Serialization at RSC Boundaries](#36-minimize-serialization-at-rsc-boundaries)\n   - 3.7 [Parallel Data Fetching with Component Composition](#37-parallel-data-fetching-with-component-composition)\n   - 3.8 [Parallel Nested Data Fetching](#38-parallel-nested-data-fetching)\n   - 3.9 [Per-Request Deduplication with React.cache()](#39-per-request-deduplication-with-reactcache)\n   - 3.10 [Use after() for Non-Blocking Operations](#310-use-after-for-non-blocking-operations)\n4. [Client-Side Data Fetching](#4-client-side-data-fetching) — **MEDIUM-HIGH**\n   - 4.1 [Deduplicate Global Event Listeners](#41-deduplicate-global-event-listeners)\n   - 4.2 [Use Passive Event Listeners for Scrolling Performance](#42-use-passive-event-listeners-for-scrolling-performance)\n   - 4.3 [Use SWR for Automatic Deduplication](#43-use-swr-for-automatic-deduplication)\n   - 4.4 [Version and Minimize localStorage Data](#44-version-and-minimize-localstorage-data)\n5. [Re-render Optimization](#5-re-render-optimization) — **MEDIUM**\n   - 5.1 [Calculate Derived State During Rendering](#51-calculate-derived-state-during-rendering)\n   - 5.2 [Defer State Reads to Usage Point](#52-defer-state-reads-to-usage-point)\n   - 5.3 [Do not wrap a simple expression with a primitive result type in useMemo](#53-do-not-wrap-a-simple-expression-with-a-primitive-result-type-in-usememo)\n   - 5.4 [Don't Define Components Inside Components](#54-dont-define-components-inside-components)\n   - 5.5 [Extract Default Non-primitive Parameter Value from Memoized Component to Constant](#55-extract-default-non-primitive-parameter-value-from-memoized-component-to-constant)\n   - 5.6 [Extract to Memoized Components](#56-extract-to-memoized-components)\n   - 5.7 [Narrow Effect Dependencies](#57-narrow-effect-dependencies)\n   - 5.8 [Put Interaction Logic in Event Handlers](#58-put-interaction-logic-in-event-handlers)\n   - 5.9 [Split Combined Hook Computations](#59-split-combined-hook-computations)\n   - 5.10 [Subscribe to Derived State](#510-subscribe-to-derived-state)\n   - 5.11 [Use Functional setState Updates](#511-use-functional-setstate-updates)\n   - 5.12 [Use Lazy State Initialization](#512-use-lazy-state-initialization)\n   - 5.13 [Use Transitions for Non-Urgent Updates](#513-use-transitions-for-non-urgent-updates)\n   - 5.14 [Use useDeferredValue for Expensive Derived Renders](#514-use-usedeferredvalue-for-expensive-derived-renders)\n   - 5.15 [Use useRef for Transient Values](#515-use-useref-for-transient-values)\n6. [Rendering Performance](#6-rendering-performance) — **MEDIUM**\n   - 6.1 [Animate SVG Wrapper Instead of SVG Element](#61-animate-svg-wrapper-instead-of-svg-element)\n   - 6.2 [CSS content-visibility for Long Lists](#62-css-content-visibility-for-long-lists)\n   - 6.3 [Hoist Static JSX Elements](#63-hoist-static-jsx-elements)\n   - 6.4 [Optimize SVG Precision](#64-optimize-svg-precision)\n   - 6.5 [Prevent Hydration Mismatch Without Flickering](#65-prevent-hydration-mismatch-without-flickering)\n   - 6.6 [Suppress Expected Hydration Mismatches](#66-suppress-expected-hydration-mismatches)\n   - 6.7 [Use Activity Component for Show/Hide](#67-use-activity-component-for-showhide)\n   - 6.8 [Use defer or async on Script Tags](#68-use-defer-or-async-on-script-tags)\n   - 6.9 [Use Explicit Conditional Rendering](#69-use-explicit-conditional-rendering)\n   - 6.10 [Use React DOM Resource Hints](#610-use-react-dom-resource-hints)\n   - 6.11 [Use useTransition Over Manual Loading States](#611-use-usetransition-over-manual-loading-states)\n7. [JavaScript Performance](#7-javascript-performance) — **LOW-MEDIUM**\n   - 7.1 [Avoid Layout Thrashing](#71-avoid-layout-thrashing)\n   - 7.2 [Build Index Maps for Repeated Lookups](#72-build-index-maps-for-repeated-lookups)\n   - 7.3 [Cache Property Access in Loops](#73-cache-property-access-in-loops)\n   - 7.4 [Cache Repeated Function Calls](#74-cache-repeated-function-calls)\n   - 7.5 [Cache Storage API Calls](#75-cache-storage-api-calls)\n   - 7.6 [Combine Multiple Array Iterations](#76-combine-multiple-array-iterations)\n   - 7.7 [Defer Non-Critical Work with requestIdleCallback](#77-defer-non-critical-work-with-requestidlecallback)\n   - 7.8 [Early Length Check for Array Comparisons](#78-early-length-check-for-array-comparisons)\n   - 7.9 [Early Return from Functions](#79-early-return-from-functions)\n   - 7.10 [Hoist RegExp Creation](#710-hoist-regexp-creation)\n   - 7.11 [Use flatMap to Map and Filter in One Pass](#711-use-flatmap-to-map-and-filter-in-one-pass)\n   - 7.12 [Use Loop for Min/Max Instead of Sort](#712-use-loop-for-minmax-instead-of-sort)\n   - 7.13 [Use Set/Map for O(1) Lookups](#713-use-setmap-for-o1-lookups)\n   - 7.14 [Use toSorted() Instead of sort() for Immutability](#714-use-tosorted-instead-of-sort-for-immutability)\n8. [Advanced Patterns](#8-advanced-patterns) — **LOW**\n   - 8.1 [Do Not Put Effect Events in Dependency Arrays](#81-do-not-put-effect-events-in-dependency-arrays)\n   - 8.2 [Initialize App Once, Not Per Mount](#82-initialize-app-once-not-per-mount)\n   - 8.3 [Store Event Handlers in Refs](#83-store-event-handlers-in-refs)\n   - 8.4 [useEffectEvent for Stable Callback Refs](#84-useeffectevent-for-stable-callback-refs)\n\n---\n\n## 1. Eliminating Waterfalls\n\n**Impact: CRITICAL**\n\nWaterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.\n\n### 1.1 Check Cheap Conditions Before Async Flags\n\n**Impact: HIGH (avoids unnecessary async work when a synchronous guard already fails)**\n\nWhen a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n\nThis is a specialization of [Defer Await Until Needed](./async-defer-await.md) for `flag && cheapCondition` style checks.\n\n**Incorrect:**\n\n```typescript\nconst someFlag = await getFlag()\n\nif (someFlag && someCondition) {\n  // ...\n}\n```\n\n**Correct:**\n\n```typescript\nif (someCondition) {\n  const someFlag = await getFlag()\n  if (someFlag) {\n    // ...\n  }\n}\n```\n\nThis matters when `getFlag` hits the network, a feature-flag service, or `React.cache` / DB work: skipping it when `someCondition` is false removes that cost on the cold path.\n\nKeep the original order if `someCondition` is expensive, depends on the flag, or you must run side effects in a fixed order.\n\n### 1.2 Defer Await Until Needed\n\n**Impact: HIGH (avoids blocking unused code paths)**\n\nMove `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n\n**Incorrect: blocks both branches**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  const userData = await fetchUserData(userId)\n  \n  if (skipProcessing) {\n    // Returns immediately but still waited for userData\n    return { skipped: true }\n  }\n  \n  // Only this branch uses userData\n  return processUserData(userData)\n}\n```\n\n**Correct: only blocks when needed**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  if (skipProcessing) {\n    // Returns immediately without waiting\n    return { skipped: true }\n  }\n  \n  // Fetch only when needed\n  const userData = await fetchUserData(userId)\n  return processUserData(userData)\n}\n```\n\n**Another example: early return optimization**\n\n```typescript\n// Incorrect: always fetches permissions\nasync function updateResource(resourceId: string, userId: string) {\n  const permissions = await fetchPermissions(userId)\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n\n// Correct: fetches only when needed\nasync function updateResource(resourceId: string, userId: string) {\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  const permissions = await fetchPermissions(userId)\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n```\n\nThis optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.\n\nFor `await getFlag()` combined with a cheap synchronous guard (`flag && someCondition`), see [Check Cheap Conditions Before Async Flags](./async-cheap-condition-before-await.md).\n\n### 1.3 Dependency-Based Parallelization\n\n**Impact: CRITICAL (2-10× improvement)**\n\nFor operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n\n**Incorrect: profile waits for config unnecessarily**\n\n```typescript\nconst [user, config] = await Promise.all([\n  fetchUser(),\n  fetchConfig()\n])\nconst profile = await fetchProfile(user.id)\n```\n\n**Correct: config and profile run in parallel**\n\n```typescript\nimport { all } from 'better-all'\n\nconst { user, config, profile } = await all({\n  async user() { return fetchUser() },\n  async config() { return fetchConfig() },\n  async profile() {\n    return fetchProfile((await this.$.user).id)\n  }\n})\n```\n\n**Alternative without extra dependencies:**\n\n```typescript\nconst userPromise = fetchUser()\nconst profilePromise = userPromise.then(user => fetchProfile(user.id))\n\nconst [user, config, profile] = await Promise.all([\n  userPromise,\n  fetchConfig(),\n  profilePromise\n])\n```\n\nWe can also create all the promises first, and do `Promise.all()` at the end.\n\nReference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)\n\n### 1.4 Prevent Waterfall Chains in API Routes\n\n**Impact: CRITICAL (2-10× improvement)**\n\nIn API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n\n**Incorrect: config waits for auth, data waits for both**\n\n```typescript\nexport async function GET(request: Request) {\n  const session = await auth()\n  const config = await fetchConfig()\n  const data = await fetchData(session.user.id)\n  return Response.json({ data, config })\n}\n```\n\n**Correct: auth and config start immediately**\n\n```typescript\nexport async function GET(request: Request) {\n  const sessionPromise = auth()\n  const configPromise = fetchConfig()\n  const session = await sessionPromise\n  const [config, data] = await Promise.all([\n    configPromise,\n    fetchData(session.user.id)\n  ])\n  return Response.json({ data, config })\n}\n```\n\nFor operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).\n\n### 1.5 Promise.all() for Independent Operations\n\n**Impact: CRITICAL (2-10× improvement)**\n\nWhen async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n\n**Incorrect: sequential execution, 3 round trips**\n\n```typescript\nconst user = await fetchUser()\nconst posts = await fetchPosts()\nconst comments = await fetchComments()\n```\n\n**Correct: parallel execution, 1 round trip**\n\n```typescript\nconst [user, posts, comments] = await Promise.all([\n  fetchUser(),\n  fetchPosts(),\n  fetchComments()\n])\n```\n\n### 1.6 Strategic Suspense Boundaries\n\n**Impact: HIGH (faster initial paint)**\n\nInstead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n\n**Incorrect: wrapper blocked by data fetching**\n\n```tsx\nasync function Page() {\n  const data = await fetchData() // Blocks entire page\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <DataDisplay data={data} />\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n```\n\nThe entire layout waits for data even though only the middle section needs it.\n\n**Correct: wrapper shows immediately, data streams in**\n\n```tsx\nfunction Page() {\n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <Suspense fallback={<Skeleton />}>\n          <DataDisplay />\n        </Suspense>\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nasync function DataDisplay() {\n  const data = await fetchData() // Only blocks this component\n  return <div>{data.content}</div>\n}\n```\n\nSidebar, Header, and Footer render immediately. Only DataDisplay waits for data.\n\n**Alternative: share promise across components**\n\n```tsx\nfunction Page() {\n  // Start fetch immediately, but don't await\n  const dataPromise = fetchData()\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <Suspense fallback={<Skeleton />}>\n        <DataDisplay dataPromise={dataPromise} />\n        <DataSummary dataPromise={dataPromise} />\n      </Suspense>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nfunction DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Unwraps the promise\n  return <div>{data.content}</div>\n}\n\nfunction DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Reuses the same promise\n  return <div>{data.summary}</div>\n}\n```\n\nBoth components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.\n\n**When NOT to use this pattern:**\n\n- Critical data needed for layout decisions (affects positioning)\n\n- SEO-critical content above the fold\n\n- Small, fast queries where suspense overhead isn't worth it\n\n- When you want to avoid layout shift (loading → content jump)\n\n**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.\n\n---\n\n## 2. Bundle Size Optimization\n\n**Impact: CRITICAL**\n\nReducing initial bundle size improves Time to Interactive and Largest Contentful Paint.\n\n### 2.1 Avoid Barrel File Imports\n\n**Impact: CRITICAL (200-800ms import cost, slow builds)**\n\nImport directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n\nPopular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.\n\n**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.\n\n**Incorrect: imports entire library**\n\n```tsx\nimport { Check, X, Menu } from 'lucide-react'\n// Loads 1,583 modules, takes ~2.8s extra in dev\n// Runtime cost: 200-800ms on every cold start\n\nimport { Button, TextField } from '@mui/material'\n// Loads 2,225 modules, takes ~4.2s extra in dev\n```\n\n**Correct - Next.js 13.5+ (recommended):**\n\n```tsx\n// Keep the standard imports - Next.js transforms them to direct imports\nimport { Check, X, Menu } from 'lucide-react'\n// Full TypeScript support, no manual path wrangling\n```\n\nThis is the recommended approach because it preserves TypeScript type safety and editor autocompletion while still eliminating the barrel import cost.\n\n**Correct - Direct imports (non-Next.js projects):**\n\n```tsx\nimport Button from '@mui/material/Button'\nimport TextField from '@mui/material/TextField'\n// Loads only what you use\n```\n\n> **TypeScript warning:** Some libraries (notably `lucide-react`) don't ship `.d.ts` files for their deep import paths. Importing from `lucide-react/dist/esm/icons/check` resolves to an implicit `any` type, causing errors under `strict` or `noImplicitAny`. Prefer `optimizePackageImports` when available, or verify the library exports types for its subpaths before using direct imports.\n\nThese optimizations provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.\n\nLibraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.\n\nReference: [https://vercel.com/blog/how-we-optimized-package-imports-in-next-js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)\n\n### 2.2 Conditional Module Loading\n\n**Impact: HIGH (loads large data only when needed)**\n\nLoad large data or modules only when a feature is activated.\n\n**Example: lazy-load animation frames**\n\n```tsx\nfunction AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {\n  const [frames, setFrames] = useState<Frame[] | null>(null)\n\n  useEffect(() => {\n    if (enabled && !frames && typeof window !== 'undefined') {\n      import('./animation-frames.js')\n        .then(mod => setFrames(mod.frames))\n        .catch(() => setEnabled(false))\n    }\n  }, [enabled, frames, setEnabled])\n\n  if (!frames) return <Skeleton />\n  return <Canvas frames={frames} />\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.\n\n### 2.3 Defer Non-Critical Third-Party Libraries\n\n**Impact: MEDIUM (loads after hydration)**\n\nAnalytics, logging, and error tracking don't block user interaction. Load them after hydration.\n\n**Incorrect: blocks initial bundle**\n\n```tsx\nimport { Analytics } from '@vercel/analytics/react'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n**Correct: loads after hydration**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst Analytics = dynamic(\n  () => import('@vercel/analytics/react').then(m => m.Analytics),\n  { ssr: false }\n)\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n### 2.4 Dynamic Imports for Heavy Components\n\n**Impact: CRITICAL (directly affects TTI and LCP)**\n\nUse `next/dynamic` to lazy-load large components not needed on initial render.\n\n**Incorrect: Monaco bundles with main chunk ~300KB**\n\n```tsx\nimport { MonacoEditor } from './monaco-editor'\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n**Correct: Monaco loads on demand**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst MonacoEditor = dynamic(\n  () => import('./monaco-editor').then(m => m.MonacoEditor),\n  { ssr: false }\n)\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n### 2.5 Prefer Statically Analyzable Paths\n\n**Impact: HIGH (avoids accidental broad bundles and file traces)**\n\nBuild tools work best when import and file-system paths are obvious at build time. If you hide the real path inside a variable or compose it too dynamically, the tool either has to include a broad set of possible files, warn that it cannot analyze the import, or widen file tracing to stay safe.\n\nPrefer explicit maps or literal paths so the set of reachable files stays narrow and predictable. This is the same rule whether you are choosing modules with `import()` or reading files in server/build code.\n\nWhen analysis becomes too broad, the cost is real:\n\n- Larger server bundles\n\n- Slower builds\n\n- Worse cold starts\n\n- More memory use\n\n**Incorrect: the bundler cannot tell what may be imported**\n\n```ts\nconst PAGE_MODULES = {\n  home: './pages/home',\n  settings: './pages/settings',\n} as const\n\nconst Page = await import(PAGE_MODULES[pageName])\n```\n\n**Correct: use an explicit map of allowed modules**\n\n```ts\nconst PAGE_MODULES = {\n  home: () => import('./pages/home'),\n  settings: () => import('./pages/settings'),\n} as const\n\nconst Page = await PAGE_MODULES[pageName]()\n```\n\n**Incorrect: a 2-value enum still hides the final path from static analysis**\n\n```ts\nconst baseDir = path.join(process.cwd(), 'content/' + contentKind)\n```\n\n**Correct: make each final path literal at the callsite**\n\n```ts\nconst baseDir =\n  kind === ContentKind.Blog\n    ? path.join(process.cwd(), 'content/blog')\n    : path.join(process.cwd(), 'content/docs')\n```\n\nIn Next.js server code, this matters for output file tracing too. `path.join(process.cwd(), someVar)` can widen the traced file set because Next.js statically analyze `import`, `require`, and `fs` usage.\n\nReference: [https://nextjs.org/docs/app/api-reference/config/next-config-js/output](https://nextjs.org/docs/app/api-reference/config/next-config-js/output), [https://nextjs.org/learn/seo/dynamic-imports](https://nextjs.org/learn/seo/dynamic-imports), [https://vite.dev/guide/features.html](https://vite.dev/guide/features.html), [https://esbuild.github.io/api/](https://esbuild.github.io/api/), [https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars](https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars), [https://webpack.js.org/guides/dependency-management/](https://webpack.js.org/guides/dependency-management/)\n\n### 2.6 Preload Based on User Intent\n\n**Impact: MEDIUM (reduces perceived latency)**\n\nPreload heavy bundles before they're needed to reduce perceived latency.\n\n**Example: preload on hover/focus**\n\n```tsx\nfunction EditorButton({ onClick }: { onClick: () => void }) {\n  const preload = () => {\n    if (typeof window !== 'undefined') {\n      void import('./monaco-editor')\n    }\n  }\n\n  return (\n    <button\n      onMouseEnter={preload}\n      onFocus={preload}\n      onClick={onClick}\n    >\n      Open Editor\n    </button>\n  )\n}\n```\n\n**Example: preload when feature flag is enabled**\n\n```tsx\nfunction FlagsProvider({ children, flags }: Props) {\n  useEffect(() => {\n    if (flags.editorEnabled && typeof window !== 'undefined') {\n      void import('./monaco-editor').then(mod => mod.init())\n    }\n  }, [flags.editorEnabled])\n\n  return <FlagsContext.Provider value={flags}>\n    {children}\n  </FlagsContext.Provider>\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.\n\n---\n\n## 3. Server-Side Performance\n\n**Impact: HIGH**\n\nOptimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.\n\n### 3.1 Authenticate Server Actions Like API Routes\n\n**Impact: CRITICAL (prevents unauthorized access to server mutations)**\n\nServer Actions (functions with `\"use server\"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly.\n\nNext.js documentation explicitly states: \"Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation.\"\n\n**Incorrect: no authentication check**\n\n```typescript\n'use server'\n\nexport async function deleteUser(userId: string) {\n  // Anyone can call this! No auth check\n  await db.user.delete({ where: { id: userId } })\n  return { success: true }\n}\n```\n\n**Correct: authentication inside the action**\n\n```typescript\n'use server'\n\nimport { verifySession } from '@/lib/auth'\nimport { unauthorized } from '@/lib/errors'\n\nexport async function deleteUser(userId: string) {\n  // Always check auth inside the action\n  const session = await verifySession()\n  \n  if (!session) {\n    throw unauthorized('Must be logged in')\n  }\n  \n  // Check authorization too\n  if (session.user.role !== 'admin' && session.user.id !== userId) {\n    throw unauthorized('Cannot delete other users')\n  }\n  \n  await db.user.delete({ where: { id: userId } })\n  return { success: true }\n}\n```\n\n**With input validation:**\n\n```typescript\n'use server'\n\nimport { verifySession } from '@/lib/auth'\nimport { z } from 'zod'\n\nconst updateProfileSchema = z.object({\n  userId: z.string().uuid(),\n  name: z.string().min(1).max(100),\n  email: z.string().email()\n})\n\nexport async function updateProfile(data: unknown) {\n  // Validate input first\n  const validated = updateProfileSchema.parse(data)\n  \n  // Then authenticate\n  const session = await verifySession()\n  if (!session) {\n    throw new Error('Unauthorized')\n  }\n  \n  // Then authorize\n  if (session.user.id !== validated.userId) {\n    throw new Error('Can only update own profile')\n  }\n  \n  // Finally perform the mutation\n  await db.user.update({\n    where: { id: validated.userId },\n    data: {\n      name: validated.name,\n      email: validated.email\n    }\n  })\n  \n  return { success: true }\n}\n```\n\nReference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)\n\n### 3.2 Avoid Duplicate Serialization in RSC Props\n\n**Impact: LOW (reduces network payload by avoiding duplicate serialization)**\n\nRSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again. Do transformations (`.toSorted()`, `.filter()`, `.map()`) in client, not server.\n\n**Incorrect: duplicates array**\n\n```tsx\n// RSC: sends 6 strings (2 arrays × 3 items)\n<ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />\n```\n\n**Correct: sends 3 strings**\n\n```tsx\n// RSC: send once\n<ClientList usernames={usernames} />\n\n// Client: transform there\n'use client'\nconst sorted = useMemo(() => [...usernames].sort(), [usernames])\n```\n\n**Nested deduplication behavior:**\n\n```tsx\n// string[] - duplicates everything\nusernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings\n\n// object[] - duplicates array structure only\nusers={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4)\n```\n\nDeduplication works recursively. Impact varies by data type:\n\n- `string[]`, `number[]`, `boolean[]`: **HIGH impact** - array + all primitives fully duplicated\n\n- `object[]`: **LOW impact** - array duplicated, but nested objects deduplicated by reference\n\n**Operations breaking deduplication: create new references**\n\n- Arrays: `.toSorted()`, `.filter()`, `.map()`, `.slice()`, `[...arr]`\n\n- Objects: `{...obj}`, `Object.assign()`, `structuredClone()`, `JSON.parse(JSON.stringify())`\n\n**More examples:**\n\n```tsx\n// ❌ Bad\n<C users={users} active={users.filter(u => u.active)} />\n<C product={product} productName={product.name} />\n\n// ✅ Good\n<C users={users} />\n<C product={product} />\n// Do filtering/destructuring in client\n```\n\n**Exception:** Pass derived data when transformation is expensive or client doesn't need original.\n\n### 3.3 Avoid Shared Module State for Request Data\n\n**Impact: HIGH (prevents concurrency bugs and request data leaks)**\n\nFor React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race conditions, cross-request contamination, and security bugs where one user's data appears in another user's response.\n\nTreat module scope on the server as process-wide shared memory, not request-local state.\n\n**Incorrect: request data leaks across concurrent renders**\n\n```tsx\nlet currentUser: User | null = null\n\nexport default async function Page() {\n  currentUser = await auth()\n  return <Dashboard />\n}\n\nasync function Dashboard() {\n  return <div>{currentUser?.name}</div>\n}\n```\n\nIf two requests overlap, request A can set `currentUser`, then request B overwrites it before request A finishes rendering `Dashboard`.\n\n**Correct: keep request data local to the render tree**\n\n```tsx\nexport default async function Page() {\n  const user = await auth()\n  return <Dashboard user={user} />\n}\n\nfunction Dashboard({ user }: { user: User | null }) {\n  return <div>{user?.name}</div>\n}\n```\n\nSafe exceptions:\n\n- Immutable static assets or config loaded once at module scope\n\n- Shared caches intentionally designed for cross-request reuse and keyed correctly\n\n- Process-wide singletons that do not store request- or user-specific mutable data\n\nFor static assets and config, see [Hoist Static I/O to Module Level](./server-hoist-static-io.md).\n\n### 3.4 Cross-Request LRU Caching\n\n**Impact: HIGH (caches across requests)**\n\n`React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.\n\n**Implementation:**\n\n```typescript\nimport { LRUCache } from 'lru-cache'\n\nconst cache = new LRUCache<string, any>({\n  max: 1000,\n  ttl: 5 * 60 * 1000  // 5 minutes\n})\n\nexport async function getUser(id: string) {\n  const cached = cache.get(id)\n  if (cached) return cached\n\n  const user = await db.user.findUnique({ where: { id } })\n  cache.set(id, user)\n  return user\n}\n\n// Request 1: DB query, result cached\n// Request 2: cache hit, no DB query\n```\n\nUse when sequential user actions hit multiple endpoints needing the same data within seconds.\n\n**With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis.\n\n**In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching.\n\nReference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)\n\n### 3.5 Hoist Static I/O to Module Level\n\n**Impact: HIGH (avoids repeated file/network I/O per request)**\n\nWhen loading static assets (fonts, logos, images, config files) in route handlers or server functions, hoist the I/O operation to module level. Module-level code runs once when the module is first imported, not on every request. This eliminates redundant file system reads or network fetches that would otherwise run on every invocation.\n\n**Incorrect: reads font file on every request**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\n\nexport async function GET(request: Request) {\n  // Runs on EVERY request - expensive!\n  const fontData = await fetch(\n    new URL('./fonts/Inter.ttf', import.meta.url)\n  ).then(res => res.arrayBuffer())\n\n  const logoData = await fetch(\n    new URL('./images/logo.png', import.meta.url)\n  ).then(res => res.arrayBuffer())\n\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logoData} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: fontData }] }\n  )\n}\n```\n\n**Correct: loads once at module initialization**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\n\n// Module-level: runs ONCE when module is first imported\nconst fontData = fetch(\n  new URL('./fonts/Inter.ttf', import.meta.url)\n).then(res => res.arrayBuffer())\n\nconst logoData = fetch(\n  new URL('./images/logo.png', import.meta.url)\n).then(res => res.arrayBuffer())\n\nexport async function GET(request: Request) {\n  // Await the already-started promises\n  const [font, logo] = await Promise.all([fontData, logoData])\n\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logo} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: font }] }\n  )\n}\n```\n\n**Correct: synchronous fs at module level**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\nimport { readFileSync } from 'fs'\nimport { join } from 'path'\n\n// Synchronous read at module level - blocks only during module init\nconst fontData = readFileSync(\n  join(process.cwd(), 'public/fonts/Inter.ttf')\n)\n\nconst logoData = readFileSync(\n  join(process.cwd(), 'public/images/logo.png')\n)\n\nexport async function GET(request: Request) {\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logoData} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: fontData }] }\n  )\n}\n```\n\n**Incorrect: reads config on every call**\n\n```typescript\nimport fs from 'node:fs/promises'\n\nexport async function processRequest(data: Data) {\n  const config = JSON.parse(\n    await fs.readFile('./config.json', 'utf-8')\n  )\n  const template = await fs.readFile('./template.html', 'utf-8')\n\n  return render(template, data, config)\n}\n```\n\n**Correct: hoists config and template to module level**\n\n```typescript\nimport fs from 'node:fs/promises'\n\nconst configPromise = fs\n  .readFile('./config.json', 'utf-8')\n  .then(JSON.parse)\nconst templatePromise = fs.readFile('./template.html', 'utf-8')\n\nexport async function processRequest(data: Data) {\n  const [config, template] = await Promise.all([\n    configPromise,\n    templatePromise,\n  ])\n\n  return render(template, data, config)\n}\n```\n\nWhen to use this pattern:\n\n- Loading fonts for OG image generation\n\n- Loading static logos, icons, or watermarks\n\n- Reading configuration files that don't change at runtime\n\n- Loading email templates or other static templates\n\n- Any static asset that's the same across all requests\n\nWhen not to use this pattern:\n\n- Assets that vary per request or user\n\n- Files that may change during runtime (use caching with TTL instead)\n\n- Large files that would consume too much memory if kept loaded\n\n- Sensitive data that shouldn't persist in memory\n\nWith Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute), module-level caching is especially effective because multiple concurrent requests share the same function instance. The static assets stay loaded in memory across requests without cold start penalties.\n\nIn traditional serverless, each cold start re-executes module-level code, but subsequent warm invocations reuse the loaded assets until the instance is recycled.\n\n### 3.6 Minimize Serialization at RSC Boundaries\n\n**Impact: HIGH (reduces data transfer size)**\n\nThe React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.\n\n**Incorrect: serializes all 50 fields**\n\n```tsx\nasync function Page() {\n  const user = await fetchUser()  // 50 fields\n  return <Profile user={user} />\n}\n\n'use client'\nfunction Profile({ user }: { user: User }) {\n  return <div>{user.name}</div>  // uses 1 field\n}\n```\n\n**Correct: serializes only 1 field**\n\n```tsx\nasync function Page() {\n  const user = await fetchUser()\n  return <Profile name={user.name} />\n}\n\n'use client'\nfunction Profile({ name }: { name: string }) {\n  return <div>{name}</div>\n}\n```\n\n### 3.7 Parallel Data Fetching with Component Composition\n\n**Impact: CRITICAL (eliminates server-side waterfalls)**\n\nReact Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.\n\n**Incorrect: Sidebar waits for Page's fetch to complete**\n\n```tsx\nexport default async function Page() {\n  const header = await fetchHeader()\n  return (\n    <div>\n      <div>{header}</div>\n      <Sidebar />\n    </div>\n  )\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n```\n\n**Correct: both fetch simultaneously**\n\n```tsx\nasync function Header() {\n  const data = await fetchHeader()\n  return <div>{data}</div>\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n\nexport default function Page() {\n  return (\n    <div>\n      <Header />\n      <Sidebar />\n    </div>\n  )\n}\n```\n\n**Alternative with children prop:**\n\n```tsx\nasync function Header() {\n  const data = await fetchHeader()\n  return <div>{data}</div>\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n\nfunction Layout({ children }: { children: ReactNode }) {\n  return (\n    <div>\n      <Header />\n      {children}\n    </div>\n  )\n}\n\nexport default function Page() {\n  return (\n    <Layout>\n      <Sidebar />\n    </Layout>\n  )\n}\n```\n\n### 3.8 Parallel Nested Data Fetching\n\n**Impact: CRITICAL (eliminates server-side waterfalls)**\n\nWhen fetching nested data in parallel, chain dependent fetches within each item's promise so a slow item doesn't block the rest.\n\n**Incorrect: a single slow item blocks all nested fetches**\n\n```tsx\nconst chats = await Promise.all(\n  chatIds.map(id => getChat(id))\n)\n\nconst chatAuthors = await Promise.all(\n  chats.map(chat => getUser(chat.author))\n)\n```\n\nIf one `getChat(id)` out of 100 is extremely slow, the authors of the other 99 chats can't start loading even though their data is ready.\n\n**Correct: each item chains its own nested fetch**\n\n```tsx\nconst chatAuthors = await Promise.all(\n  chatIds.map(id => getChat(id).then(chat => getUser(chat.author)))\n)\n```\n\nEach item independently chains `getChat` → `getUser`, so a slow chat doesn't block author fetches for the others.\n\n### 3.9 Per-Request Deduplication with React.cache()\n\n**Impact: MEDIUM (deduplicates within request)**\n\nUse `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.\n\n**Usage:**\n\n```typescript\nimport { cache } from 'react'\n\nexport const getCurrentUser = cache(async () => {\n  const session = await auth()\n  if (!session?.user?.id) return null\n  return await db.user.findUnique({\n    where: { id: session.user.id }\n  })\n})\n```\n\nWithin a single request, multiple calls to `getCurrentUser()` execute the query only once.\n\n**Avoid inline objects as arguments:**\n\n`React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits.\n\n**Incorrect: always cache miss**\n\n```typescript\nconst getUser = cache(async (params: { uid: number }) => {\n  return await db.user.findUnique({ where: { id: params.uid } })\n})\n\n// Each call creates new object, never hits cache\ngetUser({ uid: 1 })\ngetUser({ uid: 1 })  // Cache miss, runs query again\n```\n\n**Correct: cache hit**\n\n```typescript\nconst params = { uid: 1 }\ngetUser(params)  // Query runs\ngetUser(params)  // Cache hit (same reference)\n```\n\nIf you must pass objects, pass the same reference:\n\n**Next.js-Specific Note:**\n\nIn Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks:\n\n- Database queries (Prisma, Drizzle, etc.)\n\n- Heavy computations\n\n- Authentication checks\n\n- File system operations\n\n- Any non-fetch async work\n\nUse `React.cache()` to deduplicate these operations across your component tree.\n\nReference: [https://react.dev/reference/react/cache](https://react.dev/reference/react/cache)\n\n### 3.10 Use after() for Non-Blocking Operations\n\n**Impact: MEDIUM (faster response times)**\n\nUse Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.\n\n**Incorrect: blocks response**\n\n```tsx\nimport { logUserAction } from '@/app/utils'\n\nexport async function POST(request: Request) {\n  // Perform mutation\n  await updateDatabase(request)\n  \n  // Logging blocks the response\n  const userAgent = request.headers.get('user-agent') || 'unknown'\n  await logUserAction({ userAgent })\n  \n  return new Response(JSON.stringify({ status: 'success' }), {\n    status: 200,\n    headers: { 'Content-Type': 'application/json' }\n  })\n}\n```\n\n**Correct: non-blocking**\n\n```tsx\nimport { after } from 'next/server'\nimport { headers, cookies } from 'next/headers'\nimport { logUserAction } from '@/app/utils'\n\nexport async function POST(request: Request) {\n  // Perform mutation\n  await updateDatabase(request)\n  \n  // Log after response is sent\n  after(async () => {\n    const userAgent = (await headers()).get('user-agent') || 'unknown'\n    const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous'\n    \n    logUserAction({ sessionCookie, userAgent })\n  })\n  \n  return new Response(JSON.stringify({ status: 'success' }), {\n    status: 200,\n    headers: { 'Content-Type': 'application/json' }\n  })\n}\n```\n\nThe response is sent immediately while logging happens in the background.\n\n**Common use cases:**\n\n- Analytics tracking\n\n- Audit logging\n\n- Sending notifications\n\n- Cache invalidation\n\n- Cleanup tasks\n\n**Important notes:**\n\n- `after()` runs even if the response fails or redirects\n\n- Works in Server Actions, Route Handlers, and Server Components\n\nReference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)\n\n---\n\n## 4. Client-Side Data Fetching\n\n**Impact: MEDIUM-HIGH**\n\nAutomatic deduplication and efficient data fetching patterns reduce redundant network requests.\n\n### 4.1 Deduplicate Global Event Listeners\n\n**Impact: LOW (single listener for N components)**\n\nUse `useSWRSubscription()` to share global event listeners across component instances.\n\n**Incorrect: N instances = N listeners**\n\n```tsx\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  useEffect(() => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && e.key === key) {\n        callback()\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  }, [key, callback])\n}\n```\n\nWhen using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.\n\n**Correct: N instances = 1 listener**\n\n```tsx\nimport useSWRSubscription from 'swr/subscription'\n\n// Module-level Map to track callbacks per key\nconst keyCallbacks = new Map<string, Set<() => void>>()\n\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  // Register this callback in the Map\n  useEffect(() => {\n    if (!keyCallbacks.has(key)) {\n      keyCallbacks.set(key, new Set())\n    }\n    keyCallbacks.get(key)!.add(callback)\n\n    return () => {\n      const set = keyCallbacks.get(key)\n      if (set) {\n        set.delete(callback)\n        if (set.size === 0) {\n          keyCallbacks.delete(key)\n        }\n      }\n    }\n  }, [key, callback])\n\n  useSWRSubscription('global-keydown', () => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && keyCallbacks.has(e.key)) {\n        keyCallbacks.get(e.key)!.forEach(cb => cb())\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  })\n}\n\nfunction Profile() {\n  // Multiple shortcuts will share the same listener\n  useKeyboardShortcut('p', () => { /* ... */ }) \n  useKeyboardShortcut('k', () => { /* ... */ })\n  // ...\n}\n```\n\n### 4.2 Use Passive Event Listeners for Scrolling Performance\n\n**Impact: MEDIUM (eliminates scroll delay caused by event listeners)**\n\nAdd `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n\n**Incorrect:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch)\n  document.addEventListener('wheel', handleWheel)\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Correct:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch, { passive: true })\n  document.addEventListener('wheel', handleWheel, { passive: true })\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.\n\n**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.\n\n### 4.3 Use SWR for Automatic Deduplication\n\n**Impact: MEDIUM-HIGH (automatic deduplication)**\n\nSWR enables request deduplication, caching, and revalidation across component instances.\n\n**Incorrect: no deduplication, each instance fetches**\n\n```tsx\nfunction UserList() {\n  const [users, setUsers] = useState([])\n  useEffect(() => {\n    fetch('/api/users')\n      .then(r => r.json())\n      .then(setUsers)\n  }, [])\n}\n```\n\n**Correct: multiple instances share one request**\n\n```tsx\nimport useSWR from 'swr'\n\nfunction UserList() {\n  const { data: users } = useSWR('/api/users', fetcher)\n}\n```\n\n**For immutable data:**\n\n```tsx\nimport { useImmutableSWR } from '@/lib/swr'\n\nfunction StaticContent() {\n  const { data } = useImmutableSWR('/api/config', fetcher)\n}\n```\n\n**For mutations:**\n\n```tsx\nimport { useSWRMutation } from 'swr/mutation'\n\nfunction UpdateButton() {\n  const { trigger } = useSWRMutation('/api/user', updateUser)\n  return <button onClick={() => trigger()}>Update</button>\n}\n```\n\nReference: [https://swr.vercel.app](https://swr.vercel.app)\n\n### 4.4 Version and Minimize localStorage Data\n\n**Impact: MEDIUM (prevents schema conflicts, reduces storage size)**\n\nAdd version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n\n**Incorrect:**\n\n```typescript\n// No version, stores everything, no error handling\nlocalStorage.setItem('userConfig', JSON.stringify(fullUserObject))\nconst data = localStorage.getItem('userConfig')\n```\n\n**Correct:**\n\n```typescript\nconst VERSION = 'v2'\n\nfunction saveConfig(config: { theme: string; language: string }) {\n  try {\n    localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))\n  } catch {\n    // Throws in incognito/private browsing, quota exceeded, or disabled\n  }\n}\n\nfunction loadConfig() {\n  try {\n    const data = localStorage.getItem(`userConfig:${VERSION}`)\n    return data ? JSON.parse(data) : null\n  } catch {\n    return null\n  }\n}\n\n// Migration from v1 to v2\nfunction migrate() {\n  try {\n    const v1 = localStorage.getItem('userConfig:v1')\n    if (v1) {\n      const old = JSON.parse(v1)\n      saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })\n      localStorage.removeItem('userConfig:v1')\n    }\n  } catch {}\n}\n```\n\n**Store minimal fields from server responses:**\n\n```typescript\n// User object has 20+ fields, only store what UI needs\nfunction cachePrefs(user: FullUser) {\n  try {\n    localStorage.setItem('prefs:v1', JSON.stringify({\n      theme: user.preferences.theme,\n      notifications: user.preferences.notifications\n    }))\n  } catch {}\n}\n```\n\n**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.\n\n**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.\n\n---\n\n## 5. Re-render Optimization\n\n**Impact: MEDIUM**\n\nReducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.\n\n### 5.1 Calculate Derived State During Rendering\n\n**Impact: MEDIUM (avoids redundant renders and state drift)**\n\nIf a value can be computed from current props/state, do\n\n<!-- [TRUNCATED AT 50KB] -->","isInternal":false,"tokens":12013,"sizeBytes":50062},{"name":"metadata.json","path":"skills/react-best-practices/metadata.json","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/metadata.json","title":"React-best-practices Skill","category":"anthropic-skill","format":"json","content":"{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Vercel Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://nextjs.org\",\n    \"https://swr.vercel.app\",\n    \"https://github.com/shuding/better-all\",\n    \"https://github.com/isaacs/node-lru-cache\",\n    \"https://vercel.com/blog/how-we-optimized-package-imports-in-next-js\",\n    \"https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast\"\n  ]\n}\n","frontmatter":{"version":"1.0.0","organization":"Vercel Engineering","date":"January 2026","abstract":"Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.","references":["https://react.dev","https://nextjs.org","https://swr.vercel.app","https://github.com/shuding/better-all","https://github.com/isaacs/node-lru-cache","https://vercel.com/blog/how-we-optimized-package-imports-in-next-js","https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast"]},"isInternal":false,"tokens":217,"sizeBytes":921},{"name":"README.md","path":"skills/react-best-practices/README.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/README.md","title":"React-best-practices Skill","category":"anthropic-skill","format":"markdown","content":"# React Best Practices\n\nA structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `src/` - Build scripts and utilities\n- `metadata.json` - Document metadata (version, organization, abstract)\n- __`AGENTS.md`__ - Compiled output (generated)\n- __`test-cases.json`__ - Test cases for LLM evaluation (generated)\n\n## Getting Started\n\n1. Install dependencies:\n   ```bash\n   pnpm install\n   ```\n\n2. Build AGENTS.md from rules:\n   ```bash\n   pnpm build\n   ```\n\n3. Validate rule files:\n   ```bash\n   pnpm validate\n   ```\n\n4. Extract test cases:\n   ```bash\n   pnpm extract-tests\n   ```\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `async-` for Eliminating Waterfalls (Section 1)\n   - `bundle-` for Bundle Size Optimization (Section 2)\n   - `server-` for Server-Side Performance (Section 3)\n   - `client-` for Client-Side Data Fetching (Section 4)\n   - `rerender-` for Re-render Optimization (Section 5)\n   - `rendering-` for Rendering Performance (Section 6)\n   - `js-` for JavaScript Performance (Section 7)\n   - `advanced-` for Advanced Patterns (Section 8)\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json\n\n## Rule File Structure\n\nEach rule file should follow this structure:\n\n```markdown\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description\ntags: tag1, tag2, tag3\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example\n```\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example\n```\n\nOptional explanatory text after examples.\n\nReference: [Link](https://example.com)\n\n## File Naming Convention\n\n- Files starting with `_` are special (excluded from build)\n- Rule files: `area-description.md` (e.g., `async-parallel.md`)\n- Section is automatically inferred from filename prefix\n- Rules are sorted alphabetically by title within each section\n- IDs (e.g., 1.1, 1.2) are auto-generated during build\n\n## Impact Levels\n\n- `CRITICAL` - Highest priority, major performance gains\n- `HIGH` - Significant performance improvements\n- `MEDIUM-HIGH` - Moderate-high gains\n- `MEDIUM` - Moderate performance improvements\n- `LOW-MEDIUM` - Low-medium gains\n- `LOW` - Incremental improvements\n\n## Scripts\n\n- `pnpm build` - Compile rules into AGENTS.md\n- `pnpm validate` - Validate all rule files\n- `pnpm extract-tests` - Extract test cases for LLM evaluation\n- `pnpm dev` - Build and validate\n\n## Contributing\n\nWhen adding or modifying rules:\n\n1. Use the correct filename prefix for your section\n2. Follow the `_template.md` structure\n3. Include clear bad/good examples with explanations\n4. Add appropriate tags\n5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json\n6. Rules are automatically sorted by title - no need to manage numbers!\n\n## Acknowledgments\n\nOriginally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).\n","isInternal":false,"tokens":848,"sizeBytes":3360},{"name":"_sections.md","path":"skills/react-best-practices/rules/_sections.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/_sections.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Eliminating Waterfalls (async)\n\n**Impact:** CRITICAL  \n**Description:** Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.\n\n## 2. Bundle Size Optimization (bundle)\n\n**Impact:** CRITICAL  \n**Description:** Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.\n\n## 3. Server-Side Performance (server)\n\n**Impact:** HIGH  \n**Description:** Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.\n\n## 4. Client-Side Data Fetching (client)\n\n**Impact:** MEDIUM-HIGH  \n**Description:** Automatic deduplication and efficient data fetching patterns reduce redundant network requests.\n\n## 5. Re-render Optimization (rerender)\n\n**Impact:** MEDIUM  \n**Description:** Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.\n\n## 6. Rendering Performance (rendering)\n\n**Impact:** MEDIUM  \n**Description:** Optimizing the rendering process reduces the work the browser needs to do.\n\n## 7. JavaScript Performance (js)\n\n**Impact:** LOW-MEDIUM  \n**Description:** Micro-optimizations for hot paths can add up to meaningful improvements.\n\n## 8. Advanced Patterns (advanced)\n\n**Impact:** LOW  \n**Description:** Advanced patterns for specific cases that require careful implementation.\n","isInternal":false,"tokens":318,"sizeBytes":1554},{"name":"_template.md","path":"skills/react-best-practices/rules/_template.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/_template.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description of impact (e.g., \"20-50% improvement\")\ntags: tag1, tag2\n---\n\n## Rule Title Here\n\n**Impact: MEDIUM (optional impact description)**\n\nBrief explanation of the rule and why it matters. This should be clear and concise, explaining the performance implications.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example here\nconst bad = example()\n```\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example here\nconst good = example()\n```\n\nReference: [Link to documentation or resource](https://example.com)\n","frontmatter":{"title":"Rule Title Here","impact":"MEDIUM","impactDescription":"Optional description of impact (e.g., \"20-50% improvement\")","tags":"tag1, tag2"},"isInternal":false,"tokens":143,"sizeBytes":631},{"name":"advanced-effect-event-deps.md","path":"skills/react-best-practices/rules/advanced-effect-event-deps.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/advanced-effect-event-deps.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Do Not Put Effect Events in Dependency Arrays\nimpact: LOW\nimpactDescription: avoids unnecessary effect re-runs and lint errors\ntags: advanced, hooks, useEffectEvent, dependencies, effects\n---\n\n## Do Not Put Effect Events in Dependency Arrays\n\nEffect Event functions do not have a stable identity. Their identity intentionally changes on every render. Do not include the function returned by `useEffectEvent` in a `useEffect` dependency array. Keep the actual reactive values as dependencies and call the Effect Event from inside the effect body or subscriptions created by that effect.\n\n**Incorrect (Effect Event added as a dependency):**\n\n```tsx\nimport { useEffect, useEffectEvent } from 'react'\n\nfunction ChatRoom({ roomId, onConnected }: {\n  roomId: string\n  onConnected: () => void\n}) {\n  const handleConnected = useEffectEvent(onConnected)\n\n  useEffect(() => {\n    const connection = createConnection(roomId)\n    connection.on('connected', handleConnected)\n    connection.connect()\n\n    return () => connection.disconnect()\n  }, [roomId, handleConnected])\n}\n```\n\nIncluding the Effect Event in dependencies makes the effect re-run every render and triggers the React Hooks lint rule.\n\n**Correct (depend on reactive values, not the Effect Event):**\n\n```tsx\nimport { useEffect, useEffectEvent } from 'react'\n\nfunction ChatRoom({ roomId, onConnected }: {\n  roomId: string\n  onConnected: () => void\n}) {\n  const handleConnected = useEffectEvent(onConnected)\n\n  useEffect(() => {\n    const connection = createConnection(roomId)\n    connection.on('connected', handleConnected)\n    connection.connect()\n\n    return () => connection.disconnect()\n  }, [roomId])\n}\n```\n\nReference: [React useEffectEvent: Effect Event in deps](https://react.dev/reference/react/useEffectEvent#effect-event-in-deps)\n","frontmatter":{"title":"Do Not Put Effect Events in Dependency Arrays","impact":"LOW","impactDescription":"avoids unnecessary effect re-runs and lint errors","tags":"advanced, hooks, useEffectEvent, dependencies, effects"},"isInternal":false,"tokens":375,"sizeBytes":1802},{"name":"advanced-event-handler-refs.md","path":"skills/react-best-practices/rules/advanced-event-handler-refs.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/advanced-event-handler-refs.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Store Event Handlers in Refs\nimpact: LOW\nimpactDescription: stable subscriptions\ntags: advanced, hooks, refs, event-handlers, optimization\n---\n\n## Store Event Handlers in Refs\n\nStore callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.\n\n**Incorrect (re-subscribes on every render):**\n\n```tsx\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  useEffect(() => {\n    window.addEventListener(event, handler)\n    return () => window.removeEventListener(event, handler)\n  }, [event, handler])\n}\n```\n\n**Correct (stable subscription):**\n\n```tsx\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  const handlerRef = useRef(handler)\n  useEffect(() => {\n    handlerRef.current = handler\n  }, [handler])\n\n  useEffect(() => {\n    const listener = (e) => handlerRef.current(e)\n    window.addEventListener(event, listener)\n    return () => window.removeEventListener(event, listener)\n  }, [event])\n}\n```\n\n**Alternative: use `useEffectEvent` if you're on latest React:**\n\n```tsx\nimport { useEffectEvent } from 'react'\n\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  const onEvent = useEffectEvent(handler)\n\n  useEffect(() => {\n    window.addEventListener(event, onEvent)\n    return () => window.removeEventListener(event, onEvent)\n  }, [event])\n}\n```\n\n`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.\n","frontmatter":{"title":"Store Event Handlers in Refs","impact":"LOW","impactDescription":"stable subscriptions","tags":"advanced, hooks, refs, event-handlers, optimization"},"isInternal":false,"tokens":337,"sizeBytes":1483},{"name":"advanced-init-once.md","path":"skills/react-best-practices/rules/advanced-init-once.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/advanced-init-once.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Initialize App Once, Not Per Mount\nimpact: LOW-MEDIUM\nimpactDescription: avoids duplicate init in development\ntags: initialization, useEffect, app-startup, side-effects\n---\n\n## Initialize App Once, Not Per Mount\n\nDo not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.\n\n**Incorrect (runs twice in dev, re-runs on remount):**\n\n```tsx\nfunction Comp() {\n  useEffect(() => {\n    loadFromStorage()\n    checkAuthToken()\n  }, [])\n\n  // ...\n}\n```\n\n**Correct (once per app load):**\n\n```tsx\nlet didInit = false\n\nfunction Comp() {\n  useEffect(() => {\n    if (didInit) return\n    didInit = true\n    loadFromStorage()\n    checkAuthToken()\n  }, [])\n\n  // ...\n}\n```\n\nReference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)\n","frontmatter":{"title":"Initialize App Once, Not Per Mount","impact":"LOW-MEDIUM","impactDescription":"avoids duplicate init in development","tags":"initialization, useEffect, app-startup, side-effects"},"isInternal":false,"tokens":230,"sizeBytes":958},{"name":"advanced-use-latest.md","path":"skills/react-best-practices/rules/advanced-use-latest.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/advanced-use-latest.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: useEffectEvent for Stable Callback Refs\nimpact: LOW\nimpactDescription: prevents effect re-runs\ntags: advanced, hooks, useEffectEvent, refs, optimization\n---\n\n## useEffectEvent for Stable Callback Refs\n\nAccess latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.\n\n**Incorrect (effect re-runs on every callback change):**\n\n```tsx\nfunction SearchInput({ onSearch }: { onSearch: (q: string) => void }) {\n  const [query, setQuery] = useState('')\n\n  useEffect(() => {\n    const timeout = setTimeout(() => onSearch(query), 300)\n    return () => clearTimeout(timeout)\n  }, [query, onSearch])\n}\n```\n\n**Correct (using React's useEffectEvent):**\n\n```tsx\nimport { useEffectEvent } from 'react';\n\nfunction SearchInput({ onSearch }: { onSearch: (q: string) => void }) {\n  const [query, setQuery] = useState('')\n  const onSearchEvent = useEffectEvent(onSearch)\n\n  useEffect(() => {\n    const timeout = setTimeout(() => onSearchEvent(query), 300)\n    return () => clearTimeout(timeout)\n  }, [query])\n}\n```\n","frontmatter":{"title":"useEffectEvent for Stable Callback Refs","impact":"LOW","impactDescription":"prevents effect re-runs","tags":"advanced, hooks, useEffectEvent, refs, optimization"},"isInternal":false,"tokens":254,"sizeBytes":1072},{"name":"async-api-routes.md","path":"skills/react-best-practices/rules/async-api-routes.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/async-api-routes.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Prevent Waterfall Chains in API Routes\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: api-routes, server-actions, waterfalls, parallelization\n---\n\n## Prevent Waterfall Chains in API Routes\n\nIn API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n\n**Incorrect (config waits for auth, data waits for both):**\n\n```typescript\nexport async function GET(request: Request) {\n  const session = await auth()\n  const config = await fetchConfig()\n  const data = await fetchData(session.user.id)\n  return Response.json({ data, config })\n}\n```\n\n**Correct (auth and config start immediately):**\n\n```typescript\nexport async function GET(request: Request) {\n  const sessionPromise = auth()\n  const configPromise = fetchConfig()\n  const session = await sessionPromise\n  const [config, data] = await Promise.all([\n    configPromise,\n    fetchData(session.user.id)\n  ])\n  return Response.json({ data, config })\n}\n```\n\nFor operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).\n","frontmatter":{"title":"Prevent Waterfall Chains in API Routes","impact":"CRITICAL","impactDescription":"2-10× improvement","tags":"api-routes, server-actions, waterfalls, parallelization"},"isInternal":false,"tokens":241,"sizeBytes":1125},{"name":"async-cheap-condition-before-await.md","path":"skills/react-best-practices/rules/async-cheap-condition-before-await.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/async-cheap-condition-before-await.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Check Cheap Conditions Before Async Flags\nimpact: HIGH\nimpactDescription: avoids unnecessary async work when a synchronous guard already fails\ntags: async, await, feature-flags, short-circuit, conditional\n---\n\n## Check Cheap Conditions Before Async Flags\n\nWhen a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n\nThis is a specialization of [Defer Await Until Needed](./async-defer-await.md) for `flag && cheapCondition` style checks.\n\n**Incorrect:**\n\n```typescript\nconst someFlag = await getFlag()\n\nif (someFlag && someCondition) {\n  // ...\n}\n```\n\n**Correct:**\n\n```typescript\nif (someCondition) {\n  const someFlag = await getFlag()\n  if (someFlag) {\n    // ...\n  }\n}\n```\n\nThis matters when `getFlag` hits the network, a feature-flag service, or `React.cache` / DB work: skipping it when `someCondition` is false removes that cost on the cold path.\n\nKeep the original order if `someCondition` is expensive, depends on the flag, or you must run side effects in a fixed order.\n","frontmatter":{"title":"Check Cheap Conditions Before Async Flags","impact":"HIGH","impactDescription":"avoids unnecessary async work when a synchronous guard already fails","tags":"async, await, feature-flags, short-circuit, conditional"},"isInternal":false,"tokens":277,"sizeBytes":1220},{"name":"async-defer-await.md","path":"skills/react-best-practices/rules/async-defer-await.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/async-defer-await.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Defer Await Until Needed\nimpact: HIGH\nimpactDescription: avoids blocking unused code paths\ntags: async, await, conditional, optimization\n---\n\n## Defer Await Until Needed\n\nMove `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n\n**Incorrect (blocks both branches):**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  const userData = await fetchUserData(userId)\n  \n  if (skipProcessing) {\n    // Returns immediately but still waited for userData\n    return { skipped: true }\n  }\n  \n  // Only this branch uses userData\n  return processUserData(userData)\n}\n```\n\n**Correct (only blocks when needed):**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  if (skipProcessing) {\n    // Returns immediately without waiting\n    return { skipped: true }\n  }\n  \n  // Fetch only when needed\n  const userData = await fetchUserData(userId)\n  return processUserData(userData)\n}\n```\n\n**Another example (early return optimization):**\n\n```typescript\n// Incorrect: always fetches permissions\nasync function updateResource(resourceId: string, userId: string) {\n  const permissions = await fetchPermissions(userId)\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n\n// Correct: fetches only when needed\nasync function updateResource(resourceId: string, userId: string) {\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  const permissions = await fetchPermissions(userId)\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n```\n\nThis optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.\n\nFor `await getFlag()` combined with a cheap synchronous guard (`flag && someCondition`), see [Check Cheap Conditions Before Async Flags](./async-cheap-condition-before-await.md).\n","frontmatter":{"title":"Defer Await Until Needed","impact":"HIGH","impactDescription":"avoids blocking unused code paths","tags":"async, await, conditional, optimization"},"isInternal":false,"tokens":474,"sizeBytes":2208},{"name":"async-dependencies.md","path":"skills/react-best-practices/rules/async-dependencies.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/async-dependencies.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Dependency-Based Parallelization\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: async, parallelization, dependencies, better-all\n---\n\n## Dependency-Based Parallelization\n\nFor operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n\n**Incorrect (profile waits for config unnecessarily):**\n\n```typescript\nconst [user, config] = await Promise.all([\n  fetchUser(),\n  fetchConfig()\n])\nconst profile = await fetchProfile(user.id)\n```\n\n**Correct (config and profile run in parallel):**\n\n```typescript\nimport { all } from 'better-all'\n\nconst { user, config, profile } = await all({\n  async user() { return fetchUser() },\n  async config() { return fetchConfig() },\n  async profile() {\n    return fetchProfile((await this.$.user).id)\n  }\n})\n```\n\n**Alternative without extra dependencies:**\n\nWe can also create all the promises first, and do `Promise.all()` at the end.\n\n```typescript\nconst userPromise = fetchUser()\nconst profilePromise = userPromise.then(user => fetchProfile(user.id))\n\nconst [user, config, profile] = await Promise.all([\n  userPromise,\n  fetchConfig(),\n  profilePromise\n])\n```\n\nReference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)\n","frontmatter":{"title":"Dependency-Based Parallelization","impact":"CRITICAL","impactDescription":"2-10× improvement","tags":"async, parallelization, dependencies, better-all"},"isInternal":false,"tokens":291,"sizeBytes":1293},{"name":"async-parallel.md","path":"skills/react-best-practices/rules/async-parallel.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/async-parallel.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Promise.all() for Independent Operations\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: async, parallelization, promises, waterfalls\n---\n\n## Promise.all() for Independent Operations\n\nWhen async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n\n**Incorrect (sequential execution, 3 round trips):**\n\n```typescript\nconst user = await fetchUser()\nconst posts = await fetchPosts()\nconst comments = await fetchComments()\n```\n\n**Correct (parallel execution, 1 round trip):**\n\n```typescript\nconst [user, posts, comments] = await Promise.all([\n  fetchUser(),\n  fetchPosts(),\n  fetchComments()\n])\n```\n","frontmatter":{"title":"Promise.all() for Independent Operations","impact":"CRITICAL","impactDescription":"2-10× improvement","tags":"async, parallelization, promises, waterfalls"},"isInternal":false,"tokens":144,"sizeBytes":654},{"name":"async-suspense-boundaries.md","path":"skills/react-best-practices/rules/async-suspense-boundaries.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/async-suspense-boundaries.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Strategic Suspense Boundaries\nimpact: HIGH\nimpactDescription: faster initial paint\ntags: async, suspense, streaming, layout-shift\n---\n\n## Strategic Suspense Boundaries\n\nInstead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n\n**Incorrect (wrapper blocked by data fetching):**\n\n```tsx\nasync function Page() {\n  const data = await fetchData() // Blocks entire page\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <DataDisplay data={data} />\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n```\n\nThe entire layout waits for data even though only the middle section needs it.\n\n**Correct (wrapper shows immediately, data streams in):**\n\n```tsx\nfunction Page() {\n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <Suspense fallback={<Skeleton />}>\n          <DataDisplay />\n        </Suspense>\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nasync function DataDisplay() {\n  const data = await fetchData() // Only blocks this component\n  return <div>{data.content}</div>\n}\n```\n\nSidebar, Header, and Footer render immediately. Only DataDisplay waits for data.\n\n**Alternative (share promise across components):**\n\n```tsx\nfunction Page() {\n  // Start fetch immediately, but don't await\n  const dataPromise = fetchData()\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <Suspense fallback={<Skeleton />}>\n        <DataDisplay dataPromise={dataPromise} />\n        <DataSummary dataPromise={dataPromise} />\n      </Suspense>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nfunction DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Unwraps the promise\n  return <div>{data.content}</div>\n}\n\nfunction DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Reuses the same promise\n  return <div>{data.summary}</div>\n}\n```\n\nBoth components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.\n\n**When NOT to use this pattern:**\n\n- Critical data needed for layout decisions (affects positioning)\n- SEO-critical content above the fold\n- Small, fast queries where suspense overhead isn't worth it\n- When you want to avoid layout shift (loading → content jump)\n\n**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.\n","frontmatter":{"title":"Strategic Suspense Boundaries","impact":"HIGH","impactDescription":"faster initial paint","tags":"async, suspense, streaming, layout-shift"},"isInternal":false,"tokens":591,"sizeBytes":2510},{"name":"bundle-analyzable-paths.md","path":"skills/react-best-practices/rules/bundle-analyzable-paths.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/bundle-analyzable-paths.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Prefer Statically Analyzable Paths\nimpact: HIGH\nimpactDescription: avoids accidental broad bundles and file traces\ntags: bundle, nextjs, vite, webpack, rollup, esbuild, path\n---\n\n## Prefer Statically Analyzable Paths\n\nBuild tools work best when import and file-system paths are obvious at build time. If you hide the real path inside a variable or compose it too dynamically, the tool either has to include a broad set of possible files, warn that it cannot analyze the import, or widen file tracing to stay safe.\n\nPrefer explicit maps or literal paths so the set of reachable files stays narrow and predictable. This is the same rule whether you are choosing modules with `import()` or reading files in server/build code.\n\nWhen analysis becomes too broad, the cost is real:\n- Larger server bundles\n- Slower builds\n- Worse cold starts\n- More memory use\n\n### Import Paths\n\n**Incorrect (the bundler cannot tell what may be imported):**\n\n```ts\nconst PAGE_MODULES = {\n  home: './pages/home',\n  settings: './pages/settings',\n} as const\n\nconst Page = await import(PAGE_MODULES[pageName])\n```\n\n**Correct (use an explicit map of allowed modules):**\n\n```ts\nconst PAGE_MODULES = {\n  home: () => import('./pages/home'),\n  settings: () => import('./pages/settings'),\n} as const\n\nconst Page = await PAGE_MODULES[pageName]()\n```\n\n### File-System Paths\n\n**Incorrect (a 2-value enum still hides the final path from static analysis):**\n\n```ts\nconst baseDir = path.join(process.cwd(), 'content/' + contentKind)\n```\n\n**Correct (make each final path literal at the callsite):**\n\n```ts\nconst baseDir =\n  kind === ContentKind.Blog\n    ? path.join(process.cwd(), 'content/blog')\n    : path.join(process.cwd(), 'content/docs')\n```\n\nIn Next.js server code, this matters for output file tracing too. `path.join(process.cwd(), someVar)` can widen the traced file set because Next.js statically analyze `import`, `require`, and `fs` usage.\n\nReference: [Next.js output](https://nextjs.org/docs/app/api-reference/config/next-config-js/output), [Next.js dynamic imports](https://nextjs.org/learn/seo/dynamic-imports), [Vite features](https://vite.dev/guide/features.html), [esbuild API](https://esbuild.github.io/api/), [Rollup dynamic import vars](https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars), [Webpack dependency management](https://webpack.js.org/guides/dependency-management/)\n","frontmatter":{"title":"Prefer Statically Analyzable Paths","impact":"HIGH","impactDescription":"avoids accidental broad bundles and file traces","tags":"bundle, nextjs, vite, webpack, rollup, esbuild, path"},"isInternal":false,"tokens":557,"sizeBytes":2382},{"name":"bundle-barrel-imports.md","path":"skills/react-best-practices/rules/bundle-barrel-imports.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/bundle-barrel-imports.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Avoid Barrel File Imports\nimpact: CRITICAL\nimpactDescription: 200-800ms import cost, slow builds\ntags: bundle, imports, tree-shaking, barrel-files, performance\n---\n\n## Avoid Barrel File Imports\n\nImport directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n\nPopular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.\n\n**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.\n\n**Incorrect (imports entire library):**\n\n```tsx\nimport { Check, X, Menu } from 'lucide-react'\n// Loads 1,583 modules, takes ~2.8s extra in dev\n// Runtime cost: 200-800ms on every cold start\n\nimport { Button, TextField } from '@mui/material'\n// Loads 2,225 modules, takes ~4.2s extra in dev\n```\n\n**Correct - Next.js 13.5+ (recommended):**\n\n```js\n// next.config.js - automatically optimizes barrel imports at build time\nmodule.exports = {\n  experimental: {\n    optimizePackageImports: ['lucide-react', '@mui/material']\n  }\n}\n```\n\n```tsx\n// Keep the standard imports - Next.js transforms them to direct imports\nimport { Check, X, Menu } from 'lucide-react'\n// Full TypeScript support, no manual path wrangling\n```\n\nThis is the recommended approach because it preserves TypeScript type safety and editor autocompletion while still eliminating the barrel import cost.\n\n**Correct - Direct imports (non-Next.js projects):**\n\n```tsx\nimport Button from '@mui/material/Button'\nimport TextField from '@mui/material/TextField'\n// Loads only what you use\n```\n\n> **TypeScript warning:** Some libraries (notably `lucide-react`) don't ship `.d.ts` files for their deep import paths. Importing from `lucide-react/dist/esm/icons/check` resolves to an implicit `any` type, causing errors under `strict` or `noImplicitAny`. Prefer `optimizePackageImports` when available, or verify the library exports types for its subpaths before using direct imports.\n\nThese optimizations provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.\n\nLibraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.\n\nReference: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)\n","frontmatter":{"title":"Avoid Barrel File Imports","impact":"CRITICAL","impactDescription":"200-800ms import cost, slow builds","tags":"bundle, imports, tree-shaking, barrel-files, performance"},"isInternal":false,"tokens":672,"sizeBytes":2795},{"name":"bundle-conditional.md","path":"skills/react-best-practices/rules/bundle-conditional.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/bundle-conditional.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Conditional Module Loading\nimpact: HIGH\nimpactDescription: loads large data only when needed\ntags: bundle, conditional-loading, lazy-loading\n---\n\n## Conditional Module Loading\n\nLoad large data or modules only when a feature is activated.\n\n**Example (lazy-load animation frames):**\n\n```tsx\nfunction AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {\n  const [frames, setFrames] = useState<Frame[] | null>(null)\n\n  useEffect(() => {\n    if (enabled && !frames && typeof window !== 'undefined') {\n      import('./animation-frames.js')\n        .then(mod => setFrames(mod.frames))\n        .catch(() => setEnabled(false))\n    }\n  }, [enabled, frames, setEnabled])\n\n  if (!frames) return <Skeleton />\n  return <Canvas frames={frames} />\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.\n","frontmatter":{"title":"Conditional Module Loading","impact":"HIGH","impactDescription":"loads large data only when needed","tags":"bundle, conditional-loading, lazy-loading"},"isInternal":false,"tokens":213,"sizeBytes":949},{"name":"bundle-defer-third-party.md","path":"skills/react-best-practices/rules/bundle-defer-third-party.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/bundle-defer-third-party.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Defer Non-Critical Third-Party Libraries\nimpact: MEDIUM\nimpactDescription: loads after hydration\ntags: bundle, third-party, analytics, defer\n---\n\n## Defer Non-Critical Third-Party Libraries\n\nAnalytics, logging, and error tracking don't block user interaction. Load them after hydration.\n\n**Incorrect (blocks initial bundle):**\n\n```tsx\nimport { Analytics } from '@vercel/analytics/react'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n**Correct (loads after hydration):**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst Analytics = dynamic(\n  () => import('@vercel/analytics/react').then(m => m.Analytics),\n  { ssr: false }\n)\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n","frontmatter":{"title":"Defer Non-Critical Third-Party Libraries","impact":"MEDIUM","impactDescription":"loads after hydration","tags":"bundle, third-party, analytics, defer"},"isInternal":false,"tokens":221,"sizeBytes":920},{"name":"bundle-dynamic-imports.md","path":"skills/react-best-practices/rules/bundle-dynamic-imports.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/bundle-dynamic-imports.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Dynamic Imports for Heavy Components\nimpact: CRITICAL\nimpactDescription: directly affects TTI and LCP\ntags: bundle, dynamic-import, code-splitting, next-dynamic\n---\n\n## Dynamic Imports for Heavy Components\n\nUse `next/dynamic` to lazy-load large components not needed on initial render.\n\n**Incorrect (Monaco bundles with main chunk ~300KB):**\n\n```tsx\nimport { MonacoEditor } from './monaco-editor'\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n**Correct (Monaco loads on demand):**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst MonacoEditor = dynamic(\n  () => import('./monaco-editor').then(m => m.MonacoEditor),\n  { ssr: false }\n)\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n","frontmatter":{"title":"Dynamic Imports for Heavy Components","impact":"CRITICAL","impactDescription":"directly affects TTI and LCP","tags":"bundle, dynamic-import, code-splitting, next-dynamic"},"isInternal":false,"tokens":197,"sizeBytes":791},{"name":"bundle-preload.md","path":"skills/react-best-practices/rules/bundle-preload.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/bundle-preload.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Preload Based on User Intent\nimpact: MEDIUM\nimpactDescription: reduces perceived latency\ntags: bundle, preload, user-intent, hover\n---\n\n## Preload Based on User Intent\n\nPreload heavy bundles before they're needed to reduce perceived latency.\n\n**Example (preload on hover/focus):**\n\n```tsx\nfunction EditorButton({ onClick }: { onClick: () => void }) {\n  const preload = () => {\n    if (typeof window !== 'undefined') {\n      void import('./monaco-editor')\n    }\n  }\n\n  return (\n    <button\n      onMouseEnter={preload}\n      onFocus={preload}\n      onClick={onClick}\n    >\n      Open Editor\n    </button>\n  )\n}\n```\n\n**Example (preload when feature flag is enabled):**\n\n```tsx\nfunction FlagsProvider({ children, flags }: Props) {\n  useEffect(() => {\n    if (flags.editorEnabled && typeof window !== 'undefined') {\n      void import('./monaco-editor').then(mod => mod.init())\n    }\n  }, [flags.editorEnabled])\n\n  return <FlagsContext.Provider value={flags}>\n    {children}\n  </FlagsContext.Provider>\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.\n","frontmatter":{"title":"Preload Based on User Intent","impact":"MEDIUM","impactDescription":"reduces perceived latency","tags":"bundle, preload, user-intent, hover"},"isInternal":false,"tokens":267,"sizeBytes":1149},{"name":"client-event-listeners.md","path":"skills/react-best-practices/rules/client-event-listeners.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/client-event-listeners.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Deduplicate Global Event Listeners\nimpact: LOW\nimpactDescription: single listener for N components\ntags: client, swr, event-listeners, subscription\n---\n\n## Deduplicate Global Event Listeners\n\nUse `useSWRSubscription()` to share global event listeners across component instances.\n\n**Incorrect (N instances = N listeners):**\n\n```tsx\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  useEffect(() => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && e.key === key) {\n        callback()\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  }, [key, callback])\n}\n```\n\nWhen using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.\n\n**Correct (N instances = 1 listener):**\n\n```tsx\nimport useSWRSubscription from 'swr/subscription'\n\n// Module-level Map to track callbacks per key\nconst keyCallbacks = new Map<string, Set<() => void>>()\n\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  // Register this callback in the Map\n  useEffect(() => {\n    if (!keyCallbacks.has(key)) {\n      keyCallbacks.set(key, new Set())\n    }\n    keyCallbacks.get(key)!.add(callback)\n\n    return () => {\n      const set = keyCallbacks.get(key)\n      if (set) {\n        set.delete(callback)\n        if (set.size === 0) {\n          keyCallbacks.delete(key)\n        }\n      }\n    }\n  }, [key, callback])\n\n  useSWRSubscription('global-keydown', () => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && keyCallbacks.has(e.key)) {\n        keyCallbacks.get(e.key)!.forEach(cb => cb())\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  })\n}\n\nfunction Profile() {\n  // Multiple shortcuts will share the same listener\n  useKeyboardShortcut('p', () => { /* ... */ }) \n  useKeyboardShortcut('k', () => { /* ... */ })\n  // ...\n}\n```\n","frontmatter":{"title":"Deduplicate Global Event Listeners","impact":"LOW","impactDescription":"single listener for N components","tags":"client, swr, event-listeners, subscription"},"isInternal":false,"tokens":466,"sizeBytes":1969},{"name":"client-localstorage-schema.md","path":"skills/react-best-practices/rules/client-localstorage-schema.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/client-localstorage-schema.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Version and Minimize localStorage Data\nimpact: MEDIUM\nimpactDescription: prevents schema conflicts, reduces storage size\ntags: client, localStorage, storage, versioning, data-minimization\n---\n\n## Version and Minimize localStorage Data\n\nAdd version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n\n**Incorrect:**\n\n```typescript\n// No version, stores everything, no error handling\nlocalStorage.setItem('userConfig', JSON.stringify(fullUserObject))\nconst data = localStorage.getItem('userConfig')\n```\n\n**Correct:**\n\n```typescript\nconst VERSION = 'v2'\n\nfunction saveConfig(config: { theme: string; language: string }) {\n  try {\n    localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))\n  } catch {\n    // Throws in incognito/private browsing, quota exceeded, or disabled\n  }\n}\n\nfunction loadConfig() {\n  try {\n    const data = localStorage.getItem(`userConfig:${VERSION}`)\n    return data ? JSON.parse(data) : null\n  } catch {\n    return null\n  }\n}\n\n// Migration from v1 to v2\nfunction migrate() {\n  try {\n    const v1 = localStorage.getItem('userConfig:v1')\n    if (v1) {\n      const old = JSON.parse(v1)\n      saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })\n      localStorage.removeItem('userConfig:v1')\n    }\n  } catch {}\n}\n```\n\n**Store minimal fields from server responses:**\n\n```typescript\n// User object has 20+ fields, only store what UI needs\nfunction cachePrefs(user: FullUser) {\n  try {\n    localStorage.setItem('prefs:v1', JSON.stringify({\n      theme: user.preferences.theme,\n      notifications: user.preferences.notifications\n    }))\n  } catch {}\n}\n```\n\n**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.\n\n**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.\n","frontmatter":{"title":"Version and Minimize localStorage Data","impact":"MEDIUM","impactDescription":"prevents schema conflicts, reduces storage size","tags":"client, localStorage, storage, versioning, data-minimization"},"isInternal":false,"tokens":437,"sizeBytes":1950},{"name":"client-passive-event-listeners.md","path":"skills/react-best-practices/rules/client-passive-event-listeners.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/client-passive-event-listeners.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use Passive Event Listeners for Scrolling Performance\nimpact: MEDIUM\nimpactDescription: eliminates scroll delay caused by event listeners\ntags: client, event-listeners, scrolling, performance, touch, wheel\n---\n\n## Use Passive Event Listeners for Scrolling Performance\n\nAdd `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n\n**Incorrect:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch)\n  document.addEventListener('wheel', handleWheel)\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Correct:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch, { passive: true })\n  document.addEventListener('wheel', handleWheel, { passive: true })\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.\n\n**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.\n","frontmatter":{"title":"Use Passive Event Listeners for Scrolling Performance","impact":"MEDIUM","impactDescription":"eliminates scroll delay caused by event listeners","tags":"client, event-listeners, scrolling, performance, touch, wheel"},"isInternal":false,"tokens":361,"sizeBytes":1644},{"name":"client-swr-dedup.md","path":"skills/react-best-practices/rules/client-swr-dedup.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/client-swr-dedup.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use SWR for Automatic Deduplication\nimpact: MEDIUM-HIGH\nimpactDescription: automatic deduplication\ntags: client, swr, deduplication, data-fetching\n---\n\n## Use SWR for Automatic Deduplication\n\nSWR enables request deduplication, caching, and revalidation across component instances.\n\n**Incorrect (no deduplication, each instance fetches):**\n\n```tsx\nfunction UserList() {\n  const [users, setUsers] = useState([])\n  useEffect(() => {\n    fetch('/api/users')\n      .then(r => r.json())\n      .then(setUsers)\n  }, [])\n}\n```\n\n**Correct (multiple instances share one request):**\n\n```tsx\nimport useSWR from 'swr'\n\nfunction UserList() {\n  const { data: users } = useSWR('/api/users', fetcher)\n}\n```\n\n**For immutable data:**\n\n```tsx\nimport { useImmutableSWR } from '@/lib/swr'\n\nfunction StaticContent() {\n  const { data } = useImmutableSWR('/api/config', fetcher)\n}\n```\n\n**For mutations:**\n\n```tsx\nimport { useSWRMutation } from 'swr/mutation'\n\nfunction UpdateButton() {\n  const { trigger } = useSWRMutation('/api/user', updateUser)\n  return <button onClick={() => trigger()}>Update</button>\n}\n```\n\nReference: [https://swr.vercel.app](https://swr.vercel.app)\n","frontmatter":{"title":"Use SWR for Automatic Deduplication","impact":"MEDIUM-HIGH","impactDescription":"automatic deduplication","tags":"client, swr, deduplication, data-fetching"},"isInternal":false,"tokens":308,"sizeBytes":1159},{"name":"js-batch-dom-css.md","path":"skills/react-best-practices/rules/js-batch-dom-css.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-batch-dom-css.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Avoid Layout Thrashing\nimpact: MEDIUM\nimpactDescription: prevents forced synchronous layouts and reduces performance bottlenecks\ntags: javascript, dom, css, performance, reflow, layout-thrashing\n---\n\n## Avoid Layout Thrashing\n\nAvoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.\n\n**This is OK (browser batches style changes):**\n```typescript\nfunction updateElementStyles(element: HTMLElement) {\n  // Each line invalidates style, but browser batches the recalculation\n  element.style.width = '100px'\n  element.style.height = '200px'\n  element.style.backgroundColor = 'blue'\n  element.style.border = '1px solid black'\n}\n```\n\n**Incorrect (interleaved reads and writes force reflows):**\n```typescript\nfunction layoutThrashing(element: HTMLElement) {\n  element.style.width = '100px'\n  const width = element.offsetWidth  // Forces reflow\n  element.style.height = '200px'\n  const height = element.offsetHeight  // Forces another reflow\n}\n```\n\n**Correct (batch writes, then read once):**\n```typescript\nfunction updateElementStyles(element: HTMLElement) {\n  // Batch all writes together\n  element.style.width = '100px'\n  element.style.height = '200px'\n  element.style.backgroundColor = 'blue'\n  element.style.border = '1px solid black'\n  \n  // Read after all writes are done (single reflow)\n  const { width, height } = element.getBoundingClientRect()\n}\n```\n\n**Correct (batch reads, then writes):**\n```typescript\nfunction avoidThrashing(element: HTMLElement) {\n  // Read phase - all layout queries first\n  const rect1 = element.getBoundingClientRect()\n  const offsetWidth = element.offsetWidth\n  const offsetHeight = element.offsetHeight\n  \n  // Write phase - all style changes after\n  element.style.width = '100px'\n  element.style.height = '200px'\n}\n```\n\n**Better: use CSS classes**\n```css\n.highlighted-box {\n  width: 100px;\n  height: 200px;\n  background-color: blue;\n  border: 1px solid black;\n}\n```\n```typescript\nfunction updateElementStyles(element: HTMLElement) {\n  element.classList.add('highlighted-box')\n  \n  const { width, height } = element.getBoundingClientRect()\n}\n```\n\n**React example:**\n```tsx\n// Incorrect: interleaving style changes with layout queries\nfunction Box({ isHighlighted }: { isHighlighted: boolean }) {\n  const ref = useRef<HTMLDivElement>(null)\n  \n  useEffect(() => {\n    if (ref.current && isHighlighted) {\n      ref.current.style.width = '100px'\n      const width = ref.current.offsetWidth // Forces layout\n      ref.current.style.height = '200px'\n    }\n  }, [isHighlighted])\n  \n  return <div ref={ref}>Content</div>\n}\n\n// Correct: toggle class\nfunction Box({ isHighlighted }: { isHighlighted: boolean }) {\n  return (\n    <div className={isHighlighted ? 'highlighted-box' : ''}>\n      Content\n    </div>\n  )\n}\n```\n\nPrefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.\n\nSee [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.\n","frontmatter":{"title":"Avoid Layout Thrashing","impact":"MEDIUM","impactDescription":"prevents forced synchronous layouts and reduces performance bottlenecks","tags":"javascript, dom, css, performance, reflow, layout-thrashing"},"isInternal":false,"tokens":757,"sizeBytes":3266},{"name":"js-cache-function-results.md","path":"skills/react-best-practices/rules/js-cache-function-results.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-cache-function-results.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Cache Repeated Function Calls\nimpact: MEDIUM\nimpactDescription: avoid redundant computation\ntags: javascript, cache, memoization, performance\n---\n\n## Cache Repeated Function Calls\n\nUse a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.\n\n**Incorrect (redundant computation):**\n\n```typescript\nfunction ProjectList({ projects }: { projects: Project[] }) {\n  return (\n    <div>\n      {projects.map(project => {\n        // slugify() called 100+ times for same project names\n        const slug = slugify(project.name)\n        \n        return <ProjectCard key={project.id} slug={slug} />\n      })}\n    </div>\n  )\n}\n```\n\n**Correct (cached results):**\n\n```typescript\n// Module-level cache\nconst slugifyCache = new Map<string, string>()\n\nfunction cachedSlugify(text: string): string {\n  if (slugifyCache.has(text)) {\n    return slugifyCache.get(text)!\n  }\n  const result = slugify(text)\n  slugifyCache.set(text, result)\n  return result\n}\n\nfunction ProjectList({ projects }: { projects: Project[] }) {\n  return (\n    <div>\n      {projects.map(project => {\n        // Computed only once per unique project name\n        const slug = cachedSlugify(project.name)\n        \n        return <ProjectCard key={project.id} slug={slug} />\n      })}\n    </div>\n  )\n}\n```\n\n**Simpler pattern for single-value functions:**\n\n```typescript\nlet isLoggedInCache: boolean | null = null\n\nfunction isLoggedIn(): boolean {\n  if (isLoggedInCache !== null) {\n    return isLoggedInCache\n  }\n  \n  isLoggedInCache = document.cookie.includes('auth=')\n  return isLoggedInCache\n}\n\n// Clear cache when auth changes\nfunction onAuthChange() {\n  isLoggedInCache = null\n}\n```\n\nUse a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.\n\nReference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)\n","frontmatter":{"title":"Cache Repeated Function Calls","impact":"MEDIUM","impactDescription":"avoid redundant computation","tags":"javascript, cache, memoization, performance"},"isInternal":false,"tokens":448,"sizeBytes":1949},{"name":"js-cache-property-access.md","path":"skills/react-best-practices/rules/js-cache-property-access.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-cache-property-access.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Cache Property Access in Loops\nimpact: LOW-MEDIUM\nimpactDescription: reduces lookups\ntags: javascript, loops, optimization, caching\n---\n\n## Cache Property Access in Loops\n\nCache object property lookups in hot paths.\n\n**Incorrect (3 lookups × N iterations):**\n\n```typescript\nfor (let i = 0; i < arr.length; i++) {\n  process(obj.config.settings.value)\n}\n```\n\n**Correct (1 lookup total):**\n\n```typescript\nconst value = obj.config.settings.value\nconst len = arr.length\nfor (let i = 0; i < len; i++) {\n  process(value)\n}\n```\n","frontmatter":{"title":"Cache Property Access in Loops","impact":"LOW-MEDIUM","impactDescription":"reduces lookups","tags":"javascript, loops, optimization, caching"},"isInternal":false,"tokens":138,"sizeBytes":532},{"name":"js-cache-storage.md","path":"skills/react-best-practices/rules/js-cache-storage.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-cache-storage.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Cache Storage API Calls\nimpact: LOW-MEDIUM\nimpactDescription: reduces expensive I/O\ntags: javascript, localStorage, storage, caching, performance\n---\n\n## Cache Storage API Calls\n\n`localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory.\n\n**Incorrect (reads storage on every call):**\n\n```typescript\nfunction getTheme() {\n  return localStorage.getItem('theme') ?? 'light'\n}\n// Called 10 times = 10 storage reads\n```\n\n**Correct (Map cache):**\n\n```typescript\nconst storageCache = new Map<string, string | null>()\n\nfunction getLocalStorage(key: string) {\n  if (!storageCache.has(key)) {\n    storageCache.set(key, localStorage.getItem(key))\n  }\n  return storageCache.get(key)\n}\n\nfunction setLocalStorage(key: string, value: string) {\n  localStorage.setItem(key, value)\n  storageCache.set(key, value)  // keep cache in sync\n}\n```\n\nUse a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.\n\n**Cookie caching:**\n\n```typescript\nlet cookieCache: Record<string, string> | null = null\n\nfunction getCookie(name: string) {\n  if (!cookieCache) {\n    cookieCache = Object.fromEntries(\n      document.cookie.split('; ').map(c => c.split('='))\n    )\n  }\n  return cookieCache[name]\n}\n```\n\n**Important (invalidate on external changes):**\n\nIf storage can change externally (another tab, server-set cookies), invalidate cache:\n\n```typescript\nwindow.addEventListener('storage', (e) => {\n  if (e.key) storageCache.delete(e.key)\n})\n\ndocument.addEventListener('visibilitychange', () => {\n  if (document.visibilityState === 'visible') {\n    storageCache.clear()\n  }\n})\n```\n","frontmatter":{"title":"Cache Storage API Calls","impact":"LOW-MEDIUM","impactDescription":"reduces expensive I/O","tags":"javascript, localStorage, storage, caching, performance"},"isInternal":false,"tokens":377,"sizeBytes":1651},{"name":"js-combine-iterations.md","path":"skills/react-best-practices/rules/js-combine-iterations.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-combine-iterations.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Combine Multiple Array Iterations\nimpact: LOW-MEDIUM\nimpactDescription: reduces iterations\ntags: javascript, arrays, loops, performance\n---\n\n## Combine Multiple Array Iterations\n\nMultiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.\n\n**Incorrect (3 iterations):**\n\n```typescript\nconst admins = users.filter(u => u.isAdmin)\nconst testers = users.filter(u => u.isTester)\nconst inactive = users.filter(u => !u.isActive)\n```\n\n**Correct (1 iteration):**\n\n```typescript\nconst admins: User[] = []\nconst testers: User[] = []\nconst inactive: User[] = []\n\nfor (const user of users) {\n  if (user.isAdmin) admins.push(user)\n  if (user.isTester) testers.push(user)\n  if (!user.isActive) inactive.push(user)\n}\n```\n","frontmatter":{"title":"Combine Multiple Array Iterations","impact":"LOW-MEDIUM","impactDescription":"reduces iterations","tags":"javascript, arrays, loops, performance"},"isInternal":false,"tokens":176,"sizeBytes":753},{"name":"js-early-exit.md","path":"skills/react-best-practices/rules/js-early-exit.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-early-exit.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Early Return from Functions\nimpact: LOW-MEDIUM\nimpactDescription: avoids unnecessary computation\ntags: javascript, functions, optimization, early-return\n---\n\n## Early Return from Functions\n\nReturn early when result is determined to skip unnecessary processing.\n\n**Incorrect (processes all items even after finding answer):**\n\n```typescript\nfunction validateUsers(users: User[]) {\n  let hasError = false\n  let errorMessage = ''\n  \n  for (const user of users) {\n    if (!user.email) {\n      hasError = true\n      errorMessage = 'Email required'\n    }\n    if (!user.name) {\n      hasError = true\n      errorMessage = 'Name required'\n    }\n    // Continues checking all users even after error found\n  }\n  \n  return hasError ? { valid: false, error: errorMessage } : { valid: true }\n}\n```\n\n**Correct (returns immediately on first error):**\n\n```typescript\nfunction validateUsers(users: User[]) {\n  for (const user of users) {\n    if (!user.email) {\n      return { valid: false, error: 'Email required' }\n    }\n    if (!user.name) {\n      return { valid: false, error: 'Name required' }\n    }\n  }\n\n  return { valid: true }\n}\n```\n","frontmatter":{"title":"Early Return from Functions","impact":"LOW-MEDIUM","impactDescription":"avoids unnecessary computation","tags":"javascript, functions, optimization, early-return"},"isInternal":false,"tokens":266,"sizeBytes":1133},{"name":"js-flatmap-filter.md","path":"skills/react-best-practices/rules/js-flatmap-filter.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-flatmap-filter.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Use flatMap to Map and Filter in One Pass\nimpact: LOW-MEDIUM\nimpactDescription: eliminates intermediate array\ntags: javascript, arrays, flatMap, filter, performance\n---\n\n## Use flatMap to Map and Filter in One Pass\n\n**Impact: LOW-MEDIUM (eliminates intermediate array)**\n\nChaining `.map().filter(Boolean)` creates an intermediate array and iterates twice. Use `.flatMap()` to transform and filter in a single pass.\n\n**Incorrect (2 iterations, intermediate array):**\n\n```typescript\nconst userNames = users\n  .map(user => user.isActive ? user.name : null)\n  .filter(Boolean)\n```\n\n**Correct (1 iteration, no intermediate array):**\n\n```typescript\nconst userNames = users.flatMap(user =>\n  user.isActive ? [user.name] : []\n)\n```\n\n**More examples:**\n\n```typescript\n// Extract valid emails from responses\n// Before\nconst emails = responses\n  .map(r => r.success ? r.data.email : null)\n  .filter(Boolean)\n\n// After\nconst emails = responses.flatMap(r =>\n  r.success ? [r.data.email] : []\n)\n\n// Parse and filter valid numbers\n// Before\nconst numbers = strings\n  .map(s => parseInt(s, 10))\n  .filter(n => !isNaN(n))\n\n// After\nconst numbers = strings.flatMap(s => {\n  const n = parseInt(s, 10)\n  return isNaN(n) ? [] : [n]\n})\n```\n\n**When to use:**\n- Transforming items while filtering some out\n- Conditional mapping where some inputs produce no output\n- Parsing/validating where invalid inputs should be skipped\n","frontmatter":{"title":"Use flatMap to Map and Filter in One Pass","impact":"LOW-MEDIUM","impactDescription":"eliminates intermediate array","tags":"javascript, arrays, flatMap, filter, performance"},"isInternal":false,"tokens":342,"sizeBytes":1411},{"name":"js-hoist-regexp.md","path":"skills/react-best-practices/rules/js-hoist-regexp.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-hoist-regexp.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Hoist RegExp Creation\nimpact: LOW-MEDIUM\nimpactDescription: avoids recreation\ntags: javascript, regexp, optimization, memoization\n---\n\n## Hoist RegExp Creation\n\nDon't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.\n\n**Incorrect (new RegExp every render):**\n\n```tsx\nfunction Highlighter({ text, query }: Props) {\n  const regex = new RegExp(`(${query})`, 'gi')\n  const parts = text.split(regex)\n  return <>{parts.map((part, i) => ...)}</>\n}\n```\n\n**Correct (memoize or hoist):**\n\n```tsx\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nfunction Highlighter({ text, query }: Props) {\n  const regex = useMemo(\n    () => new RegExp(`(${escapeRegex(query)})`, 'gi'),\n    [query]\n  )\n  const parts = text.split(regex)\n  return <>{parts.map((part, i) => ...)}</>\n}\n```\n\n**Warning (global regex has mutable state):**\n\nGlobal regex (`/g`) has mutable `lastIndex` state:\n\n```typescript\nconst regex = /foo/g\nregex.test('foo')  // true, lastIndex = 3\nregex.test('foo')  // false, lastIndex = 0\n```\n","frontmatter":{"title":"Hoist RegExp Creation","impact":"LOW-MEDIUM","impactDescription":"avoids recreation","tags":"javascript, regexp, optimization, memoization"},"isInternal":false,"tokens":283,"sizeBytes":1028},{"name":"js-index-maps.md","path":"skills/react-best-practices/rules/js-index-maps.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-index-maps.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-index-maps.md -->","frontmatter":{"title":"Build Index Maps for Repeated Lookups","impact":"LOW-MEDIUM","impactDescription":"1M ops to 2K ops","tags":"javascript, map, indexing, optimization, performance"},"isInternal":false,"tokens":229,"sizeBytes":837},{"name":"js-length-check-first.md","path":"skills/react-best-practices/rules/js-length-check-first.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-length-check-first.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-length-check-first.md -->","frontmatter":{"title":"Early Length Check for Array Comparisons","impact":"MEDIUM-HIGH","impactDescription":"avoids expensive operations when lengths differ","tags":"javascript, arrays, performance, optimization, comparison"},"isInternal":false,"tokens":373,"sizeBytes":1747},{"name":"js-min-max-loop.md","path":"skills/react-best-practices/rules/js-min-max-loop.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-min-max-loop.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-min-max-loop.md -->","frontmatter":{"title":"Use Loop for Min/Max Instead of Sort","impact":"LOW","impactDescription":"O(n) instead of O(n log n)","tags":"javascript, arrays, performance, sorting, algorithms"},"isInternal":false,"tokens":602,"sizeBytes":2290},{"name":"js-request-idle-callback.md","path":"skills/react-best-practices/rules/js-request-idle-callback.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-request-idle-callback.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-request-idle-callback.md -->","frontmatter":{"title":"Defer Non-Critical Work with requestIdleCallback","impact":"MEDIUM","impactDescription":"keeps UI responsive during background tasks","tags":"javascript, performance, idle, scheduling, analytics"},"isInternal":false,"tokens":597,"sizeBytes":2647},{"name":"js-set-map-lookups.md","path":"skills/react-best-practices/rules/js-set-map-lookups.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-set-map-lookups.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-set-map-lookups.md -->","frontmatter":{"title":"Use Set/Map for O(1) Lookups","impact":"LOW-MEDIUM","impactDescription":"O(n) to O(1)","tags":"javascript, set, map, data-structures, performance"},"isInternal":false,"tokens":154,"sizeBytes":532},{"name":"js-tosorted-immutable.md","path":"skills/react-best-practices/rules/js-tosorted-immutable.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/js-tosorted-immutable.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fjs-tosorted-immutable.md -->","frontmatter":{"title":"Use toSorted() Instead of sort() for Immutability","impact":"MEDIUM-HIGH","impactDescription":"prevents mutation bugs in React state","tags":"javascript, arrays, immutability, react, state, mutation"},"isInternal":false,"tokens":436,"sizeBytes":1782},{"name":"rendering-activity.md","path":"skills/react-best-practices/rules/rendering-activity.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-activity.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-activity.md -->","frontmatter":{"title":"Use Activity Component for Show/Hide","impact":"MEDIUM","impactDescription":"preserves state/DOM","tags":"rendering, activity, visibility, state-preservation"},"isInternal":false,"tokens":131,"sizeBytes":564},{"name":"rendering-animate-svg-wrapper.md","path":"skills/react-best-practices/rules/rendering-animate-svg-wrapper.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-animate-svg-wrapper.md -->","frontmatter":{"title":"Animate SVG Wrapper Instead of SVG Element","impact":"LOW","impactDescription":"enables hardware acceleration","tags":"rendering, svg, css, animation, performance"},"isInternal":false,"tokens":291,"sizeBytes":1185},{"name":"rendering-conditional-render.md","path":"skills/react-best-practices/rules/rendering-conditional-render.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-conditional-render.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-conditional-render.md -->","frontmatter":{"title":"Use Explicit Conditional Rendering","impact":"LOW","impactDescription":"prevents rendering 0 or NaN","tags":"rendering, conditional, jsx, falsy-values"},"isInternal":false,"tokens":273,"sizeBytes":980},{"name":"rendering-content-visibility.md","path":"skills/react-best-practices/rules/rendering-content-visibility.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-content-visibility.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-content-visibility.md -->","frontmatter":{"title":"CSS content-visibility for Long Lists","impact":"HIGH","impactDescription":"faster initial render","tags":"rendering, css, content-visibility, long-lists"},"isInternal":false,"tokens":203,"sizeBytes":815},{"name":"rendering-hoist-jsx.md","path":"skills/react-best-practices/rules/rendering-hoist-jsx.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-hoist-jsx.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-hoist-jsx.md -->","frontmatter":{"title":"Hoist Static JSX Elements","impact":"LOW","impactDescription":"avoids re-creation","tags":"rendering, jsx, static, optimization"},"isInternal":false,"tokens":244,"sizeBytes":1039},{"name":"rendering-hydration-no-flicker.md","path":"skills/react-best-practices/rules/rendering-hydration-no-flicker.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-hydration-no-flicker.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-hydration-no-flicker.md -->","frontmatter":{"title":"Prevent Hydration Mismatch Without Flickering","impact":"MEDIUM","impactDescription":"avoids visual flicker and hydration errors","tags":"rendering, ssr, hydration, localStorage, flicker"},"isInternal":false,"tokens":490,"sizeBytes":2308},{"name":"rendering-hydration-suppress-warning.md","path":"skills/react-best-practices/rules/rendering-hydration-suppress-warning.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-hydration-suppress-warning.md -->","frontmatter":{"title":"Suppress Expected Hydration Mismatches","impact":"LOW-MEDIUM","impactDescription":"avoids noisy hydration warnings for known differences","tags":"rendering, hydration, ssr, nextjs"},"isInternal":false,"tokens":197,"sizeBytes":872},{"name":"rendering-resource-hints.md","path":"skills/react-best-practices/rules/rendering-resource-hints.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-resource-hints.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-resource-hints.md -->","frontmatter":{"title":"Use React DOM Resource Hints","impact":"HIGH","impactDescription":"reduces load time for critical resources","tags":"rendering, preload, preconnect, prefetch, resource-hints"},"isInternal":false,"tokens":621,"sizeBytes":2549},{"name":"rendering-script-defer-async.md","path":"skills/react-best-practices/rules/rendering-script-defer-async.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-script-defer-async.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-script-defer-async.md -->","frontmatter":{"title":"Use defer or async on Script Tags","impact":"HIGH","impactDescription":"eliminates render-blocking","tags":"rendering, script, defer, async, performance"},"isInternal":false,"tokens":430,"sizeBytes":1868},{"name":"rendering-svg-precision.md","path":"skills/react-best-practices/rules/rendering-svg-precision.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-svg-precision.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-svg-precision.md -->","frontmatter":{"title":"Optimize SVG Precision","impact":"LOW","impactDescription":"reduces file size","tags":"rendering, svg, optimization, svgo"},"isInternal":false,"tokens":167,"sizeBytes":588},{"name":"rendering-usetransition-loading.md","path":"skills/react-best-practices/rules/rendering-usetransition-loading.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rendering-usetransition-loading.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frendering-usetransition-loading.md -->","frontmatter":{"title":"Use useTransition Over Manual Loading States","impact":"LOW","impactDescription":"reduces re-renders and improves code clarity","tags":"rendering, transitions, useTransition, loading, state"},"isInternal":false,"tokens":457,"sizeBytes":2074},{"name":"rerender-defer-reads.md","path":"skills/react-best-practices/rules/rerender-defer-reads.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-defer-reads.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-defer-reads.md -->","frontmatter":{"title":"Defer State Reads to Usage Point","impact":"MEDIUM","impactDescription":"avoids unnecessary subscriptions","tags":"rerender, searchParams, localStorage, optimization"},"isInternal":false,"tokens":231,"sizeBytes":973},{"name":"rerender-dependencies.md","path":"skills/react-best-practices/rules/rerender-dependencies.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-dependencies.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-dependencies.md -->","frontmatter":{"title":"Narrow Effect Dependencies","impact":"LOW","impactDescription":"minimizes effect re-runs","tags":"rerender, useEffect, dependencies, optimization"},"isInternal":false,"tokens":214,"sizeBytes":824},{"name":"rerender-derived-state-no-effect.md","path":"skills/react-best-practices/rules/rerender-derived-state-no-effect.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-derived-state-no-effect.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-derived-state-no-effect.md -->","frontmatter":{"title":"Calculate Derived State During Rendering","impact":"MEDIUM","impactDescription":"avoids redundant renders and state drift","tags":"rerender, derived-state, useEffect, state"},"isInternal":false,"tokens":283,"sizeBytes":1201},{"name":"rerender-derived-state.md","path":"skills/react-best-practices/rules/rerender-derived-state.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-derived-state.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-derived-state.md -->","frontmatter":{"title":"Subscribe to Derived State","impact":"MEDIUM","impactDescription":"reduces re-render frequency","tags":"rerender, derived-state, media-query, optimization"},"isInternal":false,"tokens":175,"sizeBytes":728},{"name":"rerender-functional-setstate.md","path":"skills/react-best-practices/rules/rerender-functional-setstate.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-functional-setstate.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-functional-setstate.md -->","frontmatter":{"title":"Use Functional setState Updates","impact":"MEDIUM","impactDescription":"prevents stale closures and unnecessary callback recreations","tags":"react, hooks, useState, useCallback, callbacks, closures"},"isInternal":false,"tokens":654,"sizeBytes":2968},{"name":"rerender-lazy-state-init.md","path":"skills/react-best-practices/rules/rerender-lazy-state-init.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-lazy-state-init.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-lazy-state-init.md -->","frontmatter":{"title":"Use Lazy State Initialization","impact":"MEDIUM","impactDescription":"wasted computation on every render","tags":"react, hooks, useState, performance, initialization"},"isInternal":false,"tokens":438,"sizeBytes":2016},{"name":"rerender-memo-with-default-value.md","path":"skills/react-best-practices/rules/rerender-memo-with-default-value.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-memo-with-default-value.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-memo-with-default-value.md -->","frontmatter":{"title":"Extract Default Non-primitive Parameter Value from Memoized Component to Constant","impact":"MEDIUM","impactDescription":"restores memoization by using a constant for default value","tags":"rerender, memo, optimization"},"isInternal":false,"tokens":248,"sizeBytes":1173},{"name":"rerender-memo.md","path":"skills/react-best-practices/rules/rerender-memo.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-memo.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-memo.md -->","frontmatter":{"title":"Extract to Memoized Components","impact":"MEDIUM","impactDescription":"enables early returns","tags":"rerender, memo, useMemo, optimization"},"isInternal":false,"tokens":278,"sizeBytes":1148},{"name":"rerender-move-effect-to-event.md","path":"skills/react-best-practices/rules/rerender-move-effect-to-event.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-move-effect-to-event.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-move-effect-to-event.md -->","frontmatter":{"title":"Put Interaction Logic in Event Handlers","impact":"MEDIUM","impactDescription":"avoids effect re-runs and duplicate side effects","tags":"rerender, useEffect, events, side-effects, dependencies"},"isInternal":false,"tokens":285,"sizeBytes":1268},{"name":"rerender-no-inline-components.md","path":"skills/react-best-practices/rules/rerender-no-inline-components.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-no-inline-components.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-no-inline-components.md -->","frontmatter":{"title":"Don't Define Components Inside Components","impact":"HIGH","impactDescription":"prevents remount on every render","tags":"rerender, components, remount, performance"},"isInternal":false,"tokens":523,"sizeBytes":2135},{"name":"rerender-simple-expression-in-memo.md","path":"skills/react-best-practices/rules/rerender-simple-expression-in-memo.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-simple-expression-in-memo.md -->","frontmatter":{"title":"Do not wrap a simple expression with a primitive result type in useMemo","impact":"LOW-MEDIUM","impactDescription":"wasted computation on every render","tags":"rerender, useMemo, optimization"},"isInternal":false,"tokens":217,"sizeBytes":1018},{"name":"rerender-split-combined-hooks.md","path":"skills/react-best-practices/rules/rerender-split-combined-hooks.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-split-combined-hooks.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-split-combined-hooks.md -->","frontmatter":{"title":"Split Combined Hook Computations","impact":"MEDIUM","impactDescription":"avoids recomputing independent steps","tags":"rerender, useMemo, useEffect, dependencies, optimization"},"isInternal":false,"tokens":421,"sizeBytes":1844},{"name":"rerender-transitions.md","path":"skills/react-best-practices/rules/rerender-transitions.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-transitions.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-transitions.md -->","frontmatter":{"title":"Use Transitions for Non-Urgent Updates","impact":"MEDIUM","impactDescription":"maintains UI responsiveness","tags":"rerender, transitions, startTransition, performance"},"isInternal":false,"tokens":241,"sizeBytes":1055},{"name":"rerender-use-deferred-value.md","path":"skills/react-best-practices/rules/rerender-use-deferred-value.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-use-deferred-value.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-use-deferred-value.md -->","frontmatter":{"title":"Use useDeferredValue for Expensive Derived Renders","impact":"MEDIUM","impactDescription":"keeps input responsive during heavy computation","tags":"rerender, useDeferredValue, optimization, concurrent"},"isInternal":false,"tokens":421,"sizeBytes":1846},{"name":"rerender-use-ref-transient-values.md","path":"skills/react-best-practices/rules/rerender-use-ref-transient-values.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/rerender-use-ref-transient-values.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Frerender-use-ref-transient-values.md -->","frontmatter":{"title":"Use useRef for Transient Values","impact":"MEDIUM","impactDescription":"avoids unnecessary re-renders on frequent updates","tags":"rerender, useref, state, performance"},"isInternal":false,"tokens":436,"sizeBytes":1742},{"name":"server-after-nonblocking.md","path":"skills/react-best-practices/rules/server-after-nonblocking.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-after-nonblocking.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-after-nonblocking.md -->","frontmatter":{"title":"Use after() for Non-Blocking Operations","impact":"MEDIUM","impactDescription":"faster response times","tags":"server, async, logging, analytics, side-effects"},"isInternal":false,"tokens":458,"sizeBytes":2012},{"name":"server-auth-actions.md","path":"skills/react-best-practices/rules/server-auth-actions.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-auth-actions.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-auth-actions.md -->","frontmatter":{"title":"Authenticate Server Actions Like API Routes","impact":"CRITICAL","impactDescription":"prevents unauthorized access to server mutations","tags":"server, server-actions, authentication, security, authorization"},"isInternal":false,"tokens":585,"sizeBytes":2649},{"name":"server-cache-lru.md","path":"skills/react-best-practices/rules/server-cache-lru.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-cache-lru.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-cache-lru.md -->","frontmatter":{"title":"Cross-Request LRU Caching","impact":"HIGH","impactDescription":"caches across requests","tags":"server, cache, lru, cross-request"},"isInternal":false,"tokens":326,"sizeBytes":1353},{"name":"server-cache-react.md","path":"skills/react-best-practices/rules/server-cache-react.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-cache-react.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-cache-react.md -->","frontmatter":{"title":"Per-Request Deduplication with React.cache()","impact":"MEDIUM","impactDescription":"deduplicates within request","tags":"server, cache, react-cache, deduplication"},"isInternal":false,"tokens":513,"sizeBytes":2228},{"name":"server-dedup-props.md","path":"skills/react-best-practices/rules/server-dedup-props.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-dedup-props.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-dedup-props.md -->","frontmatter":{"title":"Avoid Duplicate Serialization in RSC Props","impact":"LOW","impactDescription":"reduces network payload by avoiding duplicate serialization","tags":"server, rsc, serialization, props, client-components"},"isInternal":false,"tokens":499,"sizeBytes":2060},{"name":"server-hoist-static-io.md","path":"skills/react-best-practices/rules/server-hoist-static-io.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-hoist-static-io.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-hoist-static-io.md -->","frontmatter":{"title":"Hoist Static I/O to Module Level","impact":"HIGH","impactDescription":"avoids repeated file/network I/O per request","tags":"server, io, performance, next.js, route-handlers, og-image"},"isInternal":false,"tokens":1048,"sizeBytes":4423},{"name":"server-no-shared-module-state.md","path":"skills/react-best-practices/rules/server-no-shared-module-state.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-no-shared-module-state.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-no-shared-module-state.md -->","frontmatter":{"title":"Avoid Shared Module State for Request Data","impact":"HIGH","impactDescription":"prevents concurrency bugs and request data leaks","tags":"server, rsc, ssr, concurrency, security, state"},"isInternal":false,"tokens":368,"sizeBytes":1766},{"name":"server-parallel-fetching.md","path":"skills/react-best-practices/rules/server-parallel-fetching.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-parallel-fetching.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-parallel-fetching.md -->","frontmatter":{"title":"Parallel Data Fetching with Component Composition","impact":"CRITICAL","impactDescription":"eliminates server-side waterfalls","tags":"server, rsc, parallel-fetching, composition"},"isInternal":false,"tokens":365,"sizeBytes":1554},{"name":"server-parallel-nested-fetching.md","path":"skills/react-best-practices/rules/server-parallel-nested-fetching.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-parallel-nested-fetching.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-parallel-nested-fetching.md -->","frontmatter":{"title":"Parallel Nested Data Fetching","impact":"CRITICAL","impactDescription":"eliminates server-side waterfalls","tags":"server, rsc, parallel-fetching, promise-chaining"},"isInternal":false,"tokens":230,"sizeBytes":992},{"name":"server-serialization.md","path":"skills/react-best-practices/rules/server-serialization.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/rules/server-serialization.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2Frules%2Fserver-serialization.md -->","frontmatter":{"title":"Minimize Serialization at RSC Boundaries","impact":"HIGH","impactDescription":"reduces data transfer size","tags":"server, rsc, serialization, props"},"isInternal":false,"tokens":242,"sizeBytes":996},{"name":"SKILL.md","path":"skills/react-best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-best-practices/SKILL.md","title":"vercel-react-best-practices","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-best-practices%2FSKILL.md -->","frontmatter":{"name":"vercel-react-best-practices","description":"React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.","license":"MIT","metadata":{"author":"vercel","version":"1.0.0"}},"isInternal":false,"tokens":1756,"sizeBytes":7251},{"name":"AGENTS.md","path":"skills/react-native-skills/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/AGENTS.md","title":"React-native-skills Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2FAGENTS.md -->","isInternal":false,"tokens":12344,"sizeBytes":50096},{"name":"metadata.json","path":"skills/react-native-skills/metadata.json","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/metadata.json","title":"React-native-skills Skill","category":"anthropic-skill","format":"json","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Fmetadata.json -->","frontmatter":{"version":"1.0.0","organization":"Engineering","date":"January 2026","abstract":"Comprehensive performance optimization guide for React Native applications, designed for AI agents and LLMs. Contains 35+ rules across 13 categories, prioritized by impact from critical (core rendering, list performance) to incremental (fonts, imports). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.","references":["https://react.dev","https://reactnative.dev","https://docs.swmansion.com/react-native-reanimated","https://docs.swmansion.com/react-native-gesture-handler","https://docs.expo.dev","https://legendapp.com/open-source/legend-list","https://github.com/nandorojo/galeria","https://zeego.dev"]},"isInternal":false,"tokens":208,"sizeBytes":896},{"name":"README.md","path":"skills/react-native-skills/README.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/README.md","title":"React-native-skills Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2FREADME.md -->","isInternal":false,"tokens":1235,"sizeBytes":5064},{"name":"_sections.md","path":"skills/react-native-skills/rules/_sections.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/_sections.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2F_sections.md -->","isInternal":false,"tokens":485,"sizeBytes":2228},{"name":"_template.md","path":"skills/react-native-skills/rules/_template.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/_template.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2F_template.md -->","frontmatter":{"title":"Rule Title Here","impact":"MEDIUM","impactDescription":"Optional description of impact (e.g., \"20-50% improvement\")","tags":"tag1, tag2"},"isInternal":false,"tokens":143,"sizeBytes":631},{"name":"animation-derived-value.md","path":"skills/react-native-skills/rules/animation-derived-value.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/animation-derived-value.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fanimation-derived-value.md -->","frontmatter":{"title":"Prefer useDerivedValue Over useAnimatedReaction","impact":"MEDIUM","impactDescription":"cleaner code, automatic dependency tracking","tags":"animation, reanimated, derived-value"},"isInternal":false,"tokens":317,"sizeBytes":1373},{"name":"animation-gesture-detector-press.md","path":"skills/react-native-skills/rules/animation-gesture-detector-press.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/animation-gesture-detector-press.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fanimation-gesture-detector-press.md -->","frontmatter":{"title":"Use GestureDetector for Animated Press States","impact":"MEDIUM","impactDescription":"UI thread animations, smoother press feedback","tags":"animation, gestures, press, reanimated"},"isInternal":false,"tokens":638,"sizeBytes":2559},{"name":"animation-gpu-properties.md","path":"skills/react-native-skills/rules/animation-gpu-properties.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/animation-gpu-properties.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fanimation-gpu-properties.md -->","frontmatter":{"title":"Animate Transform and Opacity Instead of Layout Properties","impact":"HIGH","impactDescription":"GPU-accelerated animations, no layout recalculation","tags":"animation, performance, reanimated, transform, opacity"},"isInternal":false,"tokens":494,"sizeBytes":2059},{"name":"design-system-compound-components.md","path":"skills/react-native-skills/rules/design-system-compound-components.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/design-system-compound-components.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fdesign-system-compound-components.md -->","frontmatter":{"title":"Use Compound Components Over Polymorphic Children","impact":"MEDIUM","impactDescription":"flexible composition, clearer API","tags":"design-system, components, composition"},"isInternal":false,"tokens":382,"sizeBytes":1625},{"name":"fonts-config-plugin.md","path":"skills/react-native-skills/rules/fonts-config-plugin.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/fonts-config-plugin.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Ffonts-config-plugin.md -->","frontmatter":{"title":"Load fonts natively at build time","impact":"LOW","impactDescription":"fonts available at launch, no async loading","tags":"fonts, expo, performance, config-plugin"},"isInternal":false,"tokens":363,"sizeBytes":1393},{"name":"imports-design-system-folder.md","path":"skills/react-native-skills/rules/imports-design-system-folder.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/imports-design-system-folder.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fimports-design-system-folder.md -->","frontmatter":{"title":"Import from Design System Folder","impact":"LOW","impactDescription":"enables global changes and easy refactoring","tags":"imports, architecture, design-system"},"isInternal":false,"tokens":337,"sizeBytes":1418},{"name":"js-hoist-intl.md","path":"skills/react-native-skills/rules/js-hoist-intl.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/js-hoist-intl.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fjs-hoist-intl.md -->","frontmatter":{"title":"Hoist Intl Formatter Creation","impact":"LOW-MEDIUM","impactDescription":"avoids expensive object recreation","tags":"javascript, intl, optimization, memoization"},"isInternal":false,"tokens":386,"sizeBytes":1645},{"name":"list-performance-callbacks.md","path":"skills/react-native-skills/rules/list-performance-callbacks.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/list-performance-callbacks.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-callbacks.md -->","frontmatter":{"title":"Hoist callbacks to the root of lists","impact":"MEDIUM","impactDescription":"Fewer re-renders and faster lists","tags":"tag1, tag2"},"isInternal":false,"tokens":259,"sizeBytes":1059},{"name":"list-performance-function-references.md","path":"skills/react-native-skills/rules/list-performance-function-references.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/list-performance-function-references.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-function-references.md -->","frontmatter":{"title":"Optimize List Performance with Stable Object References","impact":"CRITICAL","impactDescription":"virtualization relies on reference stability","tags":"lists, performance, flatlist, virtualization"},"isInternal":false,"tokens":1006,"sizeBytes":4133},{"name":"list-performance-images.md","path":"skills/react-native-skills/rules/list-performance-images.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/list-performance-images.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-images.md -->","frontmatter":{"title":"Use Compressed Images in Lists","impact":"HIGH","impactDescription":"faster load times, less memory","tags":"lists, images, performance, optimization"},"isInternal":false,"tokens":341,"sizeBytes":1417},{"name":"list-performance-inline-objects.md","path":"skills/react-native-skills/rules/list-performance-inline-objects.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/list-performance-inline-objects.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-inline-objects.md -->","frontmatter":{"title":"Avoid Inline Objects in renderItem","impact":"HIGH","impactDescription":"prevents unnecessary re-renders of memoized list items","tags":"lists, performance, flatlist, virtualization, memo"},"isInternal":false,"tokens":556,"sizeBytes":2308},{"name":"list-performance-item-expensive.md","path":"skills/react-native-skills/rules/list-performance-item-expensive.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/list-performance-item-expensive.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-item-expensive.md -->","frontmatter":{"title":"Keep List Items Lightweight","impact":"HIGH","impactDescription":"reduces render time for visible items during scroll","tags":"lists, performance, virtualization, hooks"},"isInternal":false,"tokens":586,"sizeBytes":2466},{"name":"list-performance-item-memo.md","path":"skills/react-native-skills/rules/list-performance-item-memo.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/list-performance-item-memo.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-item-memo.md -->","frontmatter":{"title":"Pass Primitives to List Items for Memoization","impact":"HIGH","impactDescription":"enables effective memo() comparison","tags":"lists, performance, memo, primitives"},"isInternal":false,"tokens":557,"sizeBytes":2250},{"name":"list-performance-item-types.md","path":"skills/react-native-skills/rules/list-performance-item-types.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/list-performance-item-types.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-item-types.md -->","frontmatter":{"title":"Use Item Types for Heterogeneous Lists","impact":"HIGH","impactDescription":"efficient recycling, less layout thrashing","tags":"list, performance, recycling, heterogeneous, LegendList"},"isInternal":false,"tokens":678,"sizeBytes":2749},{"name":"list-performance-virtualize.md","path":"skills/react-native-skills/rules/list-performance-virtualize.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/list-performance-virtualize.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Flist-performance-virtualize.md -->","frontmatter":{"title":"Use a List Virtualizer for Any List","impact":"HIGH","impactDescription":"reduced memory, faster mounts","tags":"lists, performance, virtualization, scrollview"},"isInternal":false,"tokens":416,"sizeBytes":1742},{"name":"monorepo-native-deps-in-app.md","path":"skills/react-native-skills/rules/monorepo-native-deps-in-app.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/monorepo-native-deps-in-app.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fmonorepo-native-deps-in-app.md -->","frontmatter":{"title":"Install Native Dependencies in App Directory","impact":"CRITICAL","impactDescription":"required for autolinking to work","tags":"monorepo, native, autolinking, installation"},"isInternal":false,"tokens":259,"sizeBytes":1091},{"name":"monorepo-single-dependency-versions.md","path":"skills/react-native-skills/rules/monorepo-single-dependency-versions.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/monorepo-single-dependency-versions.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fmonorepo-single-dependency-versions.md -->","frontmatter":{"title":"Use Single Dependency Versions Across Monorepo","impact":"MEDIUM","impactDescription":"avoids duplicate bundles, version conflicts","tags":"monorepo, dependencies, installation"},"isInternal":false,"tokens":329,"sizeBytes":1351},{"name":"navigation-native-navigators.md","path":"skills/react-native-skills/rules/navigation-native-navigators.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/navigation-native-navigators.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fnavigation-native-navigators.md -->","frontmatter":{"title":"Use Native Navigators for Navigation","impact":"HIGH","impactDescription":"native performance, platform-appropriate UI","tags":"navigation, react-navigation, expo-router, native-stack, tabs"},"isInternal":false,"tokens":1115,"sizeBytes":4941},{"name":"react-compiler-destructure-functions.md","path":"skills/react-native-skills/rules/react-compiler-destructure-functions.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/react-compiler-destructure-functions.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-compiler-destructure-functions.md -->","frontmatter":{"title":"Destructure Functions Early in Render (React Compiler)","impact":"HIGH","impactDescription":"stable references, fewer re-renders","tags":"rerender, hooks, performance, react-compiler"},"isInternal":false,"tokens":295,"sizeBytes":1274},{"name":"react-compiler-reanimated-shared-values.md","path":"skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-compiler-reanimated-shared-values.md -->","frontmatter":{"title":"Use .get() and .set() for Reanimated Shared Values (not .value)","impact":"LOW","impactDescription":"required for React Compiler compatibility","tags":"reanimated, react-compiler, shared-values"},"isInternal":false,"tokens":303,"sizeBytes":1268},{"name":"react-state-dispatcher.md","path":"skills/react-native-skills/rules/react-state-dispatcher.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/react-state-dispatcher.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-state-dispatcher.md -->","frontmatter":{"title":"useState Dispatch updaters for State That Depends on Current Value","impact":"MEDIUM","impactDescription":"avoids stale closures, prevents unnecessary re-renders","tags":"state, hooks, useState, callbacks"},"isInternal":false,"tokens":552,"sizeBytes":2255},{"name":"react-state-fallback.md","path":"skills/react-native-skills/rules/react-state-fallback.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/react-state-fallback.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-state-fallback.md -->","frontmatter":{"title":"Use fallback state instead of initialState","impact":"MEDIUM","impactDescription":"reactive fallbacks without syncing","tags":"state, hooks, derived-state, props, initialState"},"isInternal":false,"tokens":390,"sizeBytes":1735},{"name":"react-state-minimize.md","path":"skills/react-native-skills/rules/react-state-minimize.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/react-state-minimize.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Freact-state-minimize.md -->","frontmatter":{"title":"Minimize State Variables and Derive Values","impact":"MEDIUM","impactDescription":"fewer re-renders, less state drift","tags":"state, derived-state, hooks, optimization"},"isInternal":false,"tokens":434,"sizeBytes":1740},{"name":"rendering-no-falsy-and.md","path":"skills/react-native-skills/rules/rendering-no-falsy-and.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/rendering-no-falsy-and.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Frendering-no-falsy-and.md -->","frontmatter":{"title":"Never Use && with Potentially Falsy Values","impact":"CRITICAL","impactDescription":"prevents production crash","tags":"rendering, conditional, jsx, crash"},"isInternal":false,"tokens":487,"sizeBytes":1812},{"name":"rendering-text-in-text-component.md","path":"skills/react-native-skills/rules/rendering-text-in-text-component.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/rendering-text-in-text-component.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Frendering-text-in-text-component.md -->","frontmatter":{"title":"Wrap Strings in Text Components","impact":"CRITICAL","impactDescription":"prevents runtime crash","tags":"rendering, text, core"},"isInternal":false,"tokens":180,"sizeBytes":697},{"name":"scroll-position-no-state.md","path":"skills/react-native-skills/rules/scroll-position-no-state.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/scroll-position-no-state.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fscroll-position-no-state.md -->","frontmatter":{"title":"Never Track Scroll Position in useState","impact":"HIGH","impactDescription":"prevents render thrashing during scroll","tags":"scroll, performance, reanimated, useRef"},"isInternal":false,"tokens":470,"sizeBytes":1936},{"name":"state-ground-truth.md","path":"skills/react-native-skills/rules/state-ground-truth.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/state-ground-truth.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fstate-ground-truth.md -->","frontmatter":{"title":"State Must Represent Ground Truth","impact":"HIGH","impactDescription":"cleaner logic, easier debugging, single source of truth","tags":"state, derived-state, reanimated, hooks"},"isInternal":false,"tokens":550,"sizeBytes":2172},{"name":"ui-expo-image.md","path":"skills/react-native-skills/rules/ui-expo-image.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/ui-expo-image.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-expo-image.md -->","frontmatter":{"title":"Use expo-image for Optimized Images","impact":"HIGH","impactDescription":"memory efficiency, caching, blurhash placeholders, progressive loading","tags":"images, performance, expo-image, ui"},"isInternal":false,"tokens":436,"sizeBytes":1648},{"name":"ui-image-gallery.md","path":"skills/react-native-skills/rules/ui-image-gallery.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/ui-image-gallery.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-image-gallery.md -->","frontmatter":{"title":"Use Galeria for Image Galleries and Lightbox","impact":"MEDIUM","impactDescription":"native shared element transitions, pinch-to-zoom, pan-to-close","tags":"images, gallery, lightbox, expo-image, ui"},"isInternal":false,"tokens":683,"sizeBytes":2546},{"name":"ui-measure-views.md","path":"skills/react-native-skills/rules/ui-measure-views.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/ui-measure-views.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-measure-views.md -->","frontmatter":{"title":"Measuring View Dimensions","impact":"MEDIUM","impactDescription":"synchronous measurement, avoid unnecessary re-renders","tags":"layout, measurement, onLayout, useLayoutEffect"},"isInternal":false,"tokens":552,"sizeBytes":2232},{"name":"ui-menus.md","path":"skills/react-native-skills/rules/ui-menus.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/ui-menus.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-menus.md -->","frontmatter":{"title":"Use Native Menus for Dropdowns and Context Menus","impact":"HIGH","impactDescription":"native accessibility, platform-consistent UX","tags":"user-interface, menus, context-menus, zeego, accessibility"},"isInternal":false,"tokens":1030,"sizeBytes":4598},{"name":"ui-native-modals.md","path":"skills/react-native-skills/rules/ui-native-modals.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/ui-native-modals.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-native-modals.md -->","frontmatter":{"title":"Use Native Modals Over JS-Based Bottom Sheets","impact":"HIGH","impactDescription":"native performance, gestures, accessibility","tags":"modals, bottom-sheet, native, react-navigation"},"isInternal":false,"tokens":443,"sizeBytes":1859},{"name":"ui-pressable.md","path":"skills/react-native-skills/rules/ui-pressable.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/ui-pressable.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-pressable.md -->","frontmatter":{"title":"Use Pressable Instead of Touchable Components","impact":"LOW","impactDescription":"modern API, more flexible","tags":"ui, pressable, touchable, gestures"},"isInternal":false,"tokens":370,"sizeBytes":1534},{"name":"ui-safe-area-scroll.md","path":"skills/react-native-skills/rules/ui-safe-area-scroll.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/ui-safe-area-scroll.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-safe-area-scroll.md -->","frontmatter":{"title":"Use contentInsetAdjustmentBehavior for Safe Areas","impact":"MEDIUM","impactDescription":"native safe area handling, no layout shifts","tags":"safe-area, scrollview, layout"},"isInternal":false,"tokens":358,"sizeBytes":1579},{"name":"ui-scrollview-content-inset.md","path":"skills/react-native-skills/rules/ui-scrollview-content-inset.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/ui-scrollview-content-inset.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-scrollview-content-inset.md -->","frontmatter":{"title":"Use contentInset for Dynamic ScrollView Spacing","impact":"LOW","impactDescription":"smoother updates, no layout recalculation","tags":"scrollview, layout, contentInset, performance"},"isInternal":false,"tokens":280,"sizeBytes":1317},{"name":"ui-styling.md","path":"skills/react-native-skills/rules/ui-styling.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/rules/ui-styling.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2Frules%2Fui-styling.md -->","frontmatter":{"title":"Modern React Native Styling Patterns","impact":"MEDIUM","impactDescription":"consistent design, smoother borders, cleaner layouts","tags":"styling, css, layout, shadows, gradients"},"isInternal":false,"tokens":565,"sizeBytes":2212},{"name":"SKILL.md","path":"skills/react-native-skills/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-native-skills/SKILL.md","title":"vercel-react-native-skills","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-native-skills%2FSKILL.md -->","frontmatter":{"name":"vercel-react-native-skills","description":"React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.","license":"MIT","metadata":{"author":"vercel","version":"1.0.0"}},"isInternal":false,"tokens":1041,"sizeBytes":4439},{"name":"AGENTS.md","path":"skills/react-view-transitions/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-view-transitions/AGENTS.md","title":"React-view-transitions Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2FAGENTS.md -->","isInternal":false,"tokens":11667,"sizeBytes":50216},{"name":"metadata.json","path":"skills/react-view-transitions/metadata.json","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-view-transitions/metadata.json","title":"React-view-transitions Skill","category":"anthropic-skill","format":"json","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Fmetadata.json -->","frontmatter":{"version":"1.0.0","organization":"Vercel Engineering","date":"March 2026","abstract":"Guide for implementing smooth, native-feeling animations using React's View Transition API. Covers the <ViewTransition> component, addTransitionType, CSS view transition pseudo-elements, shared element transitions, JavaScript animations via Web Animations API, and Next.js integration including the transitionTypes prop on next/link. Includes ready-to-use CSS animation recipes and real-world patterns from production Next.js apps.","references":["https://react.dev/reference/react/ViewTransition","https://react.dev/reference/react/addTransitionType","https://nextjs.org/docs/app/api-reference/config/next-config-js/viewTransition","https://github.com/vercel/next-app-router-playground/tree/main/app/view-transitions"]},"isInternal":false,"tokens":186,"sizeBytes":852},{"name":"README.md","path":"skills/react-view-transitions/README.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-view-transitions/README.md","title":"React-view-transitions Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2FREADME.md -->","isInternal":false,"tokens":478,"sizeBytes":2231},{"name":"css-recipes.md","path":"skills/react-view-transitions/references/css-recipes.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-view-transitions/references/css-recipes.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fcss-recipes.md -->","isInternal":false,"tokens":2076,"sizeBytes":8337},{"name":"implementation.md","path":"skills/react-view-transitions/references/implementation.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-view-transitions/references/implementation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fimplementation.md -->","isInternal":false,"tokens":2122,"sizeBytes":9280},{"name":"nextjs.md","path":"skills/react-view-transitions/references/nextjs.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-view-transitions/references/nextjs.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fnextjs.md -->","isInternal":false,"tokens":2446,"sizeBytes":10700},{"name":"patterns.md","path":"skills/react-view-transitions/references/patterns.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-view-transitions/references/patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Fpatterns.md -->","isInternal":false,"tokens":2762,"sizeBytes":11941},{"name":"troubleshooting.md","path":"skills/react-view-transitions/references/troubleshooting.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-view-transitions/references/troubleshooting.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2Freferences%2Ftroubleshooting.md -->","isInternal":false,"tokens":1062,"sizeBytes":4940},{"name":"SKILL.md","path":"skills/react-view-transitions/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/react-view-transitions/SKILL.md","title":"vercel-react-view-transitions","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Freact-view-transitions%2FSKILL.md -->","frontmatter":{"name":"vercel-react-view-transitions","description":"Guide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions view transitions, `startViewTransition`, `ViewTransition`, transition types, or asks about animating between UI states in React without third-party animation libraries.","license":"MIT","metadata":{"author":"vercel","version":"1.0.0"}},"isInternal":false,"tokens":3313,"sizeBytes":14407},{"name":"SKILL.md","path":"skills/vercel-cli-with-tokens/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-cli-with-tokens/SKILL.md","title":"vercel-cli-with-tokens","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-cli-with-tokens%2FSKILL.md -->","frontmatter":{"name":"vercel-cli-with-tokens","description":"Deploy and manage projects on Vercel using token-based authentication. Use when working with Vercel CLI using access tokens rather than interactive login — e.g. \"deploy to vercel\", \"set up vercel\", \"add environment variables to vercel\".","metadata":{"author":"vercel","version":"1.0.0"}},"isInternal":false,"tokens":2692,"sizeBytes":10476},{"name":"AGENTS.md","path":"skills/vercel-optimize/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/AGENTS.md","title":"Vercel-optimize Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FAGENTS.md -->","isInternal":false,"tokens":466,"sizeBytes":1964},{"name":"CONTRIBUTING.md","path":"skills/vercel-optimize/CONTRIBUTING.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/CONTRIBUTING.md","title":"Vercel-optimize Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FCONTRIBUTING.md -->","isInternal":false,"tokens":550,"sizeBytes":2224},{"name":"metadata.json","path":"skills/vercel-optimize/metadata.json","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/metadata.json","title":"Vercel-optimize Skill","category":"anthropic-skill","format":"json","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Fmetadata.json -->","frontmatter":{"version":"1.2.0","organization":"Vercel Engineering","date":"May 2026","abstract":"Deep cost and performance optimization for supported Vercel projects. Pulls observability metrics, billing, and project config from the Vercel CLI, then investigates the codebase only where those metrics point. Produces ranked, verified recommendations with before/after code grounded in version-aware documentation citations. Avoids precise dollar projections (cost framed as order-of-magnitude) and avoids invented documentation URLs (curated allow-list).","references":["https://vercel.com/docs/cli/metrics","https://vercel.com/docs/cli/usage","https://vercel.com/docs/cli/api","https://vercel.com/docs/observability/observability-plus","https://nextjs.org/docs/app/building-your-application/caching","https://vercel.com/docs/frameworks/full-stack/sveltekit"]},"isInternal":false,"tokens":213,"sizeBytes":903},{"name":"README.md","path":"skills/vercel-optimize/README.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/README.md","title":"Vercel-optimize Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FREADME.md -->","isInternal":false,"tokens":968,"sizeBytes":4471},{"name":"candidates.md","path":"skills/vercel-optimize/references/candidates.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/candidates.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fcandidates.md -->","isInternal":false,"tokens":1999,"sizeBytes":8510},{"name":"data-collection.md","path":"skills/vercel-optimize/references/data-collection.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/data-collection.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fdata-collection.md -->","isInternal":false,"tokens":5304,"sizeBytes":20925},{"name":"docs-library.json","path":"skills/vercel-optimize/references/docs-library.json","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/docs-library.json","title":"References Skill","category":"anthropic-skill","format":"json","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fdocs-library.json -->","frontmatter":{"$schema":"Curated documentation allow-list for the vercel-optimize skill. Entries are version-aware via applicableFrameworks (semver). The recommender prompt receives only the subset valid for the user's stack. Citations outside this allow-list are stripped by the unknown-citation sanitizer.","version":"1.1.1","lastVerified":"2026-05-26","schemaVersion":"1.0","applicableFrameworksSyntax":"Semver-style. '*' = any framework. 'next@*' = any Next.js. 'next@14' = Next 14.x. 'next@>=15.0.0' = Next 15+. Multiple via '||': 'next@14 || next@>=15.0.0'.","urls":[{"url":"https://vercel.com/docs/caching/cdn-cache","topic":"Vercel CDN Cache — cacheable response criteria, static file caching, dynamic response caching, and cache limits","appliesTo":["uncached_route","cache_header_gap"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/headers/request-headers","topic":"Vercel request headers — documented geolocation header names including country, country-region, city, latitude, longitude, timezone, and postal code","appliesTo":["uncached_route","cache_header_gap"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/caching/cache-control-headers","topic":"Cache-Control headers on Vercel — s-maxage, CDN-Cache-Control, Vercel-CDN-Cache-Control, stale-while-revalidate","appliesTo":["uncached_route","cache_header_gap"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/caching/runtime-cache","topic":"Runtime Cache — reuse repeated API, database, and expensive computation results from Vercel Functions","appliesTo":["slow_route","external_api_slow"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/fluid-compute","topic":"Fluid Compute — reduced cold starts, active CPU pricing, function instance reuse, memory right-sizing","appliesTo":["cold_start","platform_fluid_compute","slow_route","oversized_memory"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/functions/configuring-functions/memory","topic":"Function memory configuration — Standard 2GB / Performance 4GB tiers, when to upgrade Performance, account-level setting","appliesTo":["platform_fluid_compute"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/speed-insights","topic":"Vercel Speed Insights — LCP, INP, CLS, TTFB, FCP, FID on real-user traffic","appliesTo":["cwv_poor"],"applicableFrameworks":["*"]},{"url":"https://web.dev/articles/vitals","topic":"Core Web Vitals — LCP < 2500ms, INP < 200ms, CLS < 0.1 thresholds","appliesTo":["cwv_poor"],"applicableFrameworks":["*"]},{"url":"https://web.dev/articles/optimize-lcp","topic":"Optimize LCP — preload critical images, defer non-critical CSS, reduce server response","appliesTo":["cwv_poor"],"applicableFrameworks":["*"]},{"url":"https://web.dev/articles/optimize-inp","topic":"Optimize INP — break up long tasks, defer non-essential JavaScript","appliesTo":["cwv_poor"],"applicableFrameworks":["*"]},{"url":"https://web.dev/articles/optimize-cls","topic":"Optimize CLS — reserve space for images, fonts, ads; avoid layout-affecting injections","appliesTo":["cwv_poor"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/vercel-firewall/vercel-waf/managed-rulesets","topic":"Vercel WAF managed rulesets — managed bot and AI-bot actions by ruleset","appliesTo":["platform_bot_protection","uncached_route"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/vercel-firewall/vercel-waf/custom-rules","topic":"Vercel WAF custom rules — log, deny, challenge, bypass, redirect, and persistent actions","appliesTo":["platform_bot_protection","uncached_route","middleware_heavy"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/functions","topic":"Vercel Functions lifecycle — regions, memory, max duration, runtimes","appliesTo":["slow_route","cold_start","route_errors"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/functions/debug-slow-functions","topic":"Debugging slow functions — isolate external latency, cold starts, bundle size, initialization, memory, and connection pooling","appliesTo":["slow_route","cold_start","external_api_slow"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package","topic":"@vercel/functions helpers — waitUntil for post-response work and attachDatabasePool for pool lifecycle","appliesTo":["slow_route","cold_start","external_api_slow","route_errors"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/functions/limitations","topic":"Vercel Functions limits — duration, payload, runtime, and invocation constraints","appliesTo":["cold_start","route_errors","slow_route"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/functions/runtimes","topic":"Runtime selection — node, python, ruby, go (edge runtime deprecated per 2026 platform notes)","appliesTo":["cold_start","slow_route"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/incremental-static-regeneration","topic":"Incremental Static Regeneration — reads vs writes, revalidate timing, on-demand revalidation","appliesTo":["isr_overrevalidation","uncached_route"],"applicableFrameworks":["next@*","sveltekit@*","astro@*","nuxt@*"]},{"url":"https://vercel.com/docs/image-optimization","topic":"Image Optimization on Vercel — supported formats, transformations, billing","appliesTo":["image_optimization"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/image-optimization/managing-image-optimization-costs","topic":"Managing Image Optimization usage and costs — reduce unnecessary transformations and cache misses","appliesTo":["image_optimization"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/image-optimization/limits-and-pricing","topic":"Image Optimization limits and pricing — source format, dimensions, transformed image size, and billing dimensions","appliesTo":["image_optimization"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/observability/observability-plus","topic":"Observability Plus — path-level metrics, p75 sort, and retention","appliesTo":["platform_fluid_compute","observability_events_attribution"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/monorepos","topic":"Monorepos — skip unaffected projects and ignored build step tradeoffs","appliesTo":["build_minutes_fanout"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/builds","topic":"Builds — build infrastructure, monorepo build controls, concurrency, and queues","appliesTo":["build_minutes_fanout"],"applicableFrameworks":["*"]},{"url":"https://turborepo.dev/docs/crafting-your-repository/caching","topic":"Turborepo caching — task-level cache config, force flags, remote cache","appliesTo":["build_minutes_fanout"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/regions","topic":"Vercel Functions regions — single vs multi-region, defaults, latency tradeoffs","appliesTo":["region_misconfig"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/functions/configuring-functions/region","topic":"Configuring function region — vercel.json regions, segment-level preferredRegion","appliesTo":["region_misconfig"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/alerts","topic":"Alerts — per-metric alerting on routes/status/usage with supported destinations","appliesTo":["observability_events_attribution","usage_spike_triage"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/spend-management","topic":"Spend Management — usage thresholds, notifications, and project pause behavior","appliesTo":["usage_spike_triage"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/cli/metrics","topic":"vercel metrics CLI — schema, query construction, OData filters","appliesTo":[],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/cli/usage","topic":"vercel usage CLI — billing line items, breakdown, effectiveCost vs billedCost","appliesTo":[],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/cli/inspect","topic":"vercel inspect — view a deployment's build logs, region, runtime, commit, creator","appliesTo":["route_errors"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/manage-cdn-usage","topic":"Vercel Edge Network bandwidth — Fast Data Transfer, included quotas, overage pricing","appliesTo":["large_static_asset","uncached_route"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/functions/runtimes/edge-runtime","topic":"Vercel Edge Runtime — supported APIs, constraints, incompatible modules (node:* builtins, native deps)","appliesTo":["edge_heavy_import","cold_start"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/bot-management","topic":"Vercel Bot Protection — enabling, false-positive handling, billing implications","appliesTo":["platform_bot_protection"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/botid","topic":"BotID — invisible challenge, Basic and Deep Analysis modes, checkBotId, observability, and billing","appliesTo":["platform_bot_protection"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/functions/limitations","topic":"Platform limits — function payload size, max duration, regional caps","appliesTo":["route_errors","slow_route"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/project-configuration","topic":"vercel.json configuration — functions, crons, rewrites, redirects, headers, regions","appliesTo":["uncached_route","cache_header_gap"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/routing/","topic":"Vercel routing — firewall, bulk redirects, project routes, deployment routes, middleware, and filesystem order","appliesTo":["uncached_route","middleware_heavy","cache_header_gap"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/redirects/bulk-redirects/","topic":"Bulk Redirects — framework-agnostic large redirect tables processed before project routes","appliesTo":["uncached_route"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/routing-middleware","topic":"Vercel Routing Middleware — intercept requests before rendering and keep matcher scope intentional","appliesTo":["middleware_heavy"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/workflow","topic":"Vercel Workflow — beta durable workflows, observability, Workflow Steps/Storage pricing, and compute billing caveats","appliesTo":["route_errors","slow_route"],"applicableFrameworks":["*"]},{"url":"https://workflow-sdk.dev/docs/foundations/starting-workflows","topic":"Workflow SDK starting workflows — start() returns after enqueueing, returnValue waits for completion, streams/status can be read later","appliesTo":["route_errors","slow_route"],"applicableFrameworks":["*"]},{"url":"https://workflow-sdk.dev/docs/foundations/workflows-and-steps","topic":"Workflow SDK workflows and steps — workflow orchestration, step execution, persisted results, retries, sleep, and suspension","appliesTo":["route_errors","slow_route"],"applicableFrameworks":["*"]},{"url":"https://workflow-sdk.dev/docs/foundations/streaming","topic":"Workflow SDK streaming — durable streams, getReadable startIndex, getWritable, lock release, stream close, and retry caveats","appliesTo":["slow_route"],"applicableFrameworks":["*"]},{"url":"https://workflow-sdk.dev/docs/ai/resumable-streams","topic":"Workflow SDK resumable streams — reconnecting AI chat streams with run IDs, stream startIndex, and tail-index headers","appliesTo":["slow_route"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/queues","topic":"Vercel Queues — asynchronous task queues for background work decoupled from the request path","appliesTo":["route_errors"],"applicableFrameworks":["*"]},{"url":"https://vercel.com/docs/concepts/edge-network/regions","topic":"Vercel function regions — single-region default, multi-region, db proximity","appliesTo":["slow_route"],"applicableFrameworks":["*"]},{"url":"https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config","topic":"App Router route segment config — dynamic, revalidate, runtime, fetchCache, dynamicParams","appliesTo":["uncached_route","rendering_candidate","force-dynamic"],"applicableFrameworks":["next@>=13.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/file-conventions/route","topic":"Route Handlers — GET handler caching defaults, route segment config, and request handling","appliesTo":["uncached_route","cache_header_gap"],"applicableFrameworks":["next@>=13.2.0"]},{"url":"https://nextjs.org/docs/app/api-reference/functions/image-response","topic":"ImageResponse — dynamic OG image response options including status and headers","appliesTo":["uncached_route","cache_header_gap"],"applicableFrameworks":["next@>=13.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/functions/generate-static-params","topic":"generateStaticParams — pre-render dynamic routes at build time","appliesTo":["uncached_route","rendering_candidate"],"applicableFrameworks":["next@>=13.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/functions/fetch","topic":"Next.js fetch — server-side cache and next.revalidate semantics for App Router data requests","appliesTo":["uncached_route","isr_overrevalidation","cache_header_gap"],"applicableFrameworks":["next@>=13.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/functions/unstable_cache","topic":"unstable_cache — Next.js 14/15 cache primitive (replaced in 16 by 'use cache')","appliesTo":["uncached_route","function-duration"],"applicableFrameworks":["next@14 || next@15"]},{"url":"https://nextjs.org/docs/app/api-reference/directives/use-cache","topic":"'use cache' directive — opt-in persistent cache (15+)","appliesTo":["uncached_route","function-duration"],"applicableFrameworks":["next@>=15.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/directives/use-cache-remote","topic":"'use cache: remote' directive — remote cache storage for shared cached data with Cache Components","appliesTo":["external_api_slow"],"applicableFrameworks":["next@>=16.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/functions/cacheLife","topic":"cacheLife() — TTL annotation for 'use cache' segments","appliesTo":["uncached_route","isr_overrevalidation"],"applicableFrameworks":["next@>=15.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/functions/cacheTag","topic":"cacheTag() — tag-based invalidation for 'use cache'","appliesTo":["uncached_route","isr_overrevalidation"],"applicableFrameworks":["next@>=15.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/functions/revalidateTag","topic":"revalidateTag — on-demand invalidation by tag","appliesTo":["isr_overrevalidation"],"applicableFrameworks":["next@>=13.4.0"]},{"url":"https://nextjs.org/docs/app/api-reference/functions/revalidatePath","topic":"revalidatePath — on-demand invalidation by route","appliesTo":["isr_overrevalidation"],"applicableFrameworks":["next@>=13.4.0"]},{"url":"https://nextjs.org/docs/app/api-reference/functions/after","topic":"after() — non-blocking work post-response (15+)","appliesTo":["function-duration","slow_route"],"applicableFrameworks":["next@>=15.0.0"]},{"url":"https://react.dev/reference/react/cache","topic":"React cache() — per-request memoization in Server Components","appliesTo":["function-duration","slow_route"],"applicableFrameworks":["next@>=13.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/components/image","topic":"next/image component — formats, sizes, priority, lazy loading","appliesTo":["image_optimization","cwv_poor"],"applicableFrameworks":["next@*"]},{"url":"https://nextjs.org/docs/app/api-reference/components/font","topic":"next/font — self-hosted fonts, no CLS, build-time optimization","appliesTo":["bundle_candidate","cwv_poor"],"applicableFrameworks":["next@>=13.2.0"]},{"url":"https://nextjs.org/docs/app/api-reference/components/script","topic":"next/script component — loading strategies for third-party scripts and App Router worker caveats","appliesTo":["cwv_poor"],"applicableFrameworks":["next@*"]},{"url":"https://nextjs.org/docs/app/guides/lazy-loading","topic":"next/dynamic — lazy-load heavy client components, SSR opt-out","appliesTo":["bundle_candidate","slow_route","cwv_poor"],"applicableFrameworks":["next@*"]},{"url":"https://nextjs.org/docs/app/building-your-application/caching","topic":"Next.js App Router caching model — full-route cache, data cache, request memoization","appliesTo":["uncached_route","isr_overrevalidation","function-duration"],"applicableFrameworks":["next@>=13.0.0"]},{"url":"https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents","topic":"cacheComponents flag — explicit caching model for App Router prerendering and data access","appliesTo":["rendering_candidate","uncached_route"],"applicableFrameworks":["next@>=16.0.0"]},{"url":"https://nextjs.org/docs/app/getting-started/caching","topic":"Cache Components caching guide — static shell, cached dynamic content, runtime dynamic content","appliesTo":["rendering_candidate","uncached_route"],"applicableFrameworks":["next@>=16.0.0"]},{"url":"https://nextjs.org/docs/app/guides/migrating-to-cache-components","topic":"Migrating to Cache Components — route segment config replacements and static shell migration","appliesTo":["rendering_candidate","uncached_route"],"applicableFrameworks":["next@>=16.0.0"]},{"url":"https://nextjs.org/docs/app/building-your-application/routing/middleware","topic":"Next.js middleware — matcher config, runtime, request modification","appliesTo":["middleware-broad-matcher","platform_bot_protection","middleware_heavy"],"applicableFrameworks":["next@>=12.0.0"]},{"url":"https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering","topic":"Partial Prerendering (PPR) — combine static shell + dynamic streaming (15+ experimental, stable later)","appliesTo":["rendering_candidate","slow_route"],"applicableFrameworks":["next@>=15.0.0"]},{"url":"https://nextjs.org/docs/app/building-your-application/optimizing/bundle-analyzer","topic":"@next/bundle-analyzer — diagnose oversized client bundles","appliesTo":["bundle_candidate"],"applicableFrameworks":["next@*"]},{"url":"https://vercel.com/docs/frameworks/full-stack/sveltekit","topic":"SvelteKit on Vercel — adapter config, ISR, split function bundling, regions, and runtime settings","appliesTo":["uncached_route","isr_overrevalidation","cold_start","slow_route"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://svelte.dev/docs/kit/adapter-vercel","topic":"SvelteKit adapter-vercel — ISR, split, regions, max duration, and image config","appliesTo":["uncached_route","isr_overrevalidation","cold_start","slow_route"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://svelte.dev/docs/kit/page-options","topic":"SvelteKit page options — prerender, SSR, CSR, and server route prerendering","appliesTo":["uncached_route","isr_overrevalidation","rendering_candidate"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://kit.svelte.dev/docs/adapter-vercel","topic":"SvelteKit Vercel adapter — runtime, isr, split, regions, maxDuration config","appliesTo":["slow_route","cold_start","uncached_route"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://kit.svelte.dev/docs/page-options","topic":"SvelteKit page options — prerender, ssr, csr","appliesTo":["rendering_candidate","uncached_route"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://kit.svelte.dev/docs/load","topic":"SvelteKit load functions — server-side data loading patterns, parallel loads","appliesTo":["slow_route","function-duration"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://kit.svelte.dev/docs/routing","topic":"SvelteKit routing — +page.svelte, +page.server.ts, +server.ts route handlers, +layout.svelte","appliesTo":["slow_route","uncached_route","rendering_candidate"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://kit.svelte.dev/docs/hooks","topic":"SvelteKit hooks — handle (request middleware), handleFetch (per-fetch interception), handleError","appliesTo":["middleware_heavy","uncached_route","route_errors"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://kit.svelte.dev/docs/form-actions","topic":"SvelteKit form actions — server-side form handling without separate API endpoints","appliesTo":["slow_route","uncached_route"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://kit.svelte.dev/docs/state-management","topic":"SvelteKit state management — request-scoped state, locals, avoiding cross-request leakage","appliesTo":["slow_route","route_errors"],"applicableFrameworks":["sveltekit@*"]},{"url":"https://kit.svelte.dev/docs/cli","topic":"SvelteKit CLI — build, preview, sync. Use `vite build` for production output that adapter-vercel consumes.","appliesTo":[],"applicableFrameworks":["sveltekit@*"]},{"url":"https://kit.svelte.dev/docs/migrating","topic":"SvelteKit migration guides — for users on older majors who may not have ISR / per-route prerender","appliesTo":[],"applicableFrameworks":["sveltekit@*"]},{"url":"https://docs.astro.build/en/guides/integrations-guide/vercel/","topic":"Astro Vercel adapter — ISR, image services, edge middleware, runtime config","appliesTo":["slow_route","cold_start","uncached_route","isr_overrevalidation","rendering_candidate","middleware_heavy"],"applicableFrameworks":["astro@*"]},{"url":"https://vercel.com/docs/frameworks/frontend/astro","topic":"Astro on Vercel — output modes, ISR, edge middleware, and image optimization","appliesTo":["uncached_route","isr_overrevalidation","rendering_candidate","middleware_heavy","image_optimization"],"applicableFrameworks":["astro@*"]},{"url":"https://docs.astro.build/en/guides/on-demand-rendering/","topic":"Astro on-demand rendering — output server mode and per-route prerender controls","appliesTo":["uncached_route","isr_overrevalidation","rendering_candidate"],"applicableFrameworks":["astro@*"]},{"url":"https://docs.astro.build/en/reference/configuration-reference/","topic":"Astro configuration — output mode (static/server/hybrid), prerender, integrations","appliesTo":["rendering_candidate","uncached_route"],"applicableFrameworks":["astro@*"]},{"url":"https://docs.astro.build/en/guides/server-side-rendering/","topic":"Astro SSR — hybrid mode, prerender export, dynamic routes","appliesTo":["slow_route","uncached_route","rendering_candidate"],"applicableFrameworks":["astro@*"]},{"url":"https://nuxt.com/docs/getting-started/deployment#vercel","topic":"Nuxt on Vercel — deployment, runtime configuration, ISR support","appliesTo":["slow_route","cold_start","uncached_route"],"applicableFrameworks":["nuxt@*"]},{"url":"https://vercel.com/docs/frameworks/full-stack/nuxt","topic":"Nuxt on Vercel — routeRules ISR, Vercel cache integration, deployment, and runtime configuration","appliesTo":["uncached_route","isr_overrevalidation","slow_route","cold_start"],"applicableFrameworks":["nuxt@>=3.0.0"]},{"url":"https://nuxt.com/docs/api/composables/use-fetch","topic":"Nuxt useFetch / useAsyncData — automatic deduplication, parallel data loads, cache options","appliesTo":["slow_route","function-duration"],"applicableFrameworks":["nuxt@*"]},{"url":"https://nuxt.com/docs/4.x/api/utils/define-route-rules","topic":"Nuxt defineRouteRules — per-route prerender, ISR, SWR, headers, and rendering controls","appliesTo":["uncached_route","isr_overrevalidation","rendering_candidate"],"applicableFrameworks":["nuxt@>=3.0.0"]},{"url":"https://nuxt.com/docs/4.x/guide/concepts/rendering","topic":"Nuxt rendering modes — universal rendering, routeRules, prerendering, and hybrid rendering","appliesTo":["uncached_route","isr_overrevalidation","rendering_candidate"],"applicableFrameworks":["nuxt@>=3.0.0"]},{"url":"https://nuxt.com/docs/api/utils/define-route-rules","topic":"Nuxt route rules — per-route SSR/SSG/ISR/swr/cache config in nuxt.config.ts","appliesTo":["uncached_route","isr_overrevalidation","rendering_candidate"],"applicableFrameworks":["nuxt@*"]},{"url":"https://docs.astro.build/en/guides/integrations-guide/vercel/","topic":"Astro Vercel adapter — SSR, ISR, image optimization on Vercel","appliesTo":["slow_route","image_optimization"],"applicableFrameworks":["astro@*"]},{"url":"https://nuxt.com/docs/getting-started/deployment#vercel","topic":"Nuxt on Vercel — Nitro preset, ISR, caching headers","appliesTo":["uncached_route","rendering_candidate"],"applicableFrameworks":["nuxt@*"]}],"ruleSkillRefs":[{"skill":"vercel-react-best-practices","rule":"async-parallel","topic":"Promise.all for independent awaits","applicableFrameworks":["next@*","react@*"]},{"skill":"vercel-react-best-practices","rule":"async-suspense-boundaries","topic":"Suspense for streaming content","applicableFrameworks":["next@>=13.0.0","react@>=18.0.0"]},{"skill":"vercel-react-best-practices","rule":"bundle-barrel-imports","topic":"Avoid barrel files, prefer direct imports","applicableFrameworks":["next@*","react@*"]},{"skill":"vercel-react-best-practices","rule":"bundle-dynamic-imports","topic":"next/dynamic for heavy components","applicableFrameworks":["next@*"]},{"skill":"vercel-react-best-practices","rule":"bundle-defer-third-party","topic":"Defer analytics/logging scripts post-hydration","applicableFrameworks":["next@*","react@*"]},{"skill":"vercel-react-best-practices","rule":"server-cache-react","topic":"React.cache() for request deduplication","applicableFrameworks":["next@>=13.0.0"]},{"skill":"vercel-react-best-practices","rule":"server-cache-lru","topic":"LRU for cross-request caching","applicableFrameworks":["next@*"]},{"skill":"vercel-react-best-practices","rule":"server-after-nonblocking","topic":"after() for non-blocking work post-response","applicableFrameworks":["next@>=15.0.0"]},{"skill":"vercel-react-best-practices","rule":"server-parallel-fetching","topic":"Restructure server components for parallel fetches","applicableFrameworks":["next@>=13.0.0"]},{"skill":"vercel-react-best-practices","rule":"server-hoist-static-io","topic":"Hoist static I/O (fonts, logos) to module level","applicableFrameworks":["next@*"]},{"skill":"vercel-react-best-practices","rule":"client-swr-dedup","topic":"SWR for client-side request deduplication","applicableFrameworks":["next@*","react@*"]},{"skill":"vercel-react-best-practices","rule":"rendering-content-visibility","topic":"content-visibility for long lists","applicableFrameworks":["*"]},{"skill":"vercel-react-best-practices","rule":"rendering-resource-hints","topic":"React DOM resource hints for preloading","applicableFrameworks":["next@*","react@*"]}]},"isInternal":false,"tokens":8124,"sizeBytes":31540},{"name":"doctrine.md","path":"skills/vercel-optimize/references/doctrine.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/doctrine.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fdoctrine.md -->","isInternal":false,"tokens":2116,"sizeBytes":9512},{"name":"observability-plus.md","path":"skills/vercel-optimize/references/observability-plus.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/observability-plus.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fobservability-plus.md -->","isInternal":false,"tokens":1205,"sizeBytes":5329},{"name":"ai-application.md","path":"skills/vercel-optimize/references/playbooks/ai-application.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/playbooks/ai-application.md","title":"Playbooks Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fai-application.md -->","isInternal":false,"tokens":874,"sizeBytes":3933},{"name":"api-service.md","path":"skills/vercel-optimize/references/playbooks/api-service.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/playbooks/api-service.md","title":"Playbooks Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fapi-service.md -->","isInternal":false,"tokens":550,"sizeBytes":2485},{"name":"content-site.md","path":"skills/vercel-optimize/references/playbooks/content-site.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/playbooks/content-site.md","title":"Playbooks Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fcontent-site.md -->","isInternal":false,"tokens":578,"sizeBytes":2502},{"name":"ecommerce.md","path":"skills/vercel-optimize/references/playbooks/ecommerce.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/playbooks/ecommerce.md","title":"Playbooks Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fecommerce.md -->","isInternal":false,"tokens":597,"sizeBytes":2737},{"name":"marketing.md","path":"skills/vercel-optimize/references/playbooks/marketing.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/playbooks/marketing.md","title":"Playbooks Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fmarketing.md -->","isInternal":false,"tokens":590,"sizeBytes":2675},{"name":"README.md","path":"skills/vercel-optimize/references/playbooks/README.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/playbooks/README.md","title":"Playbooks Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2FREADME.md -->","isInternal":false,"tokens":643,"sizeBytes":2812},{"name":"saas.md","path":"skills/vercel-optimize/references/playbooks/saas.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/playbooks/saas.md","title":"Playbooks Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fsaas.md -->","isInternal":false,"tokens":641,"sizeBytes":2896},{"name":"sveltekit.md","path":"skills/vercel-optimize/references/playbooks/sveltekit.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/playbooks/sveltekit.md","title":"Playbooks Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fplaybooks%2Fsveltekit.md -->","isInternal":false,"tokens":1015,"sizeBytes":4019},{"name":"recommendations.md","path":"skills/vercel-optimize/references/recommendations.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/recommendations.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Frecommendations.md -->","isInternal":false,"tokens":2801,"sizeBytes":10788},{"name":"scanner-patterns.md","path":"skills/vercel-optimize/references/scanner-patterns.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/scanner-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fscanner-patterns.md -->","isInternal":false,"tokens":3188,"sizeBytes":13671},{"name":"scoring.md","path":"skills/vercel-optimize/references/scoring.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/scoring.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fscoring.md -->","isInternal":false,"tokens":2930,"sizeBytes":12505},{"name":"astro-edge-middleware-scope.md","path":"skills/vercel-optimize/references/support-topics/astro-edge-middleware-scope.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/astro-edge-middleware-scope.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fastro-edge-middleware-scope.md -->","frontmatter":{"id":"astro-edge-middleware-scope","title":"Astro edge middleware scope","status":"active","candidateKinds":["middleware_heavy"],"frameworks":["astro@*"],"priority":88,"citations":["https://vercel.com/docs/frameworks/frontend/astro","https://docs.astro.build/en/guides/integrations-guide/vercel/"],"maxBriefChars":800},"isInternal":false,"tokens":222,"sizeBytes":1012},{"name":"astro-output-mode-and-isr.md","path":"skills/vercel-optimize/references/support-topics/astro-output-mode-and-isr.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/astro-output-mode-and-isr.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fastro-output-mode-and-isr.md -->","frontmatter":{"id":"astro-output-mode-and-isr","title":"Astro output mode and ISR","status":"active","candidateKinds":["uncached_route","rendering_candidate"],"frameworks":["astro@*"],"priority":90,"citations":["https://vercel.com/docs/frameworks/frontend/astro","https://docs.astro.build/en/guides/on-demand-rendering/","https://docs.astro.build/en/reference/configuration-reference/"],"maxBriefChars":850},"isInternal":false,"tokens":255,"sizeBytes":1135},{"name":"auth-preserving-parallelization.md","path":"skills/vercel-optimize/references/support-topics/auth-preserving-parallelization.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/auth-preserving-parallelization.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fauth-preserving-parallelization.md -->","frontmatter":{"id":"auth-preserving-parallelization","title":"Authorization-preserving parallelization","status":"active","candidateKinds":["slow_route"],"frameworks":["*"],"priority":90,"citations":["vercel-react-best-practices:async-parallel","vercel-react-best-practices:server-parallel-fetching"],"maxBriefChars":900},"isInternal":false,"tokens":246,"sizeBytes":1203},{"name":"bot-protection-product-guardrails.md","path":"skills/vercel-optimize/references/support-topics/bot-protection-product-guardrails.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/bot-protection-product-guardrails.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fbot-protection-product-guardrails.md -->","frontmatter":{"id":"bot-protection-product-guardrails","title":"Bot Protection product guardrails","status":"active","candidateKinds":["platform_bot_protection"],"frameworks":["*"],"priority":90,"citations":["https://vercel.com/docs/bot-management","https://vercel.com/docs/vercel-firewall/vercel-waf/managed-rulesets","https://vercel.com/docs/vercel-firewall/vercel-waf/custom-rules","https://vercel.com/docs/botid"],"maxBriefChars":800},"isInternal":false,"tokens":253,"sizeBytes":1119},{"name":"build-minutes-monorepo-fanout.md","path":"skills/vercel-optimize/references/support-topics/build-minutes-monorepo-fanout.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/build-minutes-monorepo-fanout.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fbuild-minutes-monorepo-fanout.md -->","frontmatter":{"id":"build-minutes-monorepo-fanout","title":"Build Minutes monorepo fanout","status":"active","candidateKinds":["build_minutes_fanout"],"frameworks":["*"],"scannerPatterns":["turbo-force-bypass"],"priority":90,"citations":["https://vercel.com/docs/monorepos","https://vercel.com/docs/builds","https://turborepo.dev/docs/crafting-your-repository/caching"],"maxBriefChars":900},"isInternal":false,"tokens":299,"sizeBytes":1269},{"name":"cache-components-static-shell-boundaries.md","path":"skills/vercel-optimize/references/support-topics/cache-components-static-shell-boundaries.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/cache-components-static-shell-boundaries.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcache-components-static-shell-boundaries.md -->","frontmatter":{"id":"cache-components-static-shell-boundaries","title":"Cache Components static shell boundaries","status":"active","candidateKinds":["rendering_candidate"],"frameworks":["next@>=16.0.0"],"priority":94,"citations":["https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents","https://nextjs.org/docs/app/getting-started/caching","https://nextjs.org/docs/app/guides/migrating-to-cache-components"],"maxBriefChars":900},"isInternal":false,"tokens":261,"sizeBytes":1162},{"name":"cache-components-suspense-dedupe-pitfall.md","path":"skills/vercel-optimize/references/support-topics/cache-components-suspense-dedupe-pitfall.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/cache-components-suspense-dedupe-pitfall.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcache-components-suspense-dedupe-pitfall.md -->","frontmatter":{"id":"cache-components-suspense-dedupe-pitfall","title":"Cache Components Suspense dedupe pitfall","status":"active","candidateKinds":["cache_components_suspense_dedupe"],"frameworks":["next@>=16.0.0"],"scannerPatterns":["cache-components-suspense-dedupe"],"priority":87,"citations":["https://nextjs.org/docs/app/api-reference/directives/use-cache","https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents","https://nextjs.org/docs/app/guides/migrating-to-cache-components"],"maxBriefChars":900},"isInternal":false,"tokens":319,"sizeBytes":1380},{"name":"cdn-cache-auth-safety.md","path":"skills/vercel-optimize/references/support-topics/cdn-cache-auth-safety.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/cdn-cache-auth-safety.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcdn-cache-auth-safety.md -->","frontmatter":{"id":"cdn-cache-auth-safety","title":"CDN cache auth safety","status":"active","candidateKinds":["uncached_route","cache_header_gap"],"frameworks":["*"],"priority":100,"citations":["https://vercel.com/docs/caching/cdn-cache","https://vercel.com/docs/caching/cache-control-headers","https://vercel.com/docs/project-configuration"],"maxBriefChars":900},"isInternal":false,"tokens":255,"sizeBytes":1068},{"name":"cold-start-initialization-bundle.md","path":"skills/vercel-optimize/references/support-topics/cold-start-initialization-bundle.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/cold-start-initialization-bundle.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcold-start-initialization-bundle.md -->","frontmatter":{"id":"cold-start-initialization-bundle","title":"Cold-start initialization and bundle weight","status":"active","candidateKinds":["cold_start"],"frameworks":["*"],"priority":92,"citations":["https://vercel.com/docs/functions/debug-slow-functions","https://vercel.com/docs/functions/limitations","https://vercel.com/docs/functions/runtimes"],"maxBriefChars":850},"isInternal":false,"tokens":250,"sizeBytes":1159},{"name":"core-web-vitals-client-bottlenecks.md","path":"skills/vercel-optimize/references/support-topics/core-web-vitals-client-bottlenecks.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/core-web-vitals-client-bottlenecks.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fcore-web-vitals-client-bottlenecks.md -->","frontmatter":{"id":"core-web-vitals-client-bottlenecks","title":"Core Web Vitals client bottlenecks","status":"active","candidateKinds":["cwv_poor"],"frameworks":["*"],"priority":90,"citations":["https://vercel.com/docs/speed-insights","https://web.dev/articles/vitals","https://web.dev/articles/optimize-lcp","https://web.dev/articles/optimize-inp","https://web.dev/articles/optimize-cls"],"maxBriefChars":850},"isInternal":false,"tokens":250,"sizeBytes":1037},{"name":"database-egress-pooling-region.md","path":"skills/vercel-optimize/references/support-topics/database-egress-pooling-region.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/database-egress-pooling-region.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fdatabase-egress-pooling-region.md -->","frontmatter":{"id":"database-egress-pooling-region","title":"Database region and connection pressure","status":"active","candidateKinds":["slow_route"],"frameworks":["*"],"priority":60,"citations":["https://vercel.com/docs/regions","https://vercel.com/docs/functions","https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package","https://vercel.com/docs/functions/limitations"],"maxBriefChars":800},"isInternal":false,"tokens":222,"sizeBytes":1015},{"name":"dynamic-rendering-traps.md","path":"skills/vercel-optimize/references/support-topics/dynamic-rendering-traps.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/dynamic-rendering-traps.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fdynamic-rendering-traps.md -->","frontmatter":{"id":"dynamic-rendering-traps","title":"Dynamic rendering traps","status":"active","candidateKinds":["rendering_candidate"],"frameworks":["next@>=13.0.0"],"priority":90,"citations":["https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config","https://nextjs.org/docs/app/api-reference/functions/generate-static-params","https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering"],"maxBriefChars":850},"isInternal":false,"tokens":247,"sizeBytes":1139},{"name":"external-api-critical-path-platform.md","path":"skills/vercel-optimize/references/support-topics/external-api-critical-path-platform.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/external-api-critical-path-platform.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fexternal-api-critical-path-platform.md -->","frontmatter":{"id":"external-api-critical-path-platform","title":"Cross-framework external API critical path","status":"active","candidateKinds":["external_api_slow"],"frameworks":["*"],"priority":86,"citations":["https://vercel.com/docs/functions/debug-slow-functions","https://vercel.com/docs/caching/runtime-cache","https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package"],"maxBriefChars":850},"isInternal":false,"tokens":245,"sizeBytes":1145},{"name":"external-api-critical-path.md","path":"skills/vercel-optimize/references/support-topics/external-api-critical-path.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/external-api-critical-path.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fexternal-api-critical-path.md -->","frontmatter":{"id":"external-api-critical-path","title":"External API critical path","status":"active","candidateKinds":["external_api_slow"],"frameworks":["next@>=13.0.0"],"priority":90,"citations":["vercel-react-best-practices:async-parallel","vercel-react-best-practices:server-parallel-fetching","vercel-react-best-practices:server-cache-react"],"maxBriefChars":850},"isInternal":false,"tokens":221,"sizeBytes":978},{"name":"fast-data-transfer-payloads.md","path":"skills/vercel-optimize/references/support-topics/fast-data-transfer-payloads.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/fast-data-transfer-payloads.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffast-data-transfer-payloads.md -->","frontmatter":{"id":"fast-data-transfer-payloads","title":"Fast Data Transfer payloads","status":"active","candidateKinds":["uncached_route"],"frameworks":["*"],"priority":65,"citations":["https://vercel.com/docs/manage-cdn-usage","https://vercel.com/docs/caching/cdn-cache"],"maxBriefChars":900},"isInternal":false,"tokens":224,"sizeBytes":1050},{"name":"fluid-compute-caveats.md","path":"skills/vercel-optimize/references/support-topics/fluid-compute-caveats.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/fluid-compute-caveats.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffluid-compute-caveats.md -->","frontmatter":{"id":"fluid-compute-caveats","title":"Fluid compute caveats","status":"active","candidateKinds":["platform_fluid_compute","cold_start"],"frameworks":["*"],"priority":80,"citations":["https://vercel.com/docs/fluid-compute"],"maxBriefChars":900},"isInternal":false,"tokens":244,"sizeBytes":1125},{"name":"function-duration-io-and-after.md","path":"skills/vercel-optimize/references/support-topics/function-duration-io-and-after.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/function-duration-io-and-after.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffunction-duration-io-and-after.md -->","frontmatter":{"id":"function-duration-io-and-after","title":"Function duration, I/O, and post-response work","status":"active","candidateKinds":["slow_route"],"frameworks":["next@>=15.0.0"],"priority":75,"citations":["https://nextjs.org/docs/app/api-reference/functions/after","vercel-react-best-practices:async-parallel","vercel-react-best-practices:server-after-nonblocking"],"maxBriefChars":850},"isInternal":false,"tokens":236,"sizeBytes":1027},{"name":"function-invocation-reduction.md","path":"skills/vercel-optimize/references/support-topics/function-invocation-reduction.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/function-invocation-reduction.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffunction-invocation-reduction.md -->","frontmatter":{"id":"function-invocation-reduction","title":"Function invocation reduction","status":"active","candidateKinds":["slow_route"],"frameworks":["next@>=13.0.0"],"priority":70,"citations":["https://react.dev/reference/react/cache","vercel-react-best-practices:server-parallel-fetching","vercel-react-best-practices:server-cache-react"],"maxBriefChars":850},"isInternal":false,"tokens":263,"sizeBytes":1181},{"name":"function-region-misconfiguration-ttfb.md","path":"skills/vercel-optimize/references/support-topics/function-region-misconfiguration-ttfb.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/function-region-misconfiguration-ttfb.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Ffunction-region-misconfiguration-ttfb.md -->","frontmatter":{"id":"function-region-misconfiguration-ttfb","title":"Function region misconfiguration (TTFB)","status":"active","candidateKinds":["region_misconfig"],"frameworks":["*"],"scannerPatterns":["region-pin-in-config"],"priority":85,"citations":["https://vercel.com/docs/functions/configuring-functions/region","https://vercel.com/docs/regions"],"maxBriefChars":950},"isInternal":false,"tokens":275,"sizeBytes":1254},{"name":"image-optimization-cost-control.md","path":"skills/vercel-optimize/references/support-topics/image-optimization-cost-control.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/image-optimization-cost-control.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fimage-optimization-cost-control.md -->","frontmatter":{"id":"image-optimization-cost-control","title":"Image optimization cost control","status":"active","candidateKinds":["image_optimization"],"frameworks":["*"],"priority":90,"citations":["https://vercel.com/docs/image-optimization","https://vercel.com/docs/image-optimization/managing-image-optimization-costs","https://vercel.com/docs/image-optimization/limits-and-pricing"],"maxBriefChars":850},"isInternal":false,"tokens":225,"sizeBytes":1071},{"name":"isr-revalidation-static-generation.md","path":"skills/vercel-optimize/references/support-topics/isr-revalidation-static-generation.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/isr-revalidation-static-generation.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fisr-revalidation-static-generation.md -->","frontmatter":{"id":"isr-revalidation-static-generation","title":"ISR revalidation and static generation","status":"active","candidateKinds":["isr_overrevalidation"],"frameworks":["next@>=13.4.0"],"priority":95,"citations":["https://vercel.com/docs/incremental-static-regeneration","https://nextjs.org/docs/app/api-reference/functions/revalidateTag","https://nextjs.org/docs/app/api-reference/functions/revalidatePath"],"maxBriefChars":1000},"isInternal":false,"tokens":308,"sizeBytes":1419},{"name":"middleware-proxy-edge-cost.md","path":"skills/vercel-optimize/references/support-topics/middleware-proxy-edge-cost.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/middleware-proxy-edge-cost.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fmiddleware-proxy-edge-cost.md -->","frontmatter":{"id":"middleware-proxy-edge-cost","title":"Middleware edge cost","status":"active","candidateKinds":["middleware_heavy"],"frameworks":["next@>=12.0.0"],"priority":90,"citations":["https://nextjs.org/docs/app/building-your-application/routing/middleware","https://vercel.com/docs/routing-middleware"],"maxBriefChars":850},"isInternal":false,"tokens":211,"sizeBytes":991},{"name":"next-fetch-revalidate-floor.md","path":"skills/vercel-optimize/references/support-topics/next-fetch-revalidate-floor.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/next-fetch-revalidate-floor.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-fetch-revalidate-floor.md -->","frontmatter":{"id":"next-fetch-revalidate-floor","title":"Next.js fetch revalidation floor","status":"active","candidateKinds":["uncached_route","isr_overrevalidation"],"frameworks":["next@>=13.0.0"],"priority":88,"citations":["https://nextjs.org/docs/app/api-reference/functions/fetch","https://nextjs.org/docs/app/building-your-application/caching"],"maxBriefChars":850},"isInternal":false,"tokens":247,"sizeBytes":1059},{"name":"next-font-cls-self-hosting.md","path":"skills/vercel-optimize/references/support-topics/next-font-cls-self-hosting.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/next-font-cls-self-hosting.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-font-cls-self-hosting.md -->","frontmatter":{"id":"next-font-cls-self-hosting","title":"Next.js font CLS guardrail","status":"active","candidateKinds":["cwv_poor"],"frameworks":["next@>=13.2.0"],"metrics":["CLS"],"priority":86,"citations":["https://nextjs.org/docs/app/api-reference/components/font","https://web.dev/articles/optimize-cls"],"maxBriefChars":800},"isInternal":false,"tokens":220,"sizeBytes":906},{"name":"next-heavy-ui-lazy-load-boundaries.md","path":"skills/vercel-optimize/references/support-topics/next-heavy-ui-lazy-load-boundaries.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/next-heavy-ui-lazy-load-boundaries.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-heavy-ui-lazy-load-boundaries.md -->","frontmatter":{"id":"next-heavy-ui-lazy-load-boundaries","title":"Next.js heavy UI lazy-load boundaries","status":"active","candidateKinds":["cwv_poor"],"frameworks":["next@*"],"metrics":["LCP","INP"],"priority":82,"citations":["https://nextjs.org/docs/app/guides/lazy-loading","https://web.dev/articles/optimize-inp"],"maxBriefChars":850},"isInternal":false,"tokens":246,"sizeBytes":1048},{"name":"next-image-lcp-preload-sizes.md","path":"skills/vercel-optimize/references/support-topics/next-image-lcp-preload-sizes.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/next-image-lcp-preload-sizes.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-image-lcp-preload-sizes.md -->","frontmatter":{"id":"next-image-lcp-preload-sizes","title":"Next.js image LCP preload and sizes","status":"active","candidateKinds":["cwv_poor"],"frameworks":["next@*"],"metrics":["LCP"],"priority":86,"citations":["https://nextjs.org/docs/app/api-reference/components/image","https://web.dev/articles/optimize-lcp"],"maxBriefChars":850},"isInternal":false,"tokens":242,"sizeBytes":1062},{"name":"next-route-handler-get-cache-defaults.md","path":"skills/vercel-optimize/references/support-topics/next-route-handler-get-cache-defaults.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/next-route-handler-get-cache-defaults.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-route-handler-get-cache-defaults.md -->","frontmatter":{"id":"next-route-handler-get-cache-defaults","title":"Next.js Route Handler GET cache defaults","status":"active","candidateKinds":["uncached_route","cache_header_gap"],"frameworks":["next@>=15.0.0"],"priority":91,"citations":["https://nextjs.org/docs/app/api-reference/file-conventions/route","https://vercel.com/docs/caching/cdn-cache"],"maxBriefChars":850},"isInternal":false,"tokens":245,"sizeBytes":1072},{"name":"next-script-third-party-strategy.md","path":"skills/vercel-optimize/references/support-topics/next-script-third-party-strategy.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/next-script-third-party-strategy.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnext-script-third-party-strategy.md -->","frontmatter":{"id":"next-script-third-party-strategy","title":"Next.js third-party script strategy","status":"active","candidateKinds":["cwv_poor"],"frameworks":["next@*"],"metrics":["LCP","INP"],"priority":85,"citations":["https://nextjs.org/docs/app/api-reference/components/script","https://web.dev/articles/optimize-inp"],"maxBriefChars":850},"isInternal":false,"tokens":242,"sizeBytes":1051},{"name":"nextjs-version-cache-semantics.md","path":"skills/vercel-optimize/references/support-topics/nextjs-version-cache-semantics.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/nextjs-version-cache-semantics.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnextjs-version-cache-semantics.md -->","frontmatter":{"id":"nextjs-version-cache-semantics","title":"Next.js cache semantics by version","status":"active","candidateKinds":["uncached_route"],"frameworks":["next@>=15.0.0"],"priority":85,"citations":["https://nextjs.org/docs/app/api-reference/directives/use-cache","https://nextjs.org/docs/app/api-reference/functions/cacheLife","https://nextjs.org/docs/app/building-your-application/caching"],"maxBriefChars":800},"isInternal":false,"tokens":239,"sizeBytes":996},{"name":"not-found-catchall-request-waste.md","path":"skills/vercel-optimize/references/support-topics/not-found-catchall-request-waste.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/not-found-catchall-request-waste.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnot-found-catchall-request-waste.md -->","frontmatter":{"id":"not-found-catchall-request-waste","title":"Not-found and catch-all request waste","status":"active","candidateKinds":["uncached_route"],"frameworks":["*"],"routePatterns":["(^|/)404$","not-found","\\[\\.\\.\\."],"priority":92,"citations":["https://vercel.com/docs/routing/","https://vercel.com/docs/redirects/bulk-redirects/","https://vercel.com/docs/vercel-firewall/vercel-waf/custom-rules","https://vercel.com/docs/vercel-firewall/vercel-waf/managed-rulesets"],"maxBriefChars":850},"isInternal":false,"tokens":311,"sizeBytes":1273},{"name":"nuxt-route-rules-cache-isr.md","path":"skills/vercel-optimize/references/support-topics/nuxt-route-rules-cache-isr.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/nuxt-route-rules-cache-isr.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fnuxt-route-rules-cache-isr.md -->","frontmatter":{"id":"nuxt-route-rules-cache-isr","title":"Nuxt routeRules cache and ISR","status":"active","candidateKinds":["uncached_route","isr_overrevalidation","rendering_candidate"],"frameworks":["nuxt@>=3.0.0"],"priority":90,"citations":["https://vercel.com/docs/frameworks/full-stack/nuxt","https://nuxt.com/docs/4.x/api/utils/define-route-rules","https://nuxt.com/docs/4.x/guide/concepts/rendering"],"maxBriefChars":850},"isInternal":false,"tokens":289,"sizeBytes":1188},{"name":"observability-events-cost-attribution.md","path":"skills/vercel-optimize/references/support-topics/observability-events-cost-attribution.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/observability-events-cost-attribution.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fobservability-events-cost-attribution.md -->","frontmatter":{"id":"observability-events-cost-attribution","title":"Observability Events cost attribution","status":"active","candidateKinds":["observability_events_attribution"],"frameworks":["*"],"priority":92,"citations":["https://vercel.com/docs/observability/observability-plus","https://vercel.com/docs/alerts"],"maxBriefChars":900},"isInternal":false,"tokens":250,"sizeBytes":1155},{"name":"post-response-work-waituntil.md","path":"skills/vercel-optimize/references/support-topics/post-response-work-waituntil.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/post-response-work-waituntil.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fpost-response-work-waituntil.md -->","frontmatter":{"id":"post-response-work-waituntil","title":"Post-response work with waitUntil","status":"active","candidateKinds":["slow_route","external_api_slow"],"frameworks":["next@<15.0.0","sveltekit@*","astro@*","nuxt@*","unknown@*"],"priority":78,"citations":["https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package"],"maxBriefChars":800},"isInternal":false,"tokens":228,"sizeBytes":1009},{"name":"README.md","path":"skills/vercel-optimize/references/support-topics/README.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/README.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2FREADME.md -->","isInternal":false,"tokens":410,"sizeBytes":1785},{"name":"route-error-durable-offload.md","path":"skills/vercel-optimize/references/support-topics/route-error-durable-offload.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/route-error-durable-offload.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Froute-error-durable-offload.md -->","frontmatter":{"id":"route-error-durable-offload","title":"Durable offload for timeout-heavy routes","status":"active","candidateKinds":["route_errors"],"frameworks":["*"],"priority":84,"citations":["https://vercel.com/docs/workflow","https://workflow-sdk.dev/docs/foundations/starting-workflows","https://workflow-sdk.dev/docs/foundations/workflows-and-steps","https://vercel.com/docs/queues","https://vercel.com/docs/functions/limitations"],"maxBriefChars":850},"isInternal":false,"tokens":286,"sizeBytes":1298},{"name":"route-error-runtime-limits.md","path":"skills/vercel-optimize/references/support-topics/route-error-runtime-limits.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/route-error-runtime-limits.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Froute-error-runtime-limits.md -->","frontmatter":{"id":"route-error-runtime-limits","title":"Route errors and runtime limits","status":"active","candidateKinds":["route_errors"],"frameworks":["*"],"priority":90,"citations":["https://vercel.com/docs/functions","https://vercel.com/docs/functions/limitations","https://vercel.com/docs/cli/inspect"],"maxBriefChars":850},"isInternal":false,"tokens":215,"sizeBytes":988},{"name":"runtime-cache-reusable-data.md","path":"skills/vercel-optimize/references/support-topics/runtime-cache-reusable-data.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/runtime-cache-reusable-data.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fruntime-cache-reusable-data.md -->","frontmatter":{"id":"runtime-cache-reusable-data","title":"Runtime Cache for reusable server data","status":"active","candidateKinds":["slow_route","external_api_slow"],"frameworks":["*"],"priority":84,"citations":["https://vercel.com/docs/caching/runtime-cache","https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package"],"maxBriefChars":850},"isInternal":false,"tokens":249,"sizeBytes":1180},{"name":"sveltekit-isr-prerender-safety.md","path":"skills/vercel-optimize/references/support-topics/sveltekit-isr-prerender-safety.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/sveltekit-isr-prerender-safety.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fsveltekit-isr-prerender-safety.md -->","frontmatter":{"id":"sveltekit-isr-prerender-safety","title":"SvelteKit ISR and prerender safety","status":"active","candidateKinds":["uncached_route","isr_overrevalidation"],"frameworks":["sveltekit@*"],"priority":90,"citations":["https://vercel.com/docs/frameworks/full-stack/sveltekit","https://svelte.dev/docs/kit/adapter-vercel","https://svelte.dev/docs/kit/page-options"],"maxBriefChars":850},"isInternal":false,"tokens":294,"sizeBytes":1167},{"name":"sveltekit-split-cold-start-tradeoff.md","path":"skills/vercel-optimize/references/support-topics/sveltekit-split-cold-start-tradeoff.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/sveltekit-split-cold-start-tradeoff.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fsveltekit-split-cold-start-tradeoff.md -->","frontmatter":{"id":"sveltekit-split-cold-start-tradeoff","title":"SvelteKit split function cold-start tradeoff","status":"active","candidateKinds":["cold_start","slow_route"],"frameworks":["sveltekit@*"],"priority":82,"citations":["https://vercel.com/docs/frameworks/full-stack/sveltekit","https://svelte.dev/docs/kit/adapter-vercel"],"maxBriefChars":800},"isInternal":false,"tokens":232,"sizeBytes":1040},{"name":"usage-spike-triage.md","path":"skills/vercel-optimize/references/support-topics/usage-spike-triage.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/usage-spike-triage.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fusage-spike-triage.md -->","frontmatter":{"id":"usage-spike-triage","title":"Usage spike triage","status":"active","candidateKinds":["usage_spike_triage"],"frameworks":["*"],"priority":95,"citations":["https://vercel.com/docs/alerts","https://vercel.com/docs/spend-management","https://vercel.com/docs/bot-management"],"maxBriefChars":950},"isInternal":false,"tokens":239,"sizeBytes":1018},{"name":"use-cache-date-stamp-isr-write-amplifier.md","path":"skills/vercel-optimize/references/support-topics/use-cache-date-stamp-isr-write-amplifier.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/use-cache-date-stamp-isr-write-amplifier.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fuse-cache-date-stamp-isr-write-amplifier.md -->","frontmatter":{"id":"use-cache-date-stamp-isr-write-amplifier","title":"'use cache' date-stamp ISR write amplifier","status":"active","candidateKinds":["use_cache_date_stamp"],"frameworks":["next@>=15.0.0"],"scannerPatterns":["use-cache-date-stamp"],"priority":88,"citations":["https://nextjs.org/docs/app/api-reference/directives/use-cache","https://nextjs.org/docs/app/api-reference/functions/cacheLife"],"maxBriefChars":900},"isInternal":false,"tokens":297,"sizeBytes":1189},{"name":"use-cache-remote-shared-origin-data.md","path":"skills/vercel-optimize/references/support-topics/use-cache-remote-shared-origin-data.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/use-cache-remote-shared-origin-data.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fuse-cache-remote-shared-origin-data.md -->","frontmatter":{"id":"use-cache-remote-shared-origin-data","title":"Remote cache for shared origin data","status":"active","candidateKinds":["external_api_slow","slow_route","uncached_route"],"frameworks":["next@>=16.0.0"],"priority":87,"citations":["https://vercel.com/docs/caching/runtime-cache","https://nextjs.org/docs/app/api-reference/directives/use-cache-remote"],"maxBriefChars":950},"isInternal":false,"tokens":258,"sizeBytes":1089},{"name":"workflow-resumable-stream-routes.md","path":"skills/vercel-optimize/references/support-topics/workflow-resumable-stream-routes.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/support-topics/workflow-resumable-stream-routes.md","title":"Support-topics Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fsupport-topics%2Fworkflow-resumable-stream-routes.md -->","frontmatter":{"id":"workflow-resumable-stream-routes","title":"Workflow resumable stream routes","status":"active","candidateKinds":["slow_route"],"frameworks":["*"],"routePatterns":["(^|/)api/.*/stream/?$","(^|/)chat/.*/stream/?$","\\[id\\].*/stream"],"priority":98,"citations":["https://workflow-sdk.dev/docs/ai/resumable-streams","https://workflow-sdk.dev/docs/foundations/streaming","https://vercel.com/docs/workflow"],"maxBriefChars":850},"isInternal":false,"tokens":317,"sizeBytes":1274},{"name":"verification.md","path":"skills/vercel-optimize/references/verification.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/verification.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fverification.md -->","isInternal":false,"tokens":2222,"sizeBytes":9000},{"name":"voice.md","path":"skills/vercel-optimize/references/voice.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/references/voice.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2Freferences%2Fvoice.md -->","isInternal":false,"tokens":1006,"sizeBytes":3731},{"name":"SKILL.md","path":"skills/vercel-optimize/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/vercel-optimize/SKILL.md","title":"vercel-optimize","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fvercel-optimize%2FSKILL.md -->","frontmatter":{"name":"vercel-optimize","description":"Use for Vercel cost and performance optimization on deployed projects, especially Next.js, SvelteKit, Nuxt, and limited Astro apps. Collect Vercel metrics, usage, project config, and code scan results first; investigate only metric-backed candidates; produce ranked recommendations grounded in verified files and version-aware Vercel/framework docs. Trigger for Vercel bill reduction, slow or expensive routes, caching opportunities, Function Invocations, Build Minutes, Fast Data Transfer, Core Web Vitals, Bot Management, Fluid compute, or cost breakdown requests.","metadata":{"version":"1.2.0"}},"isInternal":false,"tokens":3977,"sizeBytes":17311},{"name":"SKILL.md","path":"skills/web-design-guidelines/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/web-design-guidelines/SKILL.md","title":"web-design-guidelines","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fweb-design-guidelines%2FSKILL.md -->","frontmatter":{"name":"web-design-guidelines","description":"Review UI code for Web Interface Guidelines compliance. Use when asked to \"review my UI\", \"check accessibility\", \"audit design\", \"review UX\", or \"check my site against best practices\".","metadata":{"author":"vercel","version":"1.0.0","argument-hint":"<file-or-pattern>"}},"isInternal":false,"tokens":268,"sizeBytes":1231},{"name":"SKILL.md","path":"skills/writing-guidelines/SKILL.md","rawUrl":"https://raw.githubusercontent.com/vercel-labs/agent-skills/HEAD/skills/writing-guidelines/SKILL.md","title":"writing-guidelines","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/vercel-labs/agent-skills?path=skills%2Fwriting-guidelines%2FSKILL.md -->","frontmatter":{"name":"writing-guidelines","description":"Review docs/prose for Writing Guidelines compliance. Use when asked to \"review my docs\", \"check writing style\", \"audit prose\", \"review docs voice and tone\", or \"check this page against the writing handbook\".","metadata":{"author":"vercel","version":"1.0.0","argument-hint":"<file-or-pattern>"}},"isInternal":false,"tokens":270,"sizeBytes":1233}],"systemPromptSnippet":"<yakaai_skills repo=\"vercel-labs/agent-skills\">\n<!-- File: AGENTS.md (Tokens: ~770 | Category: root-instruction) -->\n# AGENTS.md\n\nThis file provides guidance to AI coding agents (Claude Code, Cursor, Copilot, etc.) when working with code in this repository.\n\n## Repository Overview\n\nA collection of skills for AI coding agents working with Vercel projects. Skills are packaged instructions and scripts that extend agent capabilities.\n\n## Creating a New Skill\n\n### Directory Structure\n\n```\nskills/\n  {skill-name}/           # kebab-case directory name\n    SKILL.md              # Required: skill definition\n    scripts/              # Optional: executable scripts\n      {script-name}.sh    # Bash scripts\n      {script-name}.mjs   # Node scripts\n    references/           # Optional: supporting docs loaded on demand\n    lib/                  # Optional: shared code for scripts\n```\n\n### Naming Conventions\n\n- **Skill directory**: `kebab-case` (e.g., `vercel-deploy`, `log-monitor`)\n- **SKILL.md**: Always uppercase, always this exact filename\n- **Scripts**: `kebab-case.sh` or `kebab-case.mjs` (e.g., `deploy.sh`, `collect-signals.mjs`)\n\n### SKILL.md Format\n\n```markdown\n---\nname: {skill-name}\ndescription: {One sentence describing when to use this skill. Include trigger phrases like \"Deploy my app\", \"Check logs\", etc.}\n---\n\n# {Skill Title}\n\n{Brief description of what the skill does.}\n\n## How It Works\n\n{Numbered list explaining the skill's workflow}\n\n## Usage\n\n```bash\nbash /mnt/skills/user/{skill-name}/scripts/{script}.sh [args]\n```\n\n**Arguments:**\n- `arg1` - Description (defaults to X)\n\n**Examples:**\n{Show 2-3 common usage patterns}\n\n## Output\n\n{Show example output users will see}\n\n## Present Results to User\n\n{Template for how Claude should format results when presenting to users}\n\n## Troubleshooting\n\n{Common issues and solutions, especially network/permissions errors}\n```\n\n### Best Practices for Context Efficiency\n\nSkills are loaded on-demand — only the skill name and description are loaded at startup. The full `SKILL.md` loads into context only when the agent decides the skill is relevant. To minimize context usage:\n\n- **Keep SKILL.md under 500 lines** — put detailed reference material in separate files\n- **Write specific descriptions** — helps the agent know exactly when to activate the skill\n- **Use progressive disclosure** — reference supporting files that get read only when needed\n- **Prefer scripts over inline code** — script execution doesn't consume context (only output does)\n- **File references work one level deep** — link directly from SKILL.md to supporting files\n\n### Script Requirements\n\n- Bash scripts: use `#!/bin/bash` and `set -e`\n- Node scripts: use `#!/usr/bin/env node` and `.mjs`\n- Write status messages to stderr\n- Write machine-readable output (JSON) to stdout\n- Include a cleanup trap for temp files when scripts create them\n- Reference scripts by relative path, for example `node scripts/{script}.mjs`\n\n### End-User Installation\n\nDocument skills.sh installation for public skills:\n\n```bash\nnpx skills add vercel-labs/agent-skills --skill {skill-name}\n```\n\nFor manual installs, document the native path when the skill needs one.\n\n**Claude Code:**\n```bash\ncp -r skills/{skill-name} ~/.claude/skills/\n```\n\n**claude.ai:**\nAdd the skill to project knowledge or paste SKILL.md contents into the conversation.\n\nIf the skill requires network access, instruct users to add required domains at `claude.ai/settings/capabilities`.\n\n<!-- File: CLAUDE.md (Tokens: ~3 | Category: claude-rule) -->\nAGENTS.md\n\n<!-- File: skills/composition-patterns/AGENTS.md (Tokens: ~5063 | Category: anthropic-skill) -->\n# React Composition Patterns\n\n**Version 1.0.0**  \nEngineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React codebases using composition. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComposition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\n\n---\n\n## Table of Contents\n\n1. [Component Architecture](#1-component-architecture) — **HIGH**\n   - 1.1 [Avoid Boolean Prop Proliferation](#11-avoid-boolean-prop-proliferation)\n   - 1.2 [Use Compound Components](#12-use-compound-components)\n2. [State Management](#2-state-management) — **MEDIUM**\n   - 2.1 [Decouple State Management from UI](#21-decouple-state-management-from-ui)\n   - 2.2 [Define Generic Context Interfaces for Dependency Injection](#22-define-generic-context-interfaces-for-dependency-injection)\n   - 2.3 [Lift State into Provider Components](#23-lift-state-into-provider-components)\n3. [Implementation Patterns](#3-implementation-patterns) — **MEDIUM**\n   - 3.1 [Create Explicit Component Variants](#31-create-explicit-component-variants)\n   - 3.2 [Prefer Composing Children Over Render Props](#32-prefer-composing-children-over-render-props)\n4. [React 19 APIs](#4-react-19-apis) — **MEDIUM**\n   - 4.1 [React 19 API Changes](#41-react-19-api-changes)\n\n---\n\n## 1. Component Architecture\n\n**Impact: HIGH**\n\nFundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n### 1.1 Avoid Boolean Prop Proliferation\n\n**Impact: CRITICAL (prevents unmaintainable component variants)**\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\n\ncomponent behavior. Each boolean doubles possible states and creates\n\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect: boolean props create exponential complexity**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct: composition eliminates conditionals**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\n\nsharing a single monolithic parent.\n\n### 1.2 Use Compound Components\n\n**Impact: HIGH (enables flexible composition without prop drilling)**\n\nStructure complex components as compound components with a shared context. Each\n\nsubcomponent accesses shared state via context, not props. Consumers compose the\n\npieces they need.\n\n**Incorrect: monolithic component with render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct: compound components with shared context**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n\n---\n\n## 2. State Management\n\n**Impact: MEDIUM**\n\nPatterns for lifting state and managing shared context across\ncomposed components.\n\n### 2.1 Decouple State Management from UI\n\n**Impact: MEDIUM (enables swapping state implementations without changing UI)**\n\nThe provider component should be the only place that knows how state is managed.\n\nUI components consume the context interface—they don't know if state comes from\n\nuseState, Zustand, or a server sync.\n\n**Incorrect: UI coupled to state implementation**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct: state management isolated in provider**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\n\ndepends on the context interface, not the implementation.\n\n### 2.2 Define Generic Context Interfaces for Dependency Injection\n\n**Impact: HIGH (enables dependency-injectable state across use-cases)**\n\nDefine a **generic interface** for your component context with three parts:\n\n`state`, `actions`, and `meta`. This interface is a contract that any provider\n\ncan implement—enabling the same UI components to work with completely different\n\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\n\ndependency-injectable.\n\n**Incorrect: UI coupled to specific state implementation**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct: generic interface enables dependency injection**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe provider boundary is what matters—not the visual nesting. Components that\n\nneed shared state don't have to be inside the `Composer.Frame`. They just need\n\nto be within the provider.\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\n\nbox, but they can still access its state and actions. This is the power of\n\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\n\nby the provider. Swap the provider, keep the UI.\n\n### 2.3 Lift State into Provider Components\n\n**Impact: HIGH (enables state sharing outside component boundaries)**\n\nMove state management into dedicated provider components. This allows sibling\n\ncomponents outside the main UI to access and modify state without prop drilling\n\nor awkward refs.\n\n**Incorrect: state trapped inside component**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect: useEffect to sync state up**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect: reading state from ref on submit**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct: state lifted to provider**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\n\nsubmit action because it's within the provider. Even though it's a one-off\n\ncomponent, it can still access the composer's state and actions from outside the\n\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\n\nnested inside each other—they just need to be within the same provider.\n\n---\n\n## 3. Implementation Patterns\n\n**Impact: MEDIUM**\n\nSpecific techniques for implementing compound components and\ncontext providers.\n\n### 3.1 Create Explicit Component Variants\n\n**Impact: MEDIUM (self-documenting code, no hidden conditionals)**\n\nInstead of one component with many boolean props, create explicit variant\n\ncomponents. Each variant composes the pieces it needs. The code documents\n\nitself.\n\n**Incorrect: one component, many modes**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct: explicit variants**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\n\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n\n- What UI elements it includes\n\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n\n### 3.2 Prefer Composing Children Over Render Props\n\n**Impact: MEDIUM (cleaner composition, better readability)**\n\nUse `children` for composition instead of `renderX` props. Children are more\n\nreadable, compose naturally, and don't require understanding callback\n\nsignatures.\n\n**Incorrect: render props**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct: compound components with children**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\n\nUse children when composing static structure.\n\n---\n\n## 4. React 19 APIs\n\n**Impact: MEDIUM**\n\nReact 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n\n### 4.1 React 19 API Changes\n\n**Impact: MEDIUM (cleaner component definitions and context usage)**\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect: forwardRef in React 19**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct: ref as a regular prop**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect: useContext in React 19**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct: use instead of useContext**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n\n---\n\n## References\n\n1. [https://react.dev](https://react.dev)\n2. [https://react.dev/learn/passing-data-deeply-with-context](https://react.dev/learn/passing-data-deeply-with-context)\n3. [https://react.dev/reference/react/use](https://react.dev/reference/react/use)\n\n<!-- File: skills/composition-patterns/metadata.json (Tokens: ~122 | Category: anthropic-skill) -->\n{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://react.dev/learn/passing-data-deeply-with-context\",\n    \"https://react.dev/reference/react/use\"\n  ]\n}\n\n<!-- File: skills/composition-patterns/README.md (Tokens: ~476 | Category: anthropic-skill) -->\n# React Composition Patterns\n\nA structured repository for React composition patterns that scale. These\npatterns help avoid boolean prop proliferation by using compound components,\nlifting state, and composing internals.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `metadata.json` - Document metadata (version, organization, abstract)\n- **`AGENTS.md`** - Compiled output (generated)\n\n## Rules\n\n### Component Architecture (CRITICAL)\n\n- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize\n  behavior\n- `architecture-compound-components.md` - Structure as compound components with\n  shared context\n\n### State Management (HIGH)\n\n- `state-lift-state.md` - Lift state into provider components\n- `state-context-interface.md` - Define clear context interfaces\n  (state/actions/meta)\n- `state-decouple-implementation.md` - Decouple state management from UI\n\n### Implementation Patterns (MEDIUM)\n\n- `patterns-children-over-render-props.md` - Prefer children over renderX props\n- `patterns-explicit-variants.md` - Create explicit component variants\n\n## Core Principles\n\n1. **Composition over configuration** — Instead of adding props, let consumers\n   compose\n2. **Lift your state** — State in providers, not trapped in components\n3. **Compose your internals** — Subcomponents access context, not props\n4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer\n   with isThread\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `architecture-` for Component Architecture\n   - `state-` for State Management\n   - `patterns-` for Implementation Patterns\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n\n## Impact Levels\n\n- `CRITICAL` - Foundational patterns, prevents unmaintainable code\n- `HIGH` - Significant maintainability improvements\n- `MEDIUM` - Good practices for cleaner code\n\n<!-- File: skills/composition-patterns/rules/_sections.md (Tokens: ~176 | Category: anthropic-skill) -->\n# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Component Architecture (architecture)\n\n**Impact:** HIGH  \n**Description:** Fundamental patterns for structuring components to avoid prop\nproliferation and enable flexible composition.\n\n## 2. State Management (state)\n\n**Impact:** MEDIUM  \n**Description:** Patterns for lifting state and managing shared context across\ncomposed components.\n\n## 3. Implementation Patterns (patterns)\n\n**Impact:** MEDIUM  \n**Description:** Specific techniques for implementing compound components and\ncontext providers.\n\n## 4. React 19 APIs (react19)\n\n**Impact:** MEDIUM  \n**Description:** React 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.\n\n<!-- File: skills/composition-patterns/rules/_template.md (Tokens: ~80 | Category: anthropic-skill) -->\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: brief description of impact\ntags: composition, components\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect:**\n\n```tsx\n// Bad code example\n```\n\n**Correct:**\n\n```tsx\n// Good code example\n```\n\nReference: [Link](https://example.com)\n\n<!-- File: skills/composition-patterns/rules/architecture-avoid-boolean-props.md (Tokens: ~513 | Category: anthropic-skill) -->\n---\ntitle: Avoid Boolean Prop Proliferation\nimpact: CRITICAL\nimpactDescription: prevents unmaintainable component variants\ntags: composition, props, architecture\n---\n\n## Avoid Boolean Prop Proliferation\n\nDon't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize\ncomponent behavior. Each boolean doubles possible states and creates\nunmaintainable conditional logic. Use composition instead.\n\n**Incorrect (boolean props create exponential complexity):**\n\n```tsx\nfunction Composer({\n  onSubmit,\n  isThread,\n  channelId,\n  isDMThread,\n  dmId,\n  isEditing,\n  isForwarding,\n}: Props) {\n  return (\n    <form>\n      <Header />\n      <Input />\n      {isDMThread ? (\n        <AlsoSendToDMField id={dmId} />\n      ) : isThread ? (\n        <AlsoSendToChannelField id={channelId} />\n      ) : null}\n      {isEditing ? (\n        <EditActions />\n      ) : isForwarding ? (\n        <ForwardActions />\n      ) : (\n        <DefaultActions />\n      )}\n      <Footer onSubmit={onSubmit} />\n    </form>\n  )\n}\n```\n\n**Correct (composition eliminates conditionals):**\n\n```tsx\n// Channel composer\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Attachments />\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Thread composer - adds \"also send to channel\" field\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <AlsoSendToChannelField id={channelId} />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Edit composer - different footer actions\nfunction EditComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Formatting />\n        <Composer.Emojis />\n        <Composer.CancelEdit />\n        <Composer.SaveEdit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n```\n\nEach variant is explicit about what it renders. We can share internals without\nsharing a single monolithic parent.\n\n<!-- File: skills/composition-patterns/rules/architecture-compound-components.md (Tokens: ~578 | Category: anthropic-skill) -->\n---\ntitle: Use Compound Components\nimpact: HIGH\nimpactDescription: enables flexible composition without prop drilling\ntags: composition, compound-components, architecture\n---\n\n## Use Compound Components\n\nStructure complex components as compound components with a shared context. Each\nsubcomponent accesses shared state via context, not props. Consumers compose the\npieces they need.\n\n**Incorrect (monolithic component with render props):**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n  showAttachments,\n  showFormatting,\n  showEmojis,\n}: Props) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {showAttachments && <Attachments />}\n      {renderFooter ? (\n        renderFooter()\n      ) : (\n        <Footer>\n          {showFormatting && <Formatting />}\n          {showEmojis && <Emojis />}\n          {renderActions?.()}\n        </Footer>\n      )}\n    </form>\n  )\n}\n```\n\n**Correct (compound components with shared context):**\n\n```tsx\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n\nfunction ComposerProvider({ children, state, actions, meta }: ProviderProps) {\n  return (\n    <ComposerContext value={{ state, actions, meta }}>\n      {children}\n    </ComposerContext>\n  )\n}\n\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta: { inputRef },\n  } = use(ComposerContext)\n  return (\n    <TextInput\n      ref={inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n\nfunction ComposerSubmit() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Send</Button>\n}\n\n// Export as compound component\nconst Composer = {\n  Provider: ComposerProvider,\n  Frame: ComposerFrame,\n  Input: ComposerInput,\n  Submit: ComposerSubmit,\n  Header: ComposerHeader,\n  Footer: ComposerFooter,\n  Attachments: ComposerAttachments,\n  Formatting: ComposerFormatting,\n  Emojis: ComposerEmojis,\n}\n```\n\n**Usage:**\n\n```tsx\n<Composer.Provider state={state} actions={actions} meta={meta}>\n  <Composer.Frame>\n    <Composer.Header />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Submit />\n    </Composer.Footer>\n  </Composer.Frame>\n</Composer.Provider>\n```\n\nConsumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.\n\n<!-- File: skills/composition-patterns/rules/patterns-children-over-render-props.md (Tokens: ~428 | Category: anthropic-skill) -->\n---\ntitle: Prefer Composing Children Over Render Props\nimpact: MEDIUM\nimpactDescription: cleaner composition, better readability\ntags: composition, children, render-props\n---\n\n## Prefer Children Over Render Props\n\nUse `children` for composition instead of `renderX` props. Children are more\nreadable, compose naturally, and don't require understanding callback\nsignatures.\n\n**Incorrect (render props):**\n\n```tsx\nfunction Composer({\n  renderHeader,\n  renderFooter,\n  renderActions,\n}: {\n  renderHeader?: () => React.ReactNode\n  renderFooter?: () => React.ReactNode\n  renderActions?: () => React.ReactNode\n}) {\n  return (\n    <form>\n      {renderHeader?.()}\n      <Input />\n      {renderFooter ? renderFooter() : <DefaultFooter />}\n      {renderActions?.()}\n    </form>\n  )\n}\n\n// Usage is awkward and inflexible\nreturn (\n  <Composer\n    renderHeader={() => <CustomHeader />}\n    renderFooter={() => (\n      <>\n        <Formatting />\n        <Emojis />\n      </>\n    )}\n    renderActions={() => <SubmitButton />}\n  />\n)\n```\n\n**Correct (compound components with children):**\n\n```tsx\nfunction ComposerFrame({ children }: { children: React.ReactNode }) {\n  return <form>{children}</form>\n}\n\nfunction ComposerFooter({ children }: { children: React.ReactNode }) {\n  return <footer className='flex'>{children}</footer>\n}\n\n// Usage is flexible\nreturn (\n  <Composer.Frame>\n    <CustomHeader />\n    <Composer.Input />\n    <Composer.Footer>\n      <Composer.Formatting />\n      <Composer.Emojis />\n      <SubmitButton />\n    </Composer.Footer>\n  </Composer.Frame>\n)\n```\n\n**When render props are appropriate:**\n\n```tsx\n// Render props work well when you need to pass data back\n<List\n  data={items}\n  renderItem={({ item, index }) => <Item item={item} index={index} />}\n/>\n```\n\nUse render props when the parent needs to provide data or state to the child.\nUse children when composing static structure.\n\n<!-- File: skills/composition-patterns/rules/patterns-explicit-variants.md (Tokens: ~503 | Category: anthropic-skill) -->\n---\ntitle: Create Explicit Component Variants\nimpact: MEDIUM\nimpactDescription: self-documenting code, no hidden conditionals\ntags: composition, variants, architecture\n---\n\n## Create Explicit Component Variants\n\nInstead of one component with many boolean props, create explicit variant\ncomponents. Each variant composes the pieces it needs. The code documents\nitself.\n\n**Incorrect (one component, many modes):**\n\n```tsx\n// What does this component actually render?\n<Composer\n  isThread\n  isEditing={false}\n  channelId='abc'\n  showAttachments\n  showFormatting={false}\n/>\n```\n\n**Correct (explicit variants):**\n\n```tsx\n// Immediately clear what this renders\n<ThreadComposer channelId=\"abc\" />\n\n// Or\n<EditMessageComposer messageId=\"xyz\" />\n\n// Or\n<ForwardMessageComposer messageId=\"123\" />\n```\n\nEach implementation is unique, explicit and self-contained. Yet they can each\nuse shared parts.\n\n**Implementation:**\n\n```tsx\nfunction ThreadComposer({ channelId }: { channelId: string }) {\n  return (\n    <ThreadProvider channelId={channelId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <AlsoSendToChannelField channelId={channelId} />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Submit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ThreadProvider>\n  )\n}\n\nfunction EditMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <EditMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.CancelEdit />\n          <Composer.SaveEdit />\n        </Composer.Footer>\n      </Composer.Frame>\n    </EditMessageProvider>\n  )\n}\n\nfunction ForwardMessageComposer({ messageId }: { messageId: string }) {\n  return (\n    <ForwardMessageProvider messageId={messageId}>\n      <Composer.Frame>\n        <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n        <Composer.Footer>\n          <Composer.Formatting />\n          <Composer.Emojis />\n          <Composer.Mentions />\n        </Composer.Footer>\n      </Composer.Frame>\n    </ForwardMessageProvider>\n  )\n}\n```\n\nEach variant is explicit about:\n\n- What provider/state it uses\n- What UI elements it includes\n- What actions are available\n\nNo boolean prop combinations to reason about. No impossible states.\n\n<!-- File: skills/composition-patterns/rules/react19-no-forwardref.md (Tokens: ~255 | Category: anthropic-skill) -->\n---\ntitle: React 19 API Changes\nimpact: MEDIUM\nimpactDescription: cleaner component definitions and context usage\ntags: react19, refs, context, hooks\n---\n\n## React 19 API Changes\n\n> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.\n\nIn React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.\n\n**Incorrect (forwardRef in React 19):**\n\n```tsx\nconst ComposerInput = forwardRef<TextInput, Props>((props, ref) => {\n  return <TextInput ref={ref} {...props} />\n})\n```\n\n**Correct (ref as a regular prop):**\n\n```tsx\nfunction ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {\n  return <TextInput ref={ref} {...props} />\n}\n```\n\n**Incorrect (useContext in React 19):**\n\n```tsx\nconst value = useContext(MyContext)\n```\n\n**Correct (use instead of useContext):**\n\n```tsx\nconst value = use(MyContext)\n```\n\n`use()` can also be called conditionally, unlike `useContext()`.\n\n<!-- File: skills/composition-patterns/rules/state-context-interface.md (Tokens: ~1076 | Category: anthropic-skill) -->\n---\ntitle: Define Generic Context Interfaces for Dependency Injection\nimpact: HIGH\nimpactDescription: enables dependency-injectable state across use-cases\ntags: composition, context, state, typescript, dependency-injection\n---\n\n## Define Generic Context Interfaces for Dependency Injection\n\nDefine a **generic interface** for your component context with three parts:\n`state`, `actions`, and `meta`. This interface is a contract that any provider\ncan implement—enabling the same UI components to work with completely different\nstate implementations.\n\n**Core principle:** Lift state, compose internals, make state\ndependency-injectable.\n\n**Incorrect (UI coupled to specific state implementation):**\n\n```tsx\nfunction ComposerInput() {\n  // Tightly coupled to a specific hook\n  const { input, setInput } = useChannelComposerState()\n  return <TextInput value={input} onChangeText={setInput} />\n}\n```\n\n**Correct (generic interface enables dependency injection):**\n\n```tsx\n// Define a GENERIC interface that any provider can implement\ninterface ComposerState {\n  input: string\n  attachments: Attachment[]\n  isSubmitting: boolean\n}\n\ninterface ComposerActions {\n  update: (updater: (state: ComposerState) => ComposerState) => void\n  submit: () => void\n}\n\ninterface ComposerMeta {\n  inputRef: React.RefObject<TextInput>\n}\n\ninterface ComposerContextValue {\n  state: ComposerState\n  actions: ComposerActions\n  meta: ComposerMeta\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null)\n```\n\n**UI components consume the interface, not the implementation:**\n\n```tsx\nfunction ComposerInput() {\n  const {\n    state,\n    actions: { update },\n    meta,\n  } = use(ComposerContext)\n\n  // This component works with ANY provider that implements the interface\n  return (\n    <TextInput\n      ref={meta.inputRef}\n      value={state.input}\n      onChangeText={(text) => update((s) => ({ ...s, input: text }))}\n    />\n  )\n}\n```\n\n**Different providers implement the same interface:**\n\n```tsx\n// Provider A: Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const inputRef = useRef(null)\n  const submit = useForwardMessage()\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update: setState, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n\n// Provider B: Global synced state for channels\nfunction ChannelProvider({ channelId, children }: Props) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <ComposerContext\n      value={{\n        state,\n        actions: { update, submit },\n        meta: { inputRef },\n      }}\n    >\n      {children}\n    </ComposerContext>\n  )\n}\n```\n\n**The same composed UI works with both:**\n\n```tsx\n// Works with ForwardMessageProvider (local state)\n<ForwardMessageProvider>\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ForwardMessageProvider>\n\n// Works with ChannelProvider (global synced state)\n<ChannelProvider channelId=\"abc\">\n  <Composer.Frame>\n    <Composer.Input />\n    <Composer.Submit />\n  </Composer.Frame>\n</ChannelProvider>\n```\n\n**Custom UI outside the component can access state and actions:**\n\nThe provider boundary is what matters—not the visual nesting. Components that\nneed shared state don't have to be inside the `Composer.Frame`. They just need\nto be within the provider.\n\n```tsx\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        {/* The composer UI */}\n        <Composer.Frame>\n          <Composer.Input placeholder=\"Add a message, if you'd like.\" />\n          <Composer.Footer>\n            <Composer.Formatting />\n            <Composer.Emojis />\n          </Composer.Footer>\n        </Composer.Frame>\n\n        {/* Custom UI OUTSIDE the composer, but INSIDE the provider */}\n        <MessagePreview />\n\n        {/* Actions at the bottom of the dialog */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton />\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\n// This button lives OUTSIDE Composer.Frame but can still submit based on its context!\nfunction ForwardButton() {\n  const {\n    actions: { submit },\n  } = use(ComposerContext)\n  return <Button onPress={submit}>Forward</Button>\n}\n\n// This preview lives OUTSIDE Composer.Frame but can read composer's state!\nfunction MessagePreview() {\n  const { state } = use(ComposerContext)\n  return <Preview message={state.input} attachments={state.attachments} />\n}\n```\n\nThe `ForwardButton` and `MessagePreview` are not visually inside the composer\nbox, but they can still access its state and actions. This is the power of\nlifting state into providers.\n\nThe UI is reusable bits you compose together. The state is dependency-injected\nby the provider. Swap the provider, keep the UI.\n\n<!-- File: skills/composition-patterns/rules/state-decouple-implementation.md (Tokens: ~579 | Category: anthropic-skill) -->\n---\ntitle: Decouple State Management from UI\nimpact: MEDIUM\nimpactDescription: enables swapping state implementations without changing UI\ntags: composition, state, architecture\n---\n\n## Decouple State Management from UI\n\nThe provider component should be the only place that knows how state is managed.\nUI components consume the context interface—they don't know if state comes from\nuseState, Zustand, or a server sync.\n\n**Incorrect (UI coupled to state implementation):**\n\n```tsx\nfunction ChannelComposer({ channelId }: { channelId: string }) {\n  // UI component knows about global state implementation\n  const state = useGlobalChannelState(channelId)\n  const { submit, updateInput } = useChannelSync(channelId)\n\n  return (\n    <Composer.Frame>\n      <Composer.Input\n        value={state.input}\n        onChange={(text) => sync.updateInput(text)}\n      />\n      <Composer.Submit onPress={() => sync.submit()} />\n    </Composer.Frame>\n  )\n}\n```\n\n**Correct (state management isolated in provider):**\n\n```tsx\n// Provider handles all state management details\nfunction ChannelProvider({\n  channelId,\n  children,\n}: {\n  channelId: string\n  children: React.ReactNode\n}) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update, submit }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// UI component only knows about the context interface\nfunction ChannelComposer() {\n  return (\n    <Composer.Frame>\n      <Composer.Header />\n      <Composer.Input />\n      <Composer.Footer>\n        <Composer.Submit />\n      </Composer.Footer>\n    </Composer.Frame>\n  )\n}\n\n// Usage\nfunction Channel({ channelId }: { channelId: string }) {\n  return (\n    <ChannelProvider channelId={channelId}>\n      <ChannelComposer />\n    </ChannelProvider>\n  )\n}\n```\n\n**Different providers, same UI:**\n\n```tsx\n// Local state for ephemeral forms\nfunction ForwardMessageProvider({ children }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\n// Global synced state for channels\nfunction ChannelProvider({ channelId, children }) {\n  const { state, update, submit } = useGlobalChannel(channelId)\n\n  return (\n    <Composer.Provider state={state} actions={{ update, submit }}>\n      {children}\n    </Composer.Provider>\n  )\n}\n```\n\nThe same `Composer.Input` component works with both providers because it only\ndepends on the context interface, not the implementation.\n\n<!-- File: skills/composition-patterns/rules/state-lift-state.md (Tokens: ~686 | Category: anthropic-skill) -->\n---\ntitle: Lift State into Provider Components\nimpact: HIGH\nimpactDescription: enables state sharing outside component boundaries\ntags: composition, state, context, providers\n---\n\n## Lift State into Provider Components\n\nMove state management into dedicated provider components. This allows sibling\ncomponents outside the main UI to access and modify state without prop drilling\nor awkward refs.\n\n**Incorrect (state trapped inside component):**\n\n```tsx\nfunction ForwardMessageComposer() {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n\n  return (\n    <Composer.Frame>\n      <Composer.Input />\n      <Composer.Footer />\n    </Composer.Frame>\n  )\n}\n\n// Problem: How does this button access composer state?\nfunction ForwardMessageDialog() {\n  return (\n    <Dialog>\n      <ForwardMessageComposer />\n      <MessagePreview /> {/* Needs composer state */}\n      <DialogActions>\n        <CancelButton />\n        <ForwardButton /> {/* Needs to call submit */}\n      </DialogActions>\n    </Dialog>\n  )\n}\n```\n\n**Incorrect (useEffect to sync state up):**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const [input, setInput] = useState('')\n  return (\n    <Dialog>\n      <ForwardMessageComposer onInputChange={setInput} />\n      <MessagePreview input={input} />\n    </Dialog>\n  )\n}\n\nfunction ForwardMessageComposer({ onInputChange }) {\n  const [state, setState] = useState(initialState)\n  useEffect(() => {\n    onInputChange(state.input) // Sync on every change 😬\n  }, [state.input])\n}\n```\n\n**Incorrect (reading state from ref on submit):**\n\n```tsx\nfunction ForwardMessageDialog() {\n  const stateRef = useRef(null)\n  return (\n    <Dialog>\n      <ForwardMessageComposer stateRef={stateRef} />\n      <ForwardButton onPress={() => submit(stateRef.current)} />\n    </Dialog>\n  )\n}\n```\n\n**Correct (state lifted to provider):**\n\n```tsx\nfunction ForwardMessageProvider({ children }: { children: React.ReactNode }) {\n  const [state, setState] = useState(initialState)\n  const forwardMessage = useForwardMessage()\n  const inputRef = useRef(null)\n\n  return (\n    <Composer.Provider\n      state={state}\n      actions={{ update: setState, submit: forwardMessage }}\n      meta={{ inputRef }}\n    >\n      {children}\n    </Composer.Provider>\n  )\n}\n\nfunction ForwardMessageDialog() {\n  return (\n    <ForwardMessageProvider>\n      <Dialog>\n        <ForwardMessageComposer />\n        <MessagePreview /> {/* Custom components can access state and actions */}\n        <DialogActions>\n          <CancelButton />\n          <ForwardButton /> {/* Custom components can access state and actions */}\n        </DialogActions>\n      </Dialog>\n    </ForwardMessageProvider>\n  )\n}\n\nfunction ForwardButton() {\n  const { actions } = use(Composer.Context)\n  return <Button onPress={actions.submit}>Forward</Button>\n}\n```\n\nThe ForwardButton lives outside the Composer.Frame but still has access to the\nsubmit action because it's within the provider. Even though it's a one-off\ncomponent, it can still access the composer's state and actions from outside the\nUI itself.\n\n**Key insight:** Components that need shared state don't have to be visually\nnested inside each other—they just need to be within the same provider.\n\n<!-- File: skills/composition-patterns/SKILL.md (Tokens: ~630 | Category: anthropic-skill) -->\n---\nname: vercel-composition-patterns\ndescription:\n  React composition patterns that scale. Use when refactoring components with\n  boolean prop proliferation, building flexible component libraries, or\n  designing reusable APIs. Triggers on tasks involving compound components,\n  render props, context providers, or component architecture. Includes React 19\n  API changes.\nlicense: MIT\nmetadata:\n  author: vercel\n  version: '1.0.0'\n---\n\n# React Composition Patterns\n\nComposition patterns for building flexible, maintainable React components. Avoid\nboolean prop proliferation by using compound components, lifting state, and\ncomposing internals. These patterns make codebases easier for both humans and AI\nagents to work with as they scale.\n\n## When to Apply\n\nReference these guidelines when:\n\n- Refactoring components with many boolean props\n- Building reusable component libraries\n- Designing flexible component APIs\n- Reviewing component architecture\n- Working with compound components or context providers\n\n## Rule Categories by Priority\n\n| Priority | Category                | Impact | Prefix          |\n| -------- | ----------------------- | ------ | --------------- |\n| 1        | Component Architecture  | HIGH   | `architecture-` |\n| 2        | State Management        | MEDIUM | `state-`        |\n| 3        | Implementation Patterns | MEDIUM | `patterns-`     |\n| 4        | React 19 APIs           | MEDIUM | `react19-`      |\n\n## Quick Reference\n\n### 1. Component Architecture (HIGH)\n\n- `architecture-avoid-boolean-props` - Don't add boolean props to customize\n  behavior; use composition\n- `architecture-compound-components` - Structure complex components with shared\n  context\n\n### 2. State Management (MEDIUM)\n\n- `state-decouple-implementation` - Provider is the only place that knows how\n  state is managed\n- `state-context-interface` - Define generic interface with state, actions, meta\n  for dependency injection\n- `state-lift-state` - Move state into provider components for sibling access\n\n### 3. Implementation Patterns (MEDIUM)\n\n- `patterns-explicit-variants` - Create explicit variant components instead of\n  boolean modes\n- `patterns-children-over-render-props` - Use children for composition instead\n  of renderX props\n\n### 4. React 19 APIs (MEDIUM)\n\n> **⚠️ React 19+ only.** Skip this section if using React 18 or earlier.\n\n- `react19-no-forwardref` - Don't use `forwardRef`; use `use()` instead of `useContext()`\n\n## How to Use\n\nRead individual rule files for detailed explanations and code examples:\n\n```\nrules/architecture-avoid-boolean-props.md\nrules/state-context-interface.md\n```\n\nEach rule file contains:\n\n- Brief explanation of why it matters\n- Incorrect code example with explanation\n- Correct code example with explanation\n- Additional context and references\n\n## Full Compiled Document\n\nFor the complete guide with all rules expanded: `AGENTS.md`\n\n<!-- File: skills/deploy-to-vercel/SKILL.md (Tokens: ~2898 | Category: anthropic-skill) -->\n---\nname: deploy-to-vercel\ndescription: Deploy applications and websites to Vercel. Use when the user requests deployment actions like \"deploy my app\", \"deploy and give me the link\", \"push this live\", or \"create a preview deployment\".\nmetadata:\n  author: vercel\n  version: \"3.0.0\"\n---\n\n# Deploy to Vercel\n\nDeploy any project to Vercel. **Always deploy as preview** (not production) unless the user explicitly asks for production.\n\nThe goal is to get the user into the best long-term setup: their project linked to Vercel with git-push deploys. Every method below tries to move the user closer to that state.\n\n## Step 1: Gather Project State\n\nRun all four checks before deciding which method to use:\n\n```bash\n# 1. Check for a git remote\ngit remote get-url origin 2>/dev/null\n\n# 2. Check if locally linked to a Vercel project (either file means linked)\ncat .vercel/project.json 2>/dev/null || cat .vercel/repo.json 2>/dev/null\n\n# 3. Check if the Vercel CLI is installed and authenticated\nvercel whoami 2>/dev/null\n\n# 4. List available teams (if authenticated)\nvercel teams list --format json 2>/dev/null\n```\n\n### Team selection\n\nIf the user belongs to multiple teams, present all available team slugs as a bulleted list and ask which one to deploy to. Once the user picks a team, proceed immediately to the next step — do not ask for additional confirmation.\n\nPass the team slug via `--scope` on all subsequent CLI commands (`vercel deploy`, `vercel link`, `vercel inspect`, etc.):\n\n```bash\nvercel deploy [path] -y --no-wait --scope <team-slug>\n```\n\nIf the project is already linked (`.vercel/project.json` or `.vercel/repo.json` exists), the `orgId` in those files determines the team — no need to ask again. If there is only one team (or just a personal account), skip the prompt and use it directly.\n\n**About the `.vercel/` directory:** A linked project has either:\n- `.vercel/project.json` — created by `vercel link` (single project linking). Contains `projectId` and `orgId`.\n- `.vercel/repo.json` — created by `vercel link --repo` (repo-based linking). Contains `orgId`, `remoteName`, and a `projects` array mapping directories to Vercel project IDs.\n\nEither file means the project is linked. Check for both.\n\n**Do NOT** use `vercel project inspect`, `vercel ls`, or `vercel link` to detect state in an unlinked directory — without a `.vercel/` config, they will interactively prompt (or with `--yes`, silently link as a side-effect). Only `vercel whoami` is safe to run anywhere.\n\n## Step 2: Choose a Deploy Method\n\n### Linked (`.vercel/` exists) + has git remote → Git Push\n\nThis is the ideal state. The project is linked and has git integration.\n\n1. **Ask the user before pushing.** Never push without explicit approval:\n   ```\n   This project is connected to Vercel via git. I can commit and push to\n   trigger a deployment. Want me to proceed?\n   ```\n\n2. **Commit and push:**\n   ```bash\n   git add .\n   git commit -m \"deploy: <description of changes>\"\n   git push\n   ```\n   Vercel automatically builds from the push. Non-production branches get preview deployments; the production branch (usually `main`) gets a production deployment.\n\n3. **Retrieve the preview URL.** If the CLI is authenticated:\n   ```bash\n   sleep 5\n   vercel ls --format json\n   ```\n   The JSON output has a `deployments` array. Find the latest entry — its `url` field is the preview URL.\n\n   If the CLI is not authenticated, tell the user to check the Vercel dashboard or the commit status checks on their git provider for the preview URL.\n\n---\n\n### Linked (`.vercel/` exists) + no git remote → `vercel deploy`\n\nThe project is linked but there's no git repo. Deploy directly with the CLI.\n\n```bash\nvercel deploy [path] -y --no-wait\n```\n\nUse `--no-wait` so the CLI returns immediately with the deployment URL instead of blocking until the build finishes (builds can take a while). Then check on the deployment status with:\n\n```bash\nvercel inspect <deployment-url>\n```\n\nFor production deploys (only if user explicitly asks):\n```bash\nvercel deploy [path] --prod -y --no-wait\n```\n\n---\n\n### Not linked + CLI is authenticated → Link first, then deploy\n\nThe CLI is working but the project isn't linked yet. This is the opportunity to get the user into the best state.\n\n1. **Ask the user which team to deploy to.** Present the team slugs from Step 1 as a bulleted list. If there's only one team (or just a personal account), skip this step.\n\n2. **Once a team is selected, proceed directly to linking.** Tell the user what will happen but do not ask for separate confirmation:\n   ```\n   Linking this project to <team name> on Vercel. This will create a Vercel\n   project to deploy to and enable automatic deployments on future git pushes.\n   ```\n\n3. **If a git remote exists**, use repo-based linking with the selected team scope:\n   ```bash\n   vercel link --repo --scope <team-slug>\n   ```\n   This reads the git remote URL and matches it to existing Vercel projects that deploy from that repo. It creates `.vercel/repo.json`. This is much more reliable than `vercel link` (without `--repo`), which tries to match by directory name and often fails when the local folder and Vercel project are named differently.\n\n   **If there is no git remote**, fall back to standard linking:\n   ```bash\n   vercel link --scope <team-slug>\n   ```\n   This prompts the user to select or create a project. It creates `.vercel/project.json`.\n\n4. **Then deploy using the best available method:**\n   - If a git remote exists → commit and push (see git push method above)\n   - If no git remote → `vercel deploy [path] -y --no-wait --scope <team-slug>`, then `vercel inspect <url>` to check status\n\n---\n\n### Not linked + CLI not authenticated → Install, auth, link, deploy\n\nThe Vercel CLI isn't set up at all.\n\n1. **Install the CLI (if not already installed):**\n   ```bash\n   npm install -g vercel\n   ```\n\n2. **Authenticate:**\n   ```bash\n   vercel login\n   ```\n   The user completes auth in their browser. If running in a non-interactive environment where login is not possible, skip to the **no-auth fallback** below.\n\n3. **Ask which team to deploy to** — present team slugs from `vercel teams list --format json` as a bulleted list. If only one team / personal account, skip. Once selected, proceed immediately.\n\n4. **Link the project** with the selected team scope (use `--repo` if a git remote exists, plain `vercel link` otherwise):\n   ```bash\n   vercel link --repo --scope <team-slug>   # if git remote exists\n   vercel link --scope <team-slug>          # if no git remote\n   ```\n\n5. **Deploy** using the best available method (git push if remote exists, otherwise `vercel deploy -y --no-wait --scope <team-slug>`, then `vercel inspect <url>` to check status).\n\n---\n\n### No-Auth Fallback — claude.ai sandbox\n\n**When to use:** Last resort when the CLI can't be installed or authenticated in the claude.ai sandbox. This requires no authentication — it returns a **Preview URL** (live site) and a **Claim URL** (transfer to your Vercel account).\n\n```bash\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh [path]\n```\n\n**Arguments:**\n- `path` - Directory to deploy, or a `.tgz` file (defaults to current directory)\n\n**Examples:**\n```bash\n# Deploy current directory\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh\n\n# Deploy specific project\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh /path/to/project\n\n# Deploy existing tarball\nbash /mnt/skills/user/deploy-to-vercel/resources/deploy.sh /path/to/project.tgz\n```\n\nThe script auto-detects the framework from `package.json`, packages the project (excluding `node_modules`, `.git`, `.env`), uploads it, and waits for the build to complete.\n\n**Tell the user:** \"Your deployment is ready at [previewUrl]. Claim it at [claimUrl] to manage your deployment.\"\n\n---\n\n### No-Auth Fallback — Codex sandbox\n\n**When to use:** In the Codex sandbox where the CLI may not be authenticated. Codex runs in a sandboxed environment by default — try the CLI first, and fall back to the deploy script if auth fails.\n\n1. **Check whether the Vercel CLI is installed** (no escalation needed for this check):\n   ```bash\n   command -v vercel\n   ```\n\n2. **If `vercel` is installed**, try deploying with the CLI:\n   ```bash\n   vercel deploy [path] -y --no-wait\n   ```\n\n3. **If `vercel` is not installed, or the CLI fails with \"No existing credentials found\"**, use the fallback script:\n   ```bash\n   skill_dir=\"<path-to-skill>\"\n\n   # Deploy current directory\n   bash \"$skill_dir/resources/deploy-codex.sh\"\n\n   # Deploy specific project\n   bash \"$skill_dir/resources/deploy-codex.sh\" /path/to/project\n\n   # Deploy existing tarball\n   bash \"$skill_dir/resources/deploy-codex.sh\" /path/to/project.tgz\n   ```\n\nThe script handles framework detection, packaging, and deployment. It waits for the build to complete and returns JSON with `previewUrl` and `claimUrl`.\n\n**Tell the user:** \"Your deployment is ready at [previewUrl]. Claim it at [claimUrl] to manage your deployment.\"\n\n**Escalated network access:** Only escalate the actual deploy command if sandboxing blocks the network call (`sandbox_permissions=require_escalated`). Do **not** escalate the `command -v vercel` check.\n\n---\n\n## Agent-Specific Notes\n\n### Claude Code / terminal-based agents\n\nYou have full shell access. Do NOT use the `/mnt/skills/` path. Follow the decision flow above using the CLI directly.\n\nFor the no-auth fallback, run the deploy script from the skill's installed location:\n```bash\nbash ~/.claude/skills/deploy-to-vercel/resources/deploy.sh [path]\n```\nThe path may vary depending on where the user installed the skill.\n\n### Sandboxed environments (claude.ai)\n\nYou likely cannot run `vercel login` or `git push`. Go directly to the **no-auth fallback — claude.ai sandbox**.\n\n### Codex\n\nCodex runs in a sandbox. Check if the CLI is available first, then fall back to the deploy script. Go to the **no-auth fallback — Codex sandbox**.\n\n---\n\n## Output\n\nAlways show the user the deployment URL.\n\n- **Git push:** Use `vercel ls --format json` to find the preview URL. If the CLI isn't authenticated, tell the user to check the Vercel dashboard or commit status checks.\n- **CLI deploy:** Show the URL returned by `vercel deploy --no-wait`. Use `vercel inspect <url>` to check build status and report it to the user.\n- **No-auth fallback:** Show both the preview URL and the claim URL:\n  ```\n  Deployment successful!\n\n  Preview URL: https://my-app-abc123.vercel.app\n  Claim URL:   https://vercel.com/claim-deployment?code=...\n\n  View your site at the Preview URL.\n  To transfer this deployment to your Vercel account, visit the Claim URL.\n  ```\n\n**Do not** curl or fetch the deployed URL to verify it works. Just return the link.\n\n---\n\n## Troubleshooting\n\n### Network Egress Error (claude.ai)\n\nIf deployment fails due to network restrictions on claude.ai, tell the user:\n\n```\nDeployment failed due to network restrictions. To fix this:\n\n1. Go to https://claude.ai/settings/capabilities\n2. Add *.vercel.com to the allowed domains\n3. Try deploying again\n```\n\n### Escalated Network Access (Codex / sandboxed environments)\n\nIf deployment fails due to network issues (timeouts, DNS errors, connection resets) in a sandboxed environment, rerun with escalated permissions (`sandbox_permissions=require_escalated`). Do not escalate the initial CLI availability check — only escalate the actual deploy command.\n\nExample guidance to the user:\n```\nThe deploy needs escalated network access to deploy to Vercel. I can rerun\nthe command with escalated permissions — want me to proceed?\n```\n\n### CLI Auth Failure\n\nIf `vercel login` or `vercel deploy` fails with authentication errors, fall back to the no-auth deploy script (claude.ai or Codex variant, depending on the environment).\n\n<!-- File: skills/react-best-practices/AGENTS.md (Tokens: ~12013 | Category: anthropic-skill) -->\n# React Best Practices\n\n**Version 1.0.0**  \nVercel Engineering  \nJanuary 2026\n\n> **Note:**  \n> This document is mainly for agents and LLMs to follow when maintaining,  \n> generating, or refactoring React and Next.js codebases. Humans  \n> may also find it useful, but guidance here is optimized for automation  \n> and consistency by AI-assisted workflows.\n\n---\n\n## Abstract\n\nComprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\n\n---\n\n## Table of Contents\n\n1. [Eliminating Waterfalls](#1-eliminating-waterfalls) — **CRITICAL**\n   - 1.1 [Check Cheap Conditions Before Async Flags](#11-check-cheap-conditions-before-async-flags)\n   - 1.2 [Defer Await Until Needed](#12-defer-await-until-needed)\n   - 1.3 [Dependency-Based Parallelization](#13-dependency-based-parallelization)\n   - 1.4 [Prevent Waterfall Chains in API Routes](#14-prevent-waterfall-chains-in-api-routes)\n   - 1.5 [Promise.all() for Independent Operations](#15-promiseall-for-independent-operations)\n   - 1.6 [Strategic Suspense Boundaries](#16-strategic-suspense-boundaries)\n2. [Bundle Size Optimization](#2-bundle-size-optimization) — **CRITICAL**\n   - 2.1 [Avoid Barrel File Imports](#21-avoid-barrel-file-imports)\n   - 2.2 [Conditional Module Loading](#22-conditional-module-loading)\n   - 2.3 [Defer Non-Critical Third-Party Libraries](#23-defer-non-critical-third-party-libraries)\n   - 2.4 [Dynamic Imports for Heavy Components](#24-dynamic-imports-for-heavy-components)\n   - 2.5 [Prefer Statically Analyzable Paths](#25-prefer-statically-analyzable-paths)\n   - 2.6 [Preload Based on User Intent](#26-preload-based-on-user-intent)\n3. [Server-Side Performance](#3-server-side-performance) — **HIGH**\n   - 3.1 [Authenticate Server Actions Like API Routes](#31-authenticate-server-actions-like-api-routes)\n   - 3.2 [Avoid Duplicate Serialization in RSC Props](#32-avoid-duplicate-serialization-in-rsc-props)\n   - 3.3 [Avoid Shared Module State for Request Data](#33-avoid-shared-module-state-for-request-data)\n   - 3.4 [Cross-Request LRU Caching](#34-cross-request-lru-caching)\n   - 3.5 [Hoist Static I/O to Module Level](#35-hoist-static-io-to-module-level)\n   - 3.6 [Minimize Serialization at RSC Boundaries](#36-minimize-serialization-at-rsc-boundaries)\n   - 3.7 [Parallel Data Fetching with Component Composition](#37-parallel-data-fetching-with-component-composition)\n   - 3.8 [Parallel Nested Data Fetching](#38-parallel-nested-data-fetching)\n   - 3.9 [Per-Request Deduplication with React.cache()](#39-per-request-deduplication-with-reactcache)\n   - 3.10 [Use after() for Non-Blocking Operations](#310-use-after-for-non-blocking-operations)\n4. [Client-Side Data Fetching](#4-client-side-data-fetching) — **MEDIUM-HIGH**\n   - 4.1 [Deduplicate Global Event Listeners](#41-deduplicate-global-event-listeners)\n   - 4.2 [Use Passive Event Listeners for Scrolling Performance](#42-use-passive-event-listeners-for-scrolling-performance)\n   - 4.3 [Use SWR for Automatic Deduplication](#43-use-swr-for-automatic-deduplication)\n   - 4.4 [Version and Minimize localStorage Data](#44-version-and-minimize-localstorage-data)\n5. [Re-render Optimization](#5-re-render-optimization) — **MEDIUM**\n   - 5.1 [Calculate Derived State During Rendering](#51-calculate-derived-state-during-rendering)\n   - 5.2 [Defer State Reads to Usage Point](#52-defer-state-reads-to-usage-point)\n   - 5.3 [Do not wrap a simple expression with a primitive result type in useMemo](#53-do-not-wrap-a-simple-expression-with-a-primitive-result-type-in-usememo)\n   - 5.4 [Don't Define Components Inside Components](#54-dont-define-components-inside-components)\n   - 5.5 [Extract Default Non-primitive Parameter Value from Memoized Component to Constant](#55-extract-default-non-primitive-parameter-value-from-memoized-component-to-constant)\n   - 5.6 [Extract to Memoized Components](#56-extract-to-memoized-components)\n   - 5.7 [Narrow Effect Dependencies](#57-narrow-effect-dependencies)\n   - 5.8 [Put Interaction Logic in Event Handlers](#58-put-interaction-logic-in-event-handlers)\n   - 5.9 [Split Combined Hook Computations](#59-split-combined-hook-computations)\n   - 5.10 [Subscribe to Derived State](#510-subscribe-to-derived-state)\n   - 5.11 [Use Functional setState Updates](#511-use-functional-setstate-updates)\n   - 5.12 [Use Lazy State Initialization](#512-use-lazy-state-initialization)\n   - 5.13 [Use Transitions for Non-Urgent Updates](#513-use-transitions-for-non-urgent-updates)\n   - 5.14 [Use useDeferredValue for Expensive Derived Renders](#514-use-usedeferredvalue-for-expensive-derived-renders)\n   - 5.15 [Use useRef for Transient Values](#515-use-useref-for-transient-values)\n6. [Rendering Performance](#6-rendering-performance) — **MEDIUM**\n   - 6.1 [Animate SVG Wrapper Instead of SVG Element](#61-animate-svg-wrapper-instead-of-svg-element)\n   - 6.2 [CSS content-visibility for Long Lists](#62-css-content-visibility-for-long-lists)\n   - 6.3 [Hoist Static JSX Elements](#63-hoist-static-jsx-elements)\n   - 6.4 [Optimize SVG Precision](#64-optimize-svg-precision)\n   - 6.5 [Prevent Hydration Mismatch Without Flickering](#65-prevent-hydration-mismatch-without-flickering)\n   - 6.6 [Suppress Expected Hydration Mismatches](#66-suppress-expected-hydration-mismatches)\n   - 6.7 [Use Activity Component for Show/Hide](#67-use-activity-component-for-showhide)\n   - 6.8 [Use defer or async on Script Tags](#68-use-defer-or-async-on-script-tags)\n   - 6.9 [Use Explicit Conditional Rendering](#69-use-explicit-conditional-rendering)\n   - 6.10 [Use React DOM Resource Hints](#610-use-react-dom-resource-hints)\n   - 6.11 [Use useTransition Over Manual Loading States](#611-use-usetransition-over-manual-loading-states)\n7. [JavaScript Performance](#7-javascript-performance) — **LOW-MEDIUM**\n   - 7.1 [Avoid Layout Thrashing](#71-avoid-layout-thrashing)\n   - 7.2 [Build Index Maps for Repeated Lookups](#72-build-index-maps-for-repeated-lookups)\n   - 7.3 [Cache Property Access in Loops](#73-cache-property-access-in-loops)\n   - 7.4 [Cache Repeated Function Calls](#74-cache-repeated-function-calls)\n   - 7.5 [Cache Storage API Calls](#75-cache-storage-api-calls)\n   - 7.6 [Combine Multiple Array Iterations](#76-combine-multiple-array-iterations)\n   - 7.7 [Defer Non-Critical Work with requestIdleCallback](#77-defer-non-critical-work-with-requestidlecallback)\n   - 7.8 [Early Length Check for Array Comparisons](#78-early-length-check-for-array-comparisons)\n   - 7.9 [Early Return from Functions](#79-early-return-from-functions)\n   - 7.10 [Hoist RegExp Creation](#710-hoist-regexp-creation)\n   - 7.11 [Use flatMap to Map and Filter in One Pass](#711-use-flatmap-to-map-and-filter-in-one-pass)\n   - 7.12 [Use Loop for Min/Max Instead of Sort](#712-use-loop-for-minmax-instead-of-sort)\n   - 7.13 [Use Set/Map for O(1) Lookups](#713-use-setmap-for-o1-lookups)\n   - 7.14 [Use toSorted() Instead of sort() for Immutability](#714-use-tosorted-instead-of-sort-for-immutability)\n8. [Advanced Patterns](#8-advanced-patterns) — **LOW**\n   - 8.1 [Do Not Put Effect Events in Dependency Arrays](#81-do-not-put-effect-events-in-dependency-arrays)\n   - 8.2 [Initialize App Once, Not Per Mount](#82-initialize-app-once-not-per-mount)\n   - 8.3 [Store Event Handlers in Refs](#83-store-event-handlers-in-refs)\n   - 8.4 [useEffectEvent for Stable Callback Refs](#84-useeffectevent-for-stable-callback-refs)\n\n---\n\n## 1. Eliminating Waterfalls\n\n**Impact: CRITICAL**\n\nWaterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.\n\n### 1.1 Check Cheap Conditions Before Async Flags\n\n**Impact: HIGH (avoids unnecessary async work when a synchronous guard already fails)**\n\nWhen a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n\nThis is a specialization of [Defer Await Until Needed](./async-defer-await.md) for `flag && cheapCondition` style checks.\n\n**Incorrect:**\n\n```typescript\nconst someFlag = await getFlag()\n\nif (someFlag && someCondition) {\n  // ...\n}\n```\n\n**Correct:**\n\n```typescript\nif (someCondition) {\n  const someFlag = await getFlag()\n  if (someFlag) {\n    // ...\n  }\n}\n```\n\nThis matters when `getFlag` hits the network, a feature-flag service, or `React.cache` / DB work: skipping it when `someCondition` is false removes that cost on the cold path.\n\nKeep the original order if `someCondition` is expensive, depends on the flag, or you must run side effects in a fixed order.\n\n### 1.2 Defer Await Until Needed\n\n**Impact: HIGH (avoids blocking unused code paths)**\n\nMove `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n\n**Incorrect: blocks both branches**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  const userData = await fetchUserData(userId)\n  \n  if (skipProcessing) {\n    // Returns immediately but still waited for userData\n    return { skipped: true }\n  }\n  \n  // Only this branch uses userData\n  return processUserData(userData)\n}\n```\n\n**Correct: only blocks when needed**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  if (skipProcessing) {\n    // Returns immediately without waiting\n    return { skipped: true }\n  }\n  \n  // Fetch only when needed\n  const userData = await fetchUserData(userId)\n  return processUserData(userData)\n}\n```\n\n**Another example: early return optimization**\n\n```typescript\n// Incorrect: always fetches permissions\nasync function updateResource(resourceId: string, userId: string) {\n  const permissions = await fetchPermissions(userId)\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n\n// Correct: fetches only when needed\nasync function updateResource(resourceId: string, userId: string) {\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  const permissions = await fetchPermissions(userId)\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n```\n\nThis optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.\n\nFor `await getFlag()` combined with a cheap synchronous guard (`flag && someCondition`), see [Check Cheap Conditions Before Async Flags](./async-cheap-condition-before-await.md).\n\n### 1.3 Dependency-Based Parallelization\n\n**Impact: CRITICAL (2-10× improvement)**\n\nFor operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n\n**Incorrect: profile waits for config unnecessarily**\n\n```typescript\nconst [user, config] = await Promise.all([\n  fetchUser(),\n  fetchConfig()\n])\nconst profile = await fetchProfile(user.id)\n```\n\n**Correct: config and profile run in parallel**\n\n```typescript\nimport { all } from 'better-all'\n\nconst { user, config, profile } = await all({\n  async user() { return fetchUser() },\n  async config() { return fetchConfig() },\n  async profile() {\n    return fetchProfile((await this.$.user).id)\n  }\n})\n```\n\n**Alternative without extra dependencies:**\n\n```typescript\nconst userPromise = fetchUser()\nconst profilePromise = userPromise.then(user => fetchProfile(user.id))\n\nconst [user, config, profile] = await Promise.all([\n  userPromise,\n  fetchConfig(),\n  profilePromise\n])\n```\n\nWe can also create all the promises first, and do `Promise.all()` at the end.\n\nReference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)\n\n### 1.4 Prevent Waterfall Chains in API Routes\n\n**Impact: CRITICAL (2-10× improvement)**\n\nIn API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n\n**Incorrect: config waits for auth, data waits for both**\n\n```typescript\nexport async function GET(request: Request) {\n  const session = await auth()\n  const config = await fetchConfig()\n  const data = await fetchData(session.user.id)\n  return Response.json({ data, config })\n}\n```\n\n**Correct: auth and config start immediately**\n\n```typescript\nexport async function GET(request: Request) {\n  const sessionPromise = auth()\n  const configPromise = fetchConfig()\n  const session = await sessionPromise\n  const [config, data] = await Promise.all([\n    configPromise,\n    fetchData(session.user.id)\n  ])\n  return Response.json({ data, config })\n}\n```\n\nFor operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).\n\n### 1.5 Promise.all() for Independent Operations\n\n**Impact: CRITICAL (2-10× improvement)**\n\nWhen async operations have no interdependencies, execute them concurrently using `Promise.all()`.\n\n**Incorrect: sequential execution, 3 round trips**\n\n```typescript\nconst user = await fetchUser()\nconst posts = await fetchPosts()\nconst comments = await fetchComments()\n```\n\n**Correct: parallel execution, 1 round trip**\n\n```typescript\nconst [user, posts, comments] = await Promise.all([\n  fetchUser(),\n  fetchPosts(),\n  fetchComments()\n])\n```\n\n### 1.6 Strategic Suspense Boundaries\n\n**Impact: HIGH (faster initial paint)**\n\nInstead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.\n\n**Incorrect: wrapper blocked by data fetching**\n\n```tsx\nasync function Page() {\n  const data = await fetchData() // Blocks entire page\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <DataDisplay data={data} />\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n```\n\nThe entire layout waits for data even though only the middle section needs it.\n\n**Correct: wrapper shows immediately, data streams in**\n\n```tsx\nfunction Page() {\n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <div>\n        <Suspense fallback={<Skeleton />}>\n          <DataDisplay />\n        </Suspense>\n      </div>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nasync function DataDisplay() {\n  const data = await fetchData() // Only blocks this component\n  return <div>{data.content}</div>\n}\n```\n\nSidebar, Header, and Footer render immediately. Only DataDisplay waits for data.\n\n**Alternative: share promise across components**\n\n```tsx\nfunction Page() {\n  // Start fetch immediately, but don't await\n  const dataPromise = fetchData()\n  \n  return (\n    <div>\n      <div>Sidebar</div>\n      <div>Header</div>\n      <Suspense fallback={<Skeleton />}>\n        <DataDisplay dataPromise={dataPromise} />\n        <DataSummary dataPromise={dataPromise} />\n      </Suspense>\n      <div>Footer</div>\n    </div>\n  )\n}\n\nfunction DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Unwraps the promise\n  return <div>{data.content}</div>\n}\n\nfunction DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {\n  const data = use(dataPromise) // Reuses the same promise\n  return <div>{data.summary}</div>\n}\n```\n\nBoth components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.\n\n**When NOT to use this pattern:**\n\n- Critical data needed for layout decisions (affects positioning)\n\n- SEO-critical content above the fold\n\n- Small, fast queries where suspense overhead isn't worth it\n\n- When you want to avoid layout shift (loading → content jump)\n\n**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.\n\n---\n\n## 2. Bundle Size Optimization\n\n**Impact: CRITICAL**\n\nReducing initial bundle size improves Time to Interactive and Largest Contentful Paint.\n\n### 2.1 Avoid Barrel File Imports\n\n**Impact: CRITICAL (200-800ms import cost, slow builds)**\n\nImport directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).\n\nPopular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.\n\n**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.\n\n**Incorrect: imports entire library**\n\n```tsx\nimport { Check, X, Menu } from 'lucide-react'\n// Loads 1,583 modules, takes ~2.8s extra in dev\n// Runtime cost: 200-800ms on every cold start\n\nimport { Button, TextField } from '@mui/material'\n// Loads 2,225 modules, takes ~4.2s extra in dev\n```\n\n**Correct - Next.js 13.5+ (recommended):**\n\n```tsx\n// Keep the standard imports - Next.js transforms them to direct imports\nimport { Check, X, Menu } from 'lucide-react'\n// Full TypeScript support, no manual path wrangling\n```\n\nThis is the recommended approach because it preserves TypeScript type safety and editor autocompletion while still eliminating the barrel import cost.\n\n**Correct - Direct imports (non-Next.js projects):**\n\n```tsx\nimport Button from '@mui/material/Button'\nimport TextField from '@mui/material/TextField'\n// Loads only what you use\n```\n\n> **TypeScript warning:** Some libraries (notably `lucide-react`) don't ship `.d.ts` files for their deep import paths. Importing from `lucide-react/dist/esm/icons/check` resolves to an implicit `any` type, causing errors under `strict` or `noImplicitAny`. Prefer `optimizePackageImports` when available, or verify the library exports types for its subpaths before using direct imports.\n\nThese optimizations provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.\n\nLibraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.\n\nReference: [https://vercel.com/blog/how-we-optimized-package-imports-in-next-js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)\n\n### 2.2 Conditional Module Loading\n\n**Impact: HIGH (loads large data only when needed)**\n\nLoad large data or modules only when a feature is activated.\n\n**Example: lazy-load animation frames**\n\n```tsx\nfunction AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {\n  const [frames, setFrames] = useState<Frame[] | null>(null)\n\n  useEffect(() => {\n    if (enabled && !frames && typeof window !== 'undefined') {\n      import('./animation-frames.js')\n        .then(mod => setFrames(mod.frames))\n        .catch(() => setEnabled(false))\n    }\n  }, [enabled, frames, setEnabled])\n\n  if (!frames) return <Skeleton />\n  return <Canvas frames={frames} />\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.\n\n### 2.3 Defer Non-Critical Third-Party Libraries\n\n**Impact: MEDIUM (loads after hydration)**\n\nAnalytics, logging, and error tracking don't block user interaction. Load them after hydration.\n\n**Incorrect: blocks initial bundle**\n\n```tsx\nimport { Analytics } from '@vercel/analytics/react'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n**Correct: loads after hydration**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst Analytics = dynamic(\n  () => import('@vercel/analytics/react').then(m => m.Analytics),\n  { ssr: false }\n)\n\nexport default function RootLayout({ children }) {\n  return (\n    <html>\n      <body>\n        {children}\n        <Analytics />\n      </body>\n    </html>\n  )\n}\n```\n\n### 2.4 Dynamic Imports for Heavy Components\n\n**Impact: CRITICAL (directly affects TTI and LCP)**\n\nUse `next/dynamic` to lazy-load large components not needed on initial render.\n\n**Incorrect: Monaco bundles with main chunk ~300KB**\n\n```tsx\nimport { MonacoEditor } from './monaco-editor'\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n**Correct: Monaco loads on demand**\n\n```tsx\nimport dynamic from 'next/dynamic'\n\nconst MonacoEditor = dynamic(\n  () => import('./monaco-editor').then(m => m.MonacoEditor),\n  { ssr: false }\n)\n\nfunction CodePanel({ code }: { code: string }) {\n  return <MonacoEditor value={code} />\n}\n```\n\n### 2.5 Prefer Statically Analyzable Paths\n\n**Impact: HIGH (avoids accidental broad bundles and file traces)**\n\nBuild tools work best when import and file-system paths are obvious at build time. If you hide the real path inside a variable or compose it too dynamically, the tool either has to include a broad set of possible files, warn that it cannot analyze the import, or widen file tracing to stay safe.\n\nPrefer explicit maps or literal paths so the set of reachable files stays narrow and predictable. This is the same rule whether you are choosing modules with `import()` or reading files in server/build code.\n\nWhen analysis becomes too broad, the cost is real:\n\n- Larger server bundles\n\n- Slower builds\n\n- Worse cold starts\n\n- More memory use\n\n**Incorrect: the bundler cannot tell what may be imported**\n\n```ts\nconst PAGE_MODULES = {\n  home: './pages/home',\n  settings: './pages/settings',\n} as const\n\nconst Page = await import(PAGE_MODULES[pageName])\n```\n\n**Correct: use an explicit map of allowed modules**\n\n```ts\nconst PAGE_MODULES = {\n  home: () => import('./pages/home'),\n  settings: () => import('./pages/settings'),\n} as const\n\nconst Page = await PAGE_MODULES[pageName]()\n```\n\n**Incorrect: a 2-value enum still hides the final path from static analysis**\n\n```ts\nconst baseDir = path.join(process.cwd(), 'content/' + contentKind)\n```\n\n**Correct: make each final path literal at the callsite**\n\n```ts\nconst baseDir =\n  kind === ContentKind.Blog\n    ? path.join(process.cwd(), 'content/blog')\n    : path.join(process.cwd(), 'content/docs')\n```\n\nIn Next.js server code, this matters for output file tracing too. `path.join(process.cwd(), someVar)` can widen the traced file set because Next.js statically analyze `import`, `require`, and `fs` usage.\n\nReference: [https://nextjs.org/docs/app/api-reference/config/next-config-js/output](https://nextjs.org/docs/app/api-reference/config/next-config-js/output), [https://nextjs.org/learn/seo/dynamic-imports](https://nextjs.org/learn/seo/dynamic-imports), [https://vite.dev/guide/features.html](https://vite.dev/guide/features.html), [https://esbuild.github.io/api/](https://esbuild.github.io/api/), [https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars](https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars), [https://webpack.js.org/guides/dependency-management/](https://webpack.js.org/guides/dependency-management/)\n\n### 2.6 Preload Based on User Intent\n\n**Impact: MEDIUM (reduces perceived latency)**\n\nPreload heavy bundles before they're needed to reduce perceived latency.\n\n**Example: preload on hover/focus**\n\n```tsx\nfunction EditorButton({ onClick }: { onClick: () => void }) {\n  const preload = () => {\n    if (typeof window !== 'undefined') {\n      void import('./monaco-editor')\n    }\n  }\n\n  return (\n    <button\n      onMouseEnter={preload}\n      onFocus={preload}\n      onClick={onClick}\n    >\n      Open Editor\n    </button>\n  )\n}\n```\n\n**Example: preload when feature flag is enabled**\n\n```tsx\nfunction FlagsProvider({ children, flags }: Props) {\n  useEffect(() => {\n    if (flags.editorEnabled && typeof window !== 'undefined') {\n      void import('./monaco-editor').then(mod => mod.init())\n    }\n  }, [flags.editorEnabled])\n\n  return <FlagsContext.Provider value={flags}>\n    {children}\n  </FlagsContext.Provider>\n}\n```\n\nThe `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.\n\n---\n\n## 3. Server-Side Performance\n\n**Impact: HIGH**\n\nOptimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.\n\n### 3.1 Authenticate Server Actions Like API Routes\n\n**Impact: CRITICAL (prevents unauthorized access to server mutations)**\n\nServer Actions (functions with `\"use server\"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly.\n\nNext.js documentation explicitly states: \"Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation.\"\n\n**Incorrect: no authentication check**\n\n```typescript\n'use server'\n\nexport async function deleteUser(userId: string) {\n  // Anyone can call this! No auth check\n  await db.user.delete({ where: { id: userId } })\n  return { success: true }\n}\n```\n\n**Correct: authentication inside the action**\n\n```typescript\n'use server'\n\nimport { verifySession } from '@/lib/auth'\nimport { unauthorized } from '@/lib/errors'\n\nexport async function deleteUser(userId: string) {\n  // Always check auth inside the action\n  const session = await verifySession()\n  \n  if (!session) {\n    throw unauthorized('Must be logged in')\n  }\n  \n  // Check authorization too\n  if (session.user.role !== 'admin' && session.user.id !== userId) {\n    throw unauthorized('Cannot delete other users')\n  }\n  \n  await db.user.delete({ where: { id: userId } })\n  return { success: true }\n}\n```\n\n**With input validation:**\n\n```typescript\n'use server'\n\nimport { verifySession } from '@/lib/auth'\nimport { z } from 'zod'\n\nconst updateProfileSchema = z.object({\n  userId: z.string().uuid(),\n  name: z.string().min(1).max(100),\n  email: z.string().email()\n})\n\nexport async function updateProfile(data: unknown) {\n  // Validate input first\n  const validated = updateProfileSchema.parse(data)\n  \n  // Then authenticate\n  const session = await verifySession()\n  if (!session) {\n    throw new Error('Unauthorized')\n  }\n  \n  // Then authorize\n  if (session.user.id !== validated.userId) {\n    throw new Error('Can only update own profile')\n  }\n  \n  // Finally perform the mutation\n  await db.user.update({\n    where: { id: validated.userId },\n    data: {\n      name: validated.name,\n      email: validated.email\n    }\n  })\n  \n  return { success: true }\n}\n```\n\nReference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)\n\n### 3.2 Avoid Duplicate Serialization in RSC Props\n\n**Impact: LOW (reduces network payload by avoiding duplicate serialization)**\n\nRSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again. Do transformations (`.toSorted()`, `.filter()`, `.map()`) in client, not server.\n\n**Incorrect: duplicates array**\n\n```tsx\n// RSC: sends 6 strings (2 arrays × 3 items)\n<ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />\n```\n\n**Correct: sends 3 strings**\n\n```tsx\n// RSC: send once\n<ClientList usernames={usernames} />\n\n// Client: transform there\n'use client'\nconst sorted = useMemo(() => [...usernames].sort(), [usernames])\n```\n\n**Nested deduplication behavior:**\n\n```tsx\n// string[] - duplicates everything\nusernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings\n\n// object[] - duplicates array structure only\nusers={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4)\n```\n\nDeduplication works recursively. Impact varies by data type:\n\n- `string[]`, `number[]`, `boolean[]`: **HIGH impact** - array + all primitives fully duplicated\n\n- `object[]`: **LOW impact** - array duplicated, but nested objects deduplicated by reference\n\n**Operations breaking deduplication: create new references**\n\n- Arrays: `.toSorted()`, `.filter()`, `.map()`, `.slice()`, `[...arr]`\n\n- Objects: `{...obj}`, `Object.assign()`, `structuredClone()`, `JSON.parse(JSON.stringify())`\n\n**More examples:**\n\n```tsx\n// ❌ Bad\n<C users={users} active={users.filter(u => u.active)} />\n<C product={product} productName={product.name} />\n\n// ✅ Good\n<C users={users} />\n<C product={product} />\n// Do filtering/destructuring in client\n```\n\n**Exception:** Pass derived data when transformation is expensive or client doesn't need original.\n\n### 3.3 Avoid Shared Module State for Request Data\n\n**Impact: HIGH (prevents concurrency bugs and request data leaks)**\n\nFor React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race conditions, cross-request contamination, and security bugs where one user's data appears in another user's response.\n\nTreat module scope on the server as process-wide shared memory, not request-local state.\n\n**Incorrect: request data leaks across concurrent renders**\n\n```tsx\nlet currentUser: User | null = null\n\nexport default async function Page() {\n  currentUser = await auth()\n  return <Dashboard />\n}\n\nasync function Dashboard() {\n  return <div>{currentUser?.name}</div>\n}\n```\n\nIf two requests overlap, request A can set `currentUser`, then request B overwrites it before request A finishes rendering `Dashboard`.\n\n**Correct: keep request data local to the render tree**\n\n```tsx\nexport default async function Page() {\n  const user = await auth()\n  return <Dashboard user={user} />\n}\n\nfunction Dashboard({ user }: { user: User | null }) {\n  return <div>{user?.name}</div>\n}\n```\n\nSafe exceptions:\n\n- Immutable static assets or config loaded once at module scope\n\n- Shared caches intentionally designed for cross-request reuse and keyed correctly\n\n- Process-wide singletons that do not store request- or user-specific mutable data\n\nFor static assets and config, see [Hoist Static I/O to Module Level](./server-hoist-static-io.md).\n\n### 3.4 Cross-Request LRU Caching\n\n**Impact: HIGH (caches across requests)**\n\n`React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.\n\n**Implementation:**\n\n```typescript\nimport { LRUCache } from 'lru-cache'\n\nconst cache = new LRUCache<string, any>({\n  max: 1000,\n  ttl: 5 * 60 * 1000  // 5 minutes\n})\n\nexport async function getUser(id: string) {\n  const cached = cache.get(id)\n  if (cached) return cached\n\n  const user = await db.user.findUnique({ where: { id } })\n  cache.set(id, user)\n  return user\n}\n\n// Request 1: DB query, result cached\n// Request 2: cache hit, no DB query\n```\n\nUse when sequential user actions hit multiple endpoints needing the same data within seconds.\n\n**With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis.\n\n**In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching.\n\nReference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)\n\n### 3.5 Hoist Static I/O to Module Level\n\n**Impact: HIGH (avoids repeated file/network I/O per request)**\n\nWhen loading static assets (fonts, logos, images, config files) in route handlers or server functions, hoist the I/O operation to module level. Module-level code runs once when the module is first imported, not on every request. This eliminates redundant file system reads or network fetches that would otherwise run on every invocation.\n\n**Incorrect: reads font file on every request**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\n\nexport async function GET(request: Request) {\n  // Runs on EVERY request - expensive!\n  const fontData = await fetch(\n    new URL('./fonts/Inter.ttf', import.meta.url)\n  ).then(res => res.arrayBuffer())\n\n  const logoData = await fetch(\n    new URL('./images/logo.png', import.meta.url)\n  ).then(res => res.arrayBuffer())\n\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logoData} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: fontData }] }\n  )\n}\n```\n\n**Correct: loads once at module initialization**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\n\n// Module-level: runs ONCE when module is first imported\nconst fontData = fetch(\n  new URL('./fonts/Inter.ttf', import.meta.url)\n).then(res => res.arrayBuffer())\n\nconst logoData = fetch(\n  new URL('./images/logo.png', import.meta.url)\n).then(res => res.arrayBuffer())\n\nexport async function GET(request: Request) {\n  // Await the already-started promises\n  const [font, logo] = await Promise.all([fontData, logoData])\n\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logo} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: font }] }\n  )\n}\n```\n\n**Correct: synchronous fs at module level**\n\n```typescript\n// app/api/og/route.tsx\nimport { ImageResponse } from 'next/og'\nimport { readFileSync } from 'fs'\nimport { join } from 'path'\n\n// Synchronous read at module level - blocks only during module init\nconst fontData = readFileSync(\n  join(process.cwd(), 'public/fonts/Inter.ttf')\n)\n\nconst logoData = readFileSync(\n  join(process.cwd(), 'public/images/logo.png')\n)\n\nexport async function GET(request: Request) {\n  return new ImageResponse(\n    <div style={{ fontFamily: 'Inter' }}>\n      <img src={logoData} />\n      Hello World\n    </div>,\n    { fonts: [{ name: 'Inter', data: fontData }] }\n  )\n}\n```\n\n**Incorrect: reads config on every call**\n\n```typescript\nimport fs from 'node:fs/promises'\n\nexport async function processRequest(data: Data) {\n  const config = JSON.parse(\n    await fs.readFile('./config.json', 'utf-8')\n  )\n  const template = await fs.readFile('./template.html', 'utf-8')\n\n  return render(template, data, config)\n}\n```\n\n**Correct: hoists config and template to module level**\n\n```typescript\nimport fs from 'node:fs/promises'\n\nconst configPromise = fs\n  .readFile('./config.json', 'utf-8')\n  .then(JSON.parse)\nconst templatePromise = fs.readFile('./template.html', 'utf-8')\n\nexport async function processRequest(data: Data) {\n  const [config, template] = await Promise.all([\n    configPromise,\n    templatePromise,\n  ])\n\n  return render(template, data, config)\n}\n```\n\nWhen to use this pattern:\n\n- Loading fonts for OG image generation\n\n- Loading static logos, icons, or watermarks\n\n- Reading configuration files that don't change at runtime\n\n- Loading email templates or other static templates\n\n- Any static asset that's the same across all requests\n\nWhen not to use this pattern:\n\n- Assets that vary per request or user\n\n- Files that may change during runtime (use caching with TTL instead)\n\n- Large files that would consume too much memory if kept loaded\n\n- Sensitive data that shouldn't persist in memory\n\nWith Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute), module-level caching is especially effective because multiple concurrent requests share the same function instance. The static assets stay loaded in memory across requests without cold start penalties.\n\nIn traditional serverless, each cold start re-executes module-level code, but subsequent warm invocations reuse the loaded assets until the instance is recycled.\n\n### 3.6 Minimize Serialization at RSC Boundaries\n\n**Impact: HIGH (reduces data transfer size)**\n\nThe React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.\n\n**Incorrect: serializes all 50 fields**\n\n```tsx\nasync function Page() {\n  const user = await fetchUser()  // 50 fields\n  return <Profile user={user} />\n}\n\n'use client'\nfunction Profile({ user }: { user: User }) {\n  return <div>{user.name}</div>  // uses 1 field\n}\n```\n\n**Correct: serializes only 1 field**\n\n```tsx\nasync function Page() {\n  const user = await fetchUser()\n  return <Profile name={user.name} />\n}\n\n'use client'\nfunction Profile({ name }: { name: string }) {\n  return <div>{name}</div>\n}\n```\n\n### 3.7 Parallel Data Fetching with Component Composition\n\n**Impact: CRITICAL (eliminates server-side waterfalls)**\n\nReact Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.\n\n**Incorrect: Sidebar waits for Page's fetch to complete**\n\n```tsx\nexport default async function Page() {\n  const header = await fetchHeader()\n  return (\n    <div>\n      <div>{header}</div>\n      <Sidebar />\n    </div>\n  )\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n```\n\n**Correct: both fetch simultaneously**\n\n```tsx\nasync function Header() {\n  const data = await fetchHeader()\n  return <div>{data}</div>\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n\nexport default function Page() {\n  return (\n    <div>\n      <Header />\n      <Sidebar />\n    </div>\n  )\n}\n```\n\n**Alternative with children prop:**\n\n```tsx\nasync function Header() {\n  const data = await fetchHeader()\n  return <div>{data}</div>\n}\n\nasync function Sidebar() {\n  const items = await fetchSidebarItems()\n  return <nav>{items.map(renderItem)}</nav>\n}\n\nfunction Layout({ children }: { children: ReactNode }) {\n  return (\n    <div>\n      <Header />\n      {children}\n    </div>\n  )\n}\n\nexport default function Page() {\n  return (\n    <Layout>\n      <Sidebar />\n    </Layout>\n  )\n}\n```\n\n### 3.8 Parallel Nested Data Fetching\n\n**Impact: CRITICAL (eliminates server-side waterfalls)**\n\nWhen fetching nested data in parallel, chain dependent fetches within each item's promise so a slow item doesn't block the rest.\n\n**Incorrect: a single slow item blocks all nested fetches**\n\n```tsx\nconst chats = await Promise.all(\n  chatIds.map(id => getChat(id))\n)\n\nconst chatAuthors = await Promise.all(\n  chats.map(chat => getUser(chat.author))\n)\n```\n\nIf one `getChat(id)` out of 100 is extremely slow, the authors of the other 99 chats can't start loading even though their data is ready.\n\n**Correct: each item chains its own nested fetch**\n\n```tsx\nconst chatAuthors = await Promise.all(\n  chatIds.map(id => getChat(id).then(chat => getUser(chat.author)))\n)\n```\n\nEach item independently chains `getChat` → `getUser`, so a slow chat doesn't block author fetches for the others.\n\n### 3.9 Per-Request Deduplication with React.cache()\n\n**Impact: MEDIUM (deduplicates within request)**\n\nUse `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.\n\n**Usage:**\n\n```typescript\nimport { cache } from 'react'\n\nexport const getCurrentUser = cache(async () => {\n  const session = await auth()\n  if (!session?.user?.id) return null\n  return await db.user.findUnique({\n    where: { id: session.user.id }\n  })\n})\n```\n\nWithin a single request, multiple calls to `getCurrentUser()` execute the query only once.\n\n**Avoid inline objects as arguments:**\n\n`React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits.\n\n**Incorrect: always cache miss**\n\n```typescript\nconst getUser = cache(async (params: { uid: number }) => {\n  return await db.user.findUnique({ where: { id: params.uid } })\n})\n\n// Each call creates new object, never hits cache\ngetUser({ uid: 1 })\ngetUser({ uid: 1 })  // Cache miss, runs query again\n```\n\n**Correct: cache hit**\n\n```typescript\nconst params = { uid: 1 }\ngetUser(params)  // Query runs\ngetUser(params)  // Cache hit (same reference)\n```\n\nIf you must pass objects, pass the same reference:\n\n**Next.js-Specific Note:**\n\nIn Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks:\n\n- Database queries (Prisma, Drizzle, etc.)\n\n- Heavy computations\n\n- Authentication checks\n\n- File system operations\n\n- Any non-fetch async work\n\nUse `React.cache()` to deduplicate these operations across your component tree.\n\nReference: [https://react.dev/reference/react/cache](https://react.dev/reference/react/cache)\n\n### 3.10 Use after() for Non-Blocking Operations\n\n**Impact: MEDIUM (faster response times)**\n\nUse Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.\n\n**Incorrect: blocks response**\n\n```tsx\nimport { logUserAction } from '@/app/utils'\n\nexport async function POST(request: Request) {\n  // Perform mutation\n  await updateDatabase(request)\n  \n  // Logging blocks the response\n  const userAgent = request.headers.get('user-agent') || 'unknown'\n  await logUserAction({ userAgent })\n  \n  return new Response(JSON.stringify({ status: 'success' }), {\n    status: 200,\n    headers: { 'Content-Type': 'application/json' }\n  })\n}\n```\n\n**Correct: non-blocking**\n\n```tsx\nimport { after } from 'next/server'\nimport { headers, cookies } from 'next/headers'\nimport { logUserAction } from '@/app/utils'\n\nexport async function POST(request: Request) {\n  // Perform mutation\n  await updateDatabase(request)\n  \n  // Log after response is sent\n  after(async () => {\n    const userAgent = (await headers()).get('user-agent') || 'unknown'\n    const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous'\n    \n    logUserAction({ sessionCookie, userAgent })\n  })\n  \n  return new Response(JSON.stringify({ status: 'success' }), {\n    status: 200,\n    headers: { 'Content-Type': 'application/json' }\n  })\n}\n```\n\nThe response is sent immediately while logging happens in the background.\n\n**Common use cases:**\n\n- Analytics tracking\n\n- Audit logging\n\n- Sending notifications\n\n- Cache invalidation\n\n- Cleanup tasks\n\n**Important notes:**\n\n- `after()` runs even if the response fails or redirects\n\n- Works in Server Actions, Route Handlers, and Server Components\n\nReference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)\n\n---\n\n## 4. Client-Side Data Fetching\n\n**Impact: MEDIUM-HIGH**\n\nAutomatic deduplication and efficient data fetching patterns reduce redundant network requests.\n\n### 4.1 Deduplicate Global Event Listeners\n\n**Impact: LOW (single listener for N components)**\n\nUse `useSWRSubscription()` to share global event listeners across component instances.\n\n**Incorrect: N instances = N listeners**\n\n```tsx\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  useEffect(() => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && e.key === key) {\n        callback()\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  }, [key, callback])\n}\n```\n\nWhen using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.\n\n**Correct: N instances = 1 listener**\n\n```tsx\nimport useSWRSubscription from 'swr/subscription'\n\n// Module-level Map to track callbacks per key\nconst keyCallbacks = new Map<string, Set<() => void>>()\n\nfunction useKeyboardShortcut(key: string, callback: () => void) {\n  // Register this callback in the Map\n  useEffect(() => {\n    if (!keyCallbacks.has(key)) {\n      keyCallbacks.set(key, new Set())\n    }\n    keyCallbacks.get(key)!.add(callback)\n\n    return () => {\n      const set = keyCallbacks.get(key)\n      if (set) {\n        set.delete(callback)\n        if (set.size === 0) {\n          keyCallbacks.delete(key)\n        }\n      }\n    }\n  }, [key, callback])\n\n  useSWRSubscription('global-keydown', () => {\n    const handler = (e: KeyboardEvent) => {\n      if (e.metaKey && keyCallbacks.has(e.key)) {\n        keyCallbacks.get(e.key)!.forEach(cb => cb())\n      }\n    }\n    window.addEventListener('keydown', handler)\n    return () => window.removeEventListener('keydown', handler)\n  })\n}\n\nfunction Profile() {\n  // Multiple shortcuts will share the same listener\n  useKeyboardShortcut('p', () => { /* ... */ }) \n  useKeyboardShortcut('k', () => { /* ... */ })\n  // ...\n}\n```\n\n### 4.2 Use Passive Event Listeners for Scrolling Performance\n\n**Impact: MEDIUM (eliminates scroll delay caused by event listeners)**\n\nAdd `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.\n\n**Incorrect:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch)\n  document.addEventListener('wheel', handleWheel)\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Correct:**\n\n```typescript\nuseEffect(() => {\n  const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)\n  const handleWheel = (e: WheelEvent) => console.log(e.deltaY)\n  \n  document.addEventListener('touchstart', handleTouch, { passive: true })\n  document.addEventListener('wheel', handleWheel, { passive: true })\n  \n  return () => {\n    document.removeEventListener('touchstart', handleTouch)\n    document.removeEventListener('wheel', handleWheel)\n  }\n}, [])\n```\n\n**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.\n\n**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.\n\n### 4.3 Use SWR for Automatic Deduplication\n\n**Impact: MEDIUM-HIGH (automatic deduplication)**\n\nSWR enables request deduplication, caching, and revalidation across component instances.\n\n**Incorrect: no deduplication, each instance fetches**\n\n```tsx\nfunction UserList() {\n  const [users, setUsers] = useState([])\n  useEffect(() => {\n    fetch('/api/users')\n      .then(r => r.json())\n      .then(setUsers)\n  }, [])\n}\n```\n\n**Correct: multiple instances share one request**\n\n```tsx\nimport useSWR from 'swr'\n\nfunction UserList() {\n  const { data: users } = useSWR('/api/users', fetcher)\n}\n```\n\n**For immutable data:**\n\n```tsx\nimport { useImmutableSWR } from '@/lib/swr'\n\nfunction StaticContent() {\n  const { data } = useImmutableSWR('/api/config', fetcher)\n}\n```\n\n**For mutations:**\n\n```tsx\nimport { useSWRMutation } from 'swr/mutation'\n\nfunction UpdateButton() {\n  const { trigger } = useSWRMutation('/api/user', updateUser)\n  return <button onClick={() => trigger()}>Update</button>\n}\n```\n\nReference: [https://swr.vercel.app](https://swr.vercel.app)\n\n### 4.4 Version and Minimize localStorage Data\n\n**Impact: MEDIUM (prevents schema conflicts, reduces storage size)**\n\nAdd version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.\n\n**Incorrect:**\n\n```typescript\n// No version, stores everything, no error handling\nlocalStorage.setItem('userConfig', JSON.stringify(fullUserObject))\nconst data = localStorage.getItem('userConfig')\n```\n\n**Correct:**\n\n```typescript\nconst VERSION = 'v2'\n\nfunction saveConfig(config: { theme: string; language: string }) {\n  try {\n    localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))\n  } catch {\n    // Throws in incognito/private browsing, quota exceeded, or disabled\n  }\n}\n\nfunction loadConfig() {\n  try {\n    const data = localStorage.getItem(`userConfig:${VERSION}`)\n    return data ? JSON.parse(data) : null\n  } catch {\n    return null\n  }\n}\n\n// Migration from v1 to v2\nfunction migrate() {\n  try {\n    const v1 = localStorage.getItem('userConfig:v1')\n    if (v1) {\n      const old = JSON.parse(v1)\n      saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })\n      localStorage.removeItem('userConfig:v1')\n    }\n  } catch {}\n}\n```\n\n**Store minimal fields from server responses:**\n\n```typescript\n// User object has 20+ fields, only store what UI needs\nfunction cachePrefs(user: FullUser) {\n  try {\n    localStorage.setItem('prefs:v1', JSON.stringify({\n      theme: user.preferences.theme,\n      notifications: user.preferences.notifications\n    }))\n  } catch {}\n}\n```\n\n**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.\n\n**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.\n\n---\n\n## 5. Re-render Optimization\n\n**Impact: MEDIUM**\n\nReducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.\n\n### 5.1 Calculate Derived State During Rendering\n\n**Impact: MEDIUM (avoids redundant renders and state drift)**\n\nIf a value can be computed from current props/state, do\n\n<!-- [TRUNCATED AT 50KB] -->\n\n<!-- File: skills/react-best-practices/metadata.json (Tokens: ~217 | Category: anthropic-skill) -->\n{\n  \"version\": \"1.0.0\",\n  \"organization\": \"Vercel Engineering\",\n  \"date\": \"January 2026\",\n  \"abstract\": \"Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.\",\n  \"references\": [\n    \"https://react.dev\",\n    \"https://nextjs.org\",\n    \"https://swr.vercel.app\",\n    \"https://github.com/shuding/better-all\",\n    \"https://github.com/isaacs/node-lru-cache\",\n    \"https://vercel.com/blog/how-we-optimized-package-imports-in-next-js\",\n    \"https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast\"\n  ]\n}\n\n<!-- File: skills/react-best-practices/README.md (Tokens: ~848 | Category: anthropic-skill) -->\n# React Best Practices\n\nA structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.\n\n## Structure\n\n- `rules/` - Individual rule files (one per rule)\n  - `_sections.md` - Section metadata (titles, impacts, descriptions)\n  - `_template.md` - Template for creating new rules\n  - `area-description.md` - Individual rule files\n- `src/` - Build scripts and utilities\n- `metadata.json` - Document metadata (version, organization, abstract)\n- __`AGENTS.md`__ - Compiled output (generated)\n- __`test-cases.json`__ - Test cases for LLM evaluation (generated)\n\n## Getting Started\n\n1. Install dependencies:\n   ```bash\n   pnpm install\n   ```\n\n2. Build AGENTS.md from rules:\n   ```bash\n   pnpm build\n   ```\n\n3. Validate rule files:\n   ```bash\n   pnpm validate\n   ```\n\n4. Extract test cases:\n   ```bash\n   pnpm extract-tests\n   ```\n\n## Creating a New Rule\n\n1. Copy `rules/_template.md` to `rules/area-description.md`\n2. Choose the appropriate area prefix:\n   - `async-` for Eliminating Waterfalls (Section 1)\n   - `bundle-` for Bundle Size Optimization (Section 2)\n   - `server-` for Server-Side Performance (Section 3)\n   - `client-` for Client-Side Data Fetching (Section 4)\n   - `rerender-` for Re-render Optimization (Section 5)\n   - `rendering-` for Rendering Performance (Section 6)\n   - `js-` for JavaScript Performance (Section 7)\n   - `advanced-` for Advanced Patterns (Section 8)\n3. Fill in the frontmatter and content\n4. Ensure you have clear examples with explanations\n5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json\n\n## Rule File Structure\n\nEach rule file should follow this structure:\n\n```markdown\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description\ntags: tag1, tag2, tag3\n---\n\n## Rule Title Here\n\nBrief explanation of the rule and why it matters.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example\n```\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example\n```\n\nOptional explanatory text after examples.\n\nReference: [Link](https://example.com)\n\n## File Naming Convention\n\n- Files starting with `_` are special (excluded from build)\n- Rule files: `area-description.md` (e.g., `async-parallel.md`)\n- Section is automatically inferred from filename prefix\n- Rules are sorted alphabetically by title within each section\n- IDs (e.g., 1.1, 1.2) are auto-generated during build\n\n## Impact Levels\n\n- `CRITICAL` - Highest priority, major performance gains\n- `HIGH` - Significant performance improvements\n- `MEDIUM-HIGH` - Moderate-high gains\n- `MEDIUM` - Moderate performance improvements\n- `LOW-MEDIUM` - Low-medium gains\n- `LOW` - Incremental improvements\n\n## Scripts\n\n- `pnpm build` - Compile rules into AGENTS.md\n- `pnpm validate` - Validate all rule files\n- `pnpm extract-tests` - Extract test cases for LLM evaluation\n- `pnpm dev` - Build and validate\n\n## Contributing\n\nWhen adding or modifying rules:\n\n1. Use the correct filename prefix for your section\n2. Follow the `_template.md` structure\n3. Include clear bad/good examples with explanations\n4. Add appropriate tags\n5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json\n6. Rules are automatically sorted by title - no need to manage numbers!\n\n## Acknowledgments\n\nOriginally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).\n\n<!-- File: skills/react-best-practices/rules/_sections.md (Tokens: ~318 | Category: anthropic-skill) -->\n# Sections\n\nThis file defines all sections, their ordering, impact levels, and descriptions.\nThe section ID (in parentheses) is the filename prefix used to group rules.\n\n---\n\n## 1. Eliminating Waterfalls (async)\n\n**Impact:** CRITICAL  \n**Description:** Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.\n\n## 2. Bundle Size Optimization (bundle)\n\n**Impact:** CRITICAL  \n**Description:** Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.\n\n## 3. Server-Side Performance (server)\n\n**Impact:** HIGH  \n**Description:** Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.\n\n## 4. Client-Side Data Fetching (client)\n\n**Impact:** MEDIUM-HIGH  \n**Description:** Automatic deduplication and efficient data fetching patterns reduce redundant network requests.\n\n## 5. Re-render Optimization (rerender)\n\n**Impact:** MEDIUM  \n**Description:** Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.\n\n## 6. Rendering Performance (rendering)\n\n**Impact:** MEDIUM  \n**Description:** Optimizing the rendering process reduces the work the browser needs to do.\n\n## 7. JavaScript Performance (js)\n\n**Impact:** LOW-MEDIUM  \n**Description:** Micro-optimizations for hot paths can add up to meaningful improvements.\n\n## 8. Advanced Patterns (advanced)\n\n**Impact:** LOW  \n**Description:** Advanced patterns for specific cases that require careful implementation.\n\n<!-- File: skills/react-best-practices/rules/_template.md (Tokens: ~143 | Category: anthropic-skill) -->\n---\ntitle: Rule Title Here\nimpact: MEDIUM\nimpactDescription: Optional description of impact (e.g., \"20-50% improvement\")\ntags: tag1, tag2\n---\n\n## Rule Title Here\n\n**Impact: MEDIUM (optional impact description)**\n\nBrief explanation of the rule and why it matters. This should be clear and concise, explaining the performance implications.\n\n**Incorrect (description of what's wrong):**\n\n```typescript\n// Bad code example here\nconst bad = example()\n```\n\n**Correct (description of what's right):**\n\n```typescript\n// Good code example here\nconst good = example()\n```\n\nReference: [Link to documentation or resource](https://example.com)\n\n<!-- File: skills/react-best-practices/rules/advanced-effect-event-deps.md (Tokens: ~375 | Category: anthropic-skill) -->\n---\ntitle: Do Not Put Effect Events in Dependency Arrays\nimpact: LOW\nimpactDescription: avoids unnecessary effect re-runs and lint errors\ntags: advanced, hooks, useEffectEvent, dependencies, effects\n---\n\n## Do Not Put Effect Events in Dependency Arrays\n\nEffect Event functions do not have a stable identity. Their identity intentionally changes on every render. Do not include the function returned by `useEffectEvent` in a `useEffect` dependency array. Keep the actual reactive values as dependencies and call the Effect Event from inside the effect body or subscriptions created by that effect.\n\n**Incorrect (Effect Event added as a dependency):**\n\n```tsx\nimport { useEffect, useEffectEvent } from 'react'\n\nfunction ChatRoom({ roomId, onConnected }: {\n  roomId: string\n  onConnected: () => void\n}) {\n  const handleConnected = useEffectEvent(onConnected)\n\n  useEffect(() => {\n    const connection = createConnection(roomId)\n    connection.on('connected', handleConnected)\n    connection.connect()\n\n    return () => connection.disconnect()\n  }, [roomId, handleConnected])\n}\n```\n\nIncluding the Effect Event in dependencies makes the effect re-run every render and triggers the React Hooks lint rule.\n\n**Correct (depend on reactive values, not the Effect Event):**\n\n```tsx\nimport { useEffect, useEffectEvent } from 'react'\n\nfunction ChatRoom({ roomId, onConnected }: {\n  roomId: string\n  onConnected: () => void\n}) {\n  const handleConnected = useEffectEvent(onConnected)\n\n  useEffect(() => {\n    const connection = createConnection(roomId)\n    connection.on('connected', handleConnected)\n    connection.connect()\n\n    return () => connection.disconnect()\n  }, [roomId])\n}\n```\n\nReference: [React useEffectEvent: Effect Event in deps](https://react.dev/reference/react/useEffectEvent#effect-event-in-deps)\n\n<!-- File: skills/react-best-practices/rules/advanced-event-handler-refs.md (Tokens: ~337 | Category: anthropic-skill) -->\n---\ntitle: Store Event Handlers in Refs\nimpact: LOW\nimpactDescription: stable subscriptions\ntags: advanced, hooks, refs, event-handlers, optimization\n---\n\n## Store Event Handlers in Refs\n\nStore callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.\n\n**Incorrect (re-subscribes on every render):**\n\n```tsx\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  useEffect(() => {\n    window.addEventListener(event, handler)\n    return () => window.removeEventListener(event, handler)\n  }, [event, handler])\n}\n```\n\n**Correct (stable subscription):**\n\n```tsx\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  const handlerRef = useRef(handler)\n  useEffect(() => {\n    handlerRef.current = handler\n  }, [handler])\n\n  useEffect(() => {\n    const listener = (e) => handlerRef.current(e)\n    window.addEventListener(event, listener)\n    return () => window.removeEventListener(event, listener)\n  }, [event])\n}\n```\n\n**Alternative: use `useEffectEvent` if you're on latest React:**\n\n```tsx\nimport { useEffectEvent } from 'react'\n\nfunction useWindowEvent(event: string, handler: (e) => void) {\n  const onEvent = useEffectEvent(handler)\n\n  useEffect(() => {\n    window.addEventListener(event, onEvent)\n    return () => window.removeEventListener(event, onEvent)\n  }, [event])\n}\n```\n\n`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.\n\n<!-- File: skills/react-best-practices/rules/advanced-init-once.md (Tokens: ~230 | Category: anthropic-skill) -->\n---\ntitle: Initialize App Once, Not Per Mount\nimpact: LOW-MEDIUM\nimpactDescription: avoids duplicate init in development\ntags: initialization, useEffect, app-startup, side-effects\n---\n\n## Initialize App Once, Not Per Mount\n\nDo not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.\n\n**Incorrect (runs twice in dev, re-runs on remount):**\n\n```tsx\nfunction Comp() {\n  useEffect(() => {\n    loadFromStorage()\n    checkAuthToken()\n  }, [])\n\n  // ...\n}\n```\n\n**Correct (once per app load):**\n\n```tsx\nlet didInit = false\n\nfunction Comp() {\n  useEffect(() => {\n    if (didInit) return\n    didInit = true\n    loadFromStorage()\n    checkAuthToken()\n  }, [])\n\n  // ...\n}\n```\n\nReference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)\n\n<!-- File: skills/react-best-practices/rules/advanced-use-latest.md (Tokens: ~254 | Category: anthropic-skill) -->\n---\ntitle: useEffectEvent for Stable Callback Refs\nimpact: LOW\nimpactDescription: prevents effect re-runs\ntags: advanced, hooks, useEffectEvent, refs, optimization\n---\n\n## useEffectEvent for Stable Callback Refs\n\nAccess latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.\n\n**Incorrect (effect re-runs on every callback change):**\n\n```tsx\nfunction SearchInput({ onSearch }: { onSearch: (q: string) => void }) {\n  const [query, setQuery] = useState('')\n\n  useEffect(() => {\n    const timeout = setTimeout(() => onSearch(query), 300)\n    return () => clearTimeout(timeout)\n  }, [query, onSearch])\n}\n```\n\n**Correct (using React's useEffectEvent):**\n\n```tsx\nimport { useEffectEvent } from 'react';\n\nfunction SearchInput({ onSearch }: { onSearch: (q: string) => void }) {\n  const [query, setQuery] = useState('')\n  const onSearchEvent = useEffectEvent(onSearch)\n\n  useEffect(() => {\n    const timeout = setTimeout(() => onSearchEvent(query), 300)\n    return () => clearTimeout(timeout)\n  }, [query])\n}\n```\n\n<!-- File: skills/react-best-practices/rules/async-api-routes.md (Tokens: ~241 | Category: anthropic-skill) -->\n---\ntitle: Prevent Waterfall Chains in API Routes\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: api-routes, server-actions, waterfalls, parallelization\n---\n\n## Prevent Waterfall Chains in API Routes\n\nIn API routes and Server Actions, start independent operations immediately, even if you don't await them yet.\n\n**Incorrect (config waits for auth, data waits for both):**\n\n```typescript\nexport async function GET(request: Request) {\n  const session = await auth()\n  const config = await fetchConfig()\n  const data = await fetchData(session.user.id)\n  return Response.json({ data, config })\n}\n```\n\n**Correct (auth and config start immediately):**\n\n```typescript\nexport async function GET(request: Request) {\n  const sessionPromise = auth()\n  const configPromise = fetchConfig()\n  const session = await sessionPromise\n  const [config, data] = await Promise.all([\n    configPromise,\n    fetchData(session.user.id)\n  ])\n  return Response.json({ data, config })\n}\n```\n\nFor operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).\n\n<!-- File: skills/react-best-practices/rules/async-cheap-condition-before-await.md (Tokens: ~277 | Category: anthropic-skill) -->\n---\ntitle: Check Cheap Conditions Before Async Flags\nimpact: HIGH\nimpactDescription: avoids unnecessary async work when a synchronous guard already fails\ntags: async, await, feature-flags, short-circuit, conditional\n---\n\n## Check Cheap Conditions Before Async Flags\n\nWhen a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.\n\nThis is a specialization of [Defer Await Until Needed](./async-defer-await.md) for `flag && cheapCondition` style checks.\n\n**Incorrect:**\n\n```typescript\nconst someFlag = await getFlag()\n\nif (someFlag && someCondition) {\n  // ...\n}\n```\n\n**Correct:**\n\n```typescript\nif (someCondition) {\n  const someFlag = await getFlag()\n  if (someFlag) {\n    // ...\n  }\n}\n```\n\nThis matters when `getFlag` hits the network, a feature-flag service, or `React.cache` / DB work: skipping it when `someCondition` is false removes that cost on the cold path.\n\nKeep the original order if `someCondition` is expensive, depends on the flag, or you must run side effects in a fixed order.\n\n<!-- File: skills/react-best-practices/rules/async-defer-await.md (Tokens: ~474 | Category: anthropic-skill) -->\n---\ntitle: Defer Await Until Needed\nimpact: HIGH\nimpactDescription: avoids blocking unused code paths\ntags: async, await, conditional, optimization\n---\n\n## Defer Await Until Needed\n\nMove `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.\n\n**Incorrect (blocks both branches):**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  const userData = await fetchUserData(userId)\n  \n  if (skipProcessing) {\n    // Returns immediately but still waited for userData\n    return { skipped: true }\n  }\n  \n  // Only this branch uses userData\n  return processUserData(userData)\n}\n```\n\n**Correct (only blocks when needed):**\n\n```typescript\nasync function handleRequest(userId: string, skipProcessing: boolean) {\n  if (skipProcessing) {\n    // Returns immediately without waiting\n    return { skipped: true }\n  }\n  \n  // Fetch only when needed\n  const userData = await fetchUserData(userId)\n  return processUserData(userData)\n}\n```\n\n**Another example (early return optimization):**\n\n```typescript\n// Incorrect: always fetches permissions\nasync function updateResource(resourceId: string, userId: string) {\n  const permissions = await fetchPermissions(userId)\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n\n// Correct: fetches only when needed\nasync function updateResource(resourceId: string, userId: string) {\n  const resource = await getResource(resourceId)\n  \n  if (!resource) {\n    return { error: 'Not found' }\n  }\n  \n  const permissions = await fetchPermissions(userId)\n  \n  if (!permissions.canEdit) {\n    return { error: 'Forbidden' }\n  }\n  \n  return await updateResourceData(resource, permissions)\n}\n```\n\nThis optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.\n\nFor `await getFlag()` combined with a cheap synchronous guard (`flag && someCondition`), see [Check Cheap Conditions Before Async Flags](./async-cheap-condition-before-await.md).\n\n<!-- File: skills/react-best-practices/rules/async-dependencies.md (Tokens: ~291 | Category: anthropic-skill) -->\n---\ntitle: Dependency-Based Parallelization\nimpact: CRITICAL\nimpactDescription: 2-10× improvement\ntags: async, parallelization, dependencies, better-all\n---\n\n## Dependency-Based Parallelization\n\nFor operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.\n\n**Incorrect (profile waits for config unnecessarily):**\n\n```typescript\nconst [user, config] = await Promise.all([\n  fetchUser(),\n  fetchConfig()\n])\nconst profile = await fetchProfile(user.id)\n```\n\n**Correct (config and profile run in parallel):**\n\n```typescript\nimport { all } from 'better-all'\n\nconst { user, config, profile } = await all({\n  async user() { return fetchUser() },\n  async config() { return fetchConfig() },\n  async profile() {\n    return fetchProfile((await this.$.user).id)\n  }\n})\n```\n\n**Alternative without extra dependencies:**\n\nWe can also create all the promises first, and do `Promise.all()` at the end.\n\n```typescript\nconst userPromise = fetchUser()\nconst profilePromise = userPromise.then(user => fetchProfile(user.id))\n\nconst [user, config, profile] = await Promise.all([\n  userPromise,\n  fetchConfig(),\n  profilePromise\n])\n```\n\nReference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)\n\n<!-- ... +183 more modular skills available via /api/skills/vercel-labs/agent-skills?path={path} -->\n</yakaai_skills>"}