Curated Claude Code catalog
Updated 07.05.2026 · 19:39 CET
01 / Skill
vercel

next.js

Quality
9.0

This plugin offers expert guidance for Next.js Cache Components and Partial Prerendering (PPR), proactively activating in projects where cacheComponents is enabled. It helps developers understand and effectively utilize advanced caching strategies for improved application performance.

USP

Unlike generic documentation, this plugin provides contextual, proactive guidance directly within your Claude Code environment, specifically tailored for projects utilizing Next.js Cache Components and PPR.

Use cases

  • 01Optimizing Next.js application performance
  • 02Implementing Partial Prerendering (PPR)
  • 03Debugging cache-related issues
  • 04Learning advanced Next.js caching
  • 05Improving server-side rendering efficiency

Detected files (8)

  • .agents/skills/runtime-debug/SKILL.mdskill
    Show content (2442 bytes)
    ---
    name: runtime-debug
    description: >
      Debug and verification workflow for runtime-bundle and module-resolution
      regressions. Use when diagnosing unexpected module inclusions, bundle
      size regressions, or CI failures related to NEXT_SKIP_ISOLATE, nft.json
      traces, or runtime bundle selection (module.compiled.js). Covers CI env
      mirroring, full stack traces via __NEXT_SHOW_IGNORE_LISTED, route trace
      inspection, and webpack stats diffing.
    ---
    
    # Runtime Debug
    
    Use this skill when reproducing runtime-bundle, module-resolution, or user-bundle inclusion regressions.
    
    ## Local Repro Discipline
    
    - Mirror CI env vars when reproducing CI failures.
    - Key variables: `IS_WEBPACK_TEST=1` forces webpack (turbopack is default), `NEXT_SKIP_ISOLATE=1` skips packing next.js.
    - For module-resolution validation, always rerun without `NEXT_SKIP_ISOLATE=1`.
    
    ## Stack Trace Visibility
    
    Set `__NEXT_SHOW_IGNORE_LISTED=true` to disable the ignore-list filtering in dev server error output. By default, Next.js collapses internal frames to `at ignore-listed frames`, which hides useful context when debugging framework internals. Defined in `packages/next/src/server/patch-error-inspect.ts`.
    
    ## User-Bundle Regression Guardrail
    
    When user `next build` starts bundling internal Node-only helpers unexpectedly:
    
    1. Inspect route trace artifacts (`.next/server/.../page.js.nft.json`).
    2. Inspect traced server chunks for forbidden internals (e.g. `next/dist/server/stream-utils/node-stream-helpers.js`, `node:stream/promises`).
    3. Add a `test-start-webpack` assertion that reads the route trace and traced server chunks, and fails on forbidden internals. This validates user-project bundling (not publish-time runtime bundling).
    
    ## Bundle Tracing / Inclusion Proof
    
    To prove what user bundling includes, emit webpack stats from the app's `next.config.js`:
    
    ```js
    // next.config.js
    module.exports = {
      webpack(config) {
        config.profile = true
        return config
      },
    }
    ```
    
    Then use `stats.toJson({ modules: true, chunks: true, reasons: true })` and diff `webpack-stats-server.json` between modes. This gives concrete inclusion reasons (e.g. which module required `node:stream/promises`) and is more reliable than analyzer HTML alone.
    
    ## Related Skills
    
    - `$flags` - flag wiring (config/schema/define-env/runtime env)
    - `$dce-edge` - DCE-safe require patterns and edge constraints
    - `$react-vendoring` - entry-base boundaries and vendored React
    
  • .agents/skills/pr-status-triage/SKILL.mdskill
    Show content (2289 bytes)
    ---
    name: pr-status-triage
    description: >
      Triage CI failures and PR review comments using scripts/pr-status.js.
      Use when investigating failing CI jobs, flaky tests, or PR review feedback.
      Covers blocker-first prioritization (build > lint > types > tests),
      CI env var matching for local reproduction, and the Known Flaky Tests
      distinction.
    ---
    
    # PR Status Triage
    
    Use this skill when the user asks about PR status, CI failures, or review comments in the Next.js monorepo.
    
    ## Workflow
    
    1. Run `node scripts/pr-status.js --wait` in the background (timeout 1 min), then read `scripts/pr-status/index.md`.
    2. Analyze each `job-{id}.md` and `thread-{N}.md` file for failures and review feedback.
    3. Prioritize blocking jobs first: build, lint, types, then test jobs.
    4. Treat failures as real until disproven; check the "Known Flaky Tests" section before calling anything flaky.
    5. Reproduce locally with the same mode and env vars as CI.
    6. After addressing review comments, reply to the thread describing what was done, then resolve it. Use `reply-and-resolve-thread` to do both in one step, or use `reply-thread` + `resolve-thread` separately. See `thread-N.md` files for ready-to-use commands.
    7. When the only remaining failures are known flaky tests and no code changes are needed, retrigger the failing CI jobs with `gh run rerun <run-id> --failed`. Then wait 5 minutes and go back to step 1. Repeat this loop up to 5 times.
    
    ## Quick Commands
    
    ```bash
    node scripts/pr-status.js                  # current branch PR
    node scripts/pr-status.js <number>         # specific PR
    node scripts/pr-status.js [PR] --wait      # background mode, waits for CI to finish
    node scripts/pr-status.js --skip-flaky-check  # skip flaky test detection
    ```
    
    Thread interaction:
    
    ```bash
    node scripts/pr-status.js reply-thread <threadNodeId> "<body>"           # reply to a review thread
    node scripts/pr-status.js resolve-thread <threadNodeId>                  # resolve a review thread
    node scripts/pr-status.js reply-and-resolve-thread <threadNodeId> "<body>"  # reply and resolve in one step
    ```
    
    ## References
    
    - [workflow.md](./workflow.md) — prioritization, common failure patterns, resolving review threads
    - [local-repro.md](./local-repro.md) — mode/env matching and isolation guidance
    
  • .agents/skills/router-act/SKILL.mdskill
    Show content (10587 bytes)
    ---
    name: router-act
    description: >
      How to write end-to-end tests using createRouterAct and LinkAccordion.
      Use when writing or modifying tests that need to control the timing of
      internal Next.js requests (like prefetches) or assert on their responses.
      Covers the act API, fixture patterns, prefetch control via LinkAccordion,
      fake clocks, and avoiding flaky testing patterns.
    user-invocable: false
    ---
    
    # Router Act Testing
    
    Use this skill when writing or modifying tests that involve prefetch requests, client router navigations, or the segment cache. The `createRouterAct` utility from `test/lib/router-act.ts` lets you assert on prefetch and navigation responses in an end-to-end way without coupling to the exact number of requests or the protocol details. This is why most client router-related tests use this pattern.
    
    ## When NOT to Use `act`
    
    Don't bother with `act` if you don't need to instrument the network responses — either to control their timing or to assert on what's included in them. If all you're doing is waiting for some part of the UI to appear after a navigation, regular Playwright helpers like `browser.elementById()`, `browser.elementByCss()`, and `browser.waitForElementByCss()` are sufficient.
    
    ## Core Principles
    
    1. **Use `LinkAccordion` to control when prefetches happen.** Never let links be visible outside an `act` scope.
    2. **Prefer `'no-requests'`** whenever the data should be served from cache. This is the strongest assertion — it proves the cache is working.
    3. **Avoid retry/polling timers.** The `act` utility exists specifically to replace inherently flaky patterns like `retry()` loops or `setTimeout` waits for network activity. If you find yourself wanting to poll, you're probably not using `act` correctly.
    4. **Avoid the `block` feature.** It's prone to false negatives. Prefer `includes` and `'no-requests'` assertions instead.
    
    ## Act API
    
    ### Config Options
    
    ```typescript
    // Assert NO router requests are made (data served from cache).
    // Prefer this whenever possible — it's the strongest assertion.
    await act(async () => { ... }, 'no-requests')
    
    // Expect at least one response containing this substring
    await act(async () => { ... }, { includes: 'Page content' })
    
    // Expect multiple responses (checked in order)
    await act(async () => { ... }, [
      { includes: 'First response' },
      { includes: 'Second response' },
    ])
    
    // Assert the same content appears in two separate responses
    await act(async () => { ... }, [
      { includes: 'Repeated content' },
      { includes: 'Repeated content' },
    ])
    
    // Expect at least one request, don't assert on content
    await act(async () => { ... })
    ```
    
    ### How `includes` Matching Works
    
    - The `includes` substring is matched against the HTTP response body. Use text content that appears literally in the rendered output (e.g. `'Dynamic content (stale time 60s)'`).
    - Extra responses that don't match any `includes` assertion are silently ignored — you only need to assert on the responses you care about. This keeps tests decoupled from the exact number of requests the router makes.
    - Each `includes` expectation claims exactly one response. If the same substring appears in N separate responses, provide N separate `{ includes: '...' }` entries.
    
    ### What `act` Does Internally
    
    `act` intercepts all router requests — prefetches, navigations, and Server Actions — made during the scope:
    
    1. Installs a Playwright route handler to intercept router requests
    2. Runs your scope function
    3. Waits for a `requestIdleCallback` (captures IntersectionObserver-triggered prefetches)
    4. Fulfills buffered responses to the browser
    5. Repeats steps 3-4 until no more requests arrive
    6. Asserts on the responses based on the config
    
    Responses are buffered and only forwarded to the browser after the scope function returns. This means you cannot navigate to a new page and wait for it to render within the same scope — that would deadlock. Trigger the navigation (click the link) and let `act` handle the rest. Read destination page content _after_ `act` returns:
    
    ```typescript
    await act(
      async () => {
        /* toggle accordion, click link */
      },
      { includes: 'Page content' }
    )
    
    // Read content after act returns, not inside the scope
    expect(await browser.elementById('my-content').text()).toBe('Page content')
    ```
    
    ## LinkAccordion Pattern
    
    ### Why LinkAccordion Exists
    
    `LinkAccordion` controls when `<Link>` components enter the DOM. A Next.js `<Link>` triggers a prefetch when it enters the viewport (via IntersectionObserver). By hiding the Link behind a checkbox toggle, you control exactly when prefetches happen — only when you explicitly toggle the accordion inside an `act` scope.
    
    ```tsx
    // components/link-accordion.tsx
    'use client'
    import Link from 'next/link'
    import { useState } from 'react'
    
    export function LinkAccordion({ href, children, prefetch }) {
      const [isVisible, setIsVisible] = useState(false)
      return (
        <>
          <input
            type="checkbox"
            checked={isVisible}
            onChange={() => setIsVisible(!isVisible)}
            data-link-accordion={href}
          />
          {isVisible ? (
            <Link href={href} prefetch={prefetch}>
              {children}
            </Link>
          ) : (
            `${children} (link is hidden)`
          )}
        </>
      )
    }
    ```
    
    ### Standard Navigation Pattern
    
    Always toggle the accordion and click the link inside the same `act` scope:
    
    ```typescript
    await act(
      async () => {
        // 1. Toggle accordion — Link enters DOM, triggers prefetch
        const toggle = await browser.elementByCss(
          'input[data-link-accordion="/target-page"]'
        )
        await toggle.click()
    
        // 2. Click the now-visible link — triggers navigation
        const link = await browser.elementByCss('a[href="/target-page"]')
        await link.click()
      },
      { includes: 'Expected page content' }
    )
    ```
    
    ## Common Sources of Flakiness
    
    ### Using `browser.back()` with open accordions
    
    Do not use `browser.back()` to return to a page where accordions were previously opened. BFCache restores the full React state including `useState` values, so previously-opened Links are immediately visible. This triggers IntersectionObserver callbacks outside any `act` scope — if the cached data is stale, uncontrolled re-prefetches fire and break subsequent `no-requests` assertions.
    
    The only safe use of `browser.back()`/`browser.forward()` is when testing BFCache behavior specifically.
    
    **Fix:** navigate forward to a fresh hub page instead. See [Hub Pages](#hub-pages).
    
    ### Using visible `<Link>` components outside `act` scopes
    
    Any `<Link>` visible in the viewport can trigger a prefetch at any time via IntersectionObserver. If this happens outside an `act` scope, the request is uncontrolled and can interfere with subsequent assertions. Always hide links behind `LinkAccordion` and only toggle them inside `act`.
    
    ### Using retry/polling timers to wait for network activity
    
    `retry()`, `setTimeout`, or any polling pattern to wait for prefetches or navigations to settle is inherently flaky. `act` deterministically waits for all router requests to complete before returning.
    
    ### Navigating and waiting for render in the same `act` scope
    
    Responses are buffered until the scope exits. Clicking a link then reading destination content in the same scope deadlocks. Read page content after `act` returns instead.
    
    ## Hub Pages
    
    When you need to navigate away from a page and come back to test staleness, use "hub" pages instead of `browser.back()`. Each hub is a fresh page with its own `LinkAccordion` components that start closed.
    
    Hub pages use `connection()` to ensure they are dynamically rendered. This guarantees that navigating to a hub always produces a router request, which lets `act` properly manage the navigation and wait for the page to fully render before continuing.
    
    **Hub page pattern:**
    
    ```tsx
    // app/my-test/hub-a/page.tsx
    import { Suspense } from 'react'
    import { connection } from 'next/server'
    import { LinkAccordion } from '../../components/link-accordion'
    
    async function Content() {
      await connection()
      return <div id="hub-a-content">Hub a</div>
    }
    
    export default function Page() {
      return (
        <>
          <Suspense fallback="Loading...">
            <Content />
          </Suspense>
          <ul>
            <li>
              <LinkAccordion href="/my-test/target-page">Target page</LinkAccordion>
            </li>
          </ul>
        </>
      )
    }
    ```
    
    **Target pages link to hubs via LinkAccordion too:**
    
    ```tsx
    // On target pages, add LinkAccordion links to hub pages
    <LinkAccordion href="/my-test/hub-a">Hub A</LinkAccordion>
    ```
    
    **Test flow:**
    
    ```typescript
    // 1. Navigate to target (first visit)
    await act(
      async () => {
        /* toggle accordion, click link */
      },
      { includes: 'Target content' }
    )
    
    // 2. Navigate to hub-a (fresh page, all accordions closed)
    await act(
      async () => {
        const toggle = await browser.elementByCss(
          'input[data-link-accordion="/my-test/hub-a"]'
        )
        await toggle.click()
        const link = await browser.elementByCss('a[href="/my-test/hub-a"]')
        await link.click()
      },
      { includes: 'Hub a' }
    )
    
    // 3. Advance time
    await page.clock.setFixedTime(startDate + 60 * 1000)
    
    // 4. Navigate back to target from hub (controlled prefetch)
    await act(async () => {
      const toggle = await browser.elementByCss(
        'input[data-link-accordion="/my-test/target-page"]'
      )
      await toggle.click()
      const link = await browser.elementByCss('a[href="/my-test/target-page"]')
      await link.click()
    }, 'no-requests') // or { includes: '...' } if data is stale
    ```
    
    ## Fake Clock Setup
    
    Segment cache staleness tests use Playwright's clock API to control `Date.now()`:
    
    ```typescript
    async function startBrowserWithFakeClock(url: string) {
      let page!: Playwright.Page
      const startDate = Date.now()
    
      const browser = await next.browser(url, {
        async beforePageLoad(p: Playwright.Page) {
          page = p
          await page.clock.install()
          await page.clock.setFixedTime(startDate)
        },
      })
    
      const act = createRouterAct(page)
      return { browser, page, act, startDate }
    }
    ```
    
    - `setFixedTime` changes `Date.now()` return value but timers still run in real time
    - The segment cache uses `Date.now()` for staleness checks
    - Advancing the clock doesn't trigger IntersectionObserver — only viewport changes do
    - `setFixedTime` does NOT fire pending `setTimeout`/`setInterval` callbacks
    
    ## Reference
    
    - `createRouterAct`: `test/lib/router-act.ts`
    - `LinkAccordion`: `test/e2e/app-dir/segment-cache/staleness/components/link-accordion.tsx`
    - Example tests: `test/e2e/app-dir/segment-cache/staleness/`
    
  • .agents/skills/authoring-skills/SKILL.mdskill
    Show content (4128 bytes)
    ---
    name: authoring-skills
    description: >
      How to create and maintain agent skills in .agents/skills/. Use when
      creating a new SKILL.md, writing skill descriptions, choosing frontmatter
      fields, or deciding what content belongs in a skill vs AGENTS.md.
      Covers the supported spec fields, description writing, naming conventions,
      and the relationship between always-loaded AGENTS.md and on-demand skills.
    user-invocable: false
    ---
    
    # Authoring Skills
    
    Use this skill when creating or modifying agent skills in `.agents/skills/`.
    
    ## When to Create a Skill
    
    Create a skill when content is:
    
    - Too detailed for AGENTS.md (code templates, multi-step workflows, diagnostic procedures)
    - Only relevant for specific tasks (not needed every session)
    - Self-contained enough to load independently
    
    Keep in AGENTS.md instead when:
    
    - It's a one-liner rule or guardrail every session needs
    - It's a general-purpose gotcha any agent could hit
    
    ## File Structure
    
    ```
    .agents/skills/
    └── my-skill/
        ├── SKILL.md          # Required: frontmatter + content
        ├── workflow.md        # Optional: supplementary detail
        └── examples.md        # Optional: referenced from SKILL.md
    ```
    
    ## Supported Frontmatter Fields
    
    ```yaml
    ---
    name: my-skill # Required. Used for $name references and /name commands.
    description: > # Required. How Claude decides to auto-load the skill.
      What this covers and when to use it. Include file names and keywords.
    argument-hint: '<pr-number>' # Optional. Hint for expected arguments.
    user-invocable: false # Optional. Set false to hide from / menu.
    disable-model-invocation: true # Optional. Set true to prevent auto-triggering.
    allowed-tools: [Bash, Read] # Optional. Tools allowed without permission.
    model: opus # Optional. Model override.
    context: fork # Optional. Isolated subagent execution.
    agent: Explore # Optional. Subagent type (with context: fork).
    ---
    ```
    
    Only use fields from this list. Unknown fields are silently ignored.
    
    ## Writing Descriptions
    
    The `description` is the primary matching surface for auto-activation. Include:
    
    1. **What the skill covers** (topic)
    2. **When to use it** (trigger scenario)
    3. **Key file names** the skill references (e.g. `config-shared.ts`)
    4. **Keywords** a user or agent might mention (e.g. "feature flag", "DCE")
    
    ```yaml
    # Too vague - won't auto-trigger reliably
    description: Helps with flags.
    
    # Good - specific files and concepts for matching
    description: >
      How to add or modify Next.js experimental feature flags end-to-end.
      Use when editing config-shared.ts, config-schema.ts, define-env-plugin.ts.
    ```
    
    ## Content Conventions
    
    ### Structure for Action
    
    Skills should tell the agent what to **do**, not just what to **know**:
    
    - Lead with "Use this skill when..."
    - Include step-by-step procedures
    - Add code templates ready to adapt
    - End with verification commands
    - Cross-reference related skills in a "Related Skills" section
    
    ### Relationship to AGENTS.md
    
    | AGENTS.md (always loaded)               | Skills (on demand)                                                     |
    | --------------------------------------- | ---------------------------------------------------------------------- |
    | One-liner guardrails                    | Step-by-step workflows                                                 |
    | "Keep require() behind if/else for DCE" | Full DCE pattern with code examples, verification commands, edge cases |
    | Points to skills via `$name`            | Expands on AGENTS.md rules                                             |
    
    When adding a skill, also add a one-liner summary to the relevant AGENTS.md section with a `$skill-name` reference.
    
    ### Naming
    
    - Short, descriptive, topic-scoped: `flags`, `dce-edge`, `react-vendoring`
    - No repo prefix (already scoped by `.agents/skills/`)
    - Hyphens for multi-word names
    
    ### Supplementary Files
    
    For complex skills, use a hub + detail pattern:
    
    ```
    pr-status-triage/
    ├── SKILL.md         # Overview, quick commands, links to details
    ├── workflow.md      # Prioritization and patterns
    └── local-repro.md   # CI env matching
    ```
    
  • .agents/skills/dce-edge/SKILL.mdskill
    Show content (4077 bytes)
    ---
    name: dce-edge
    description: >
      DCE-safe require() patterns and edge runtime constraints. Use when writing
      conditional require() calls, guarding Node-only imports (node:stream etc.),
      or editing define-env-plugin.ts / app-render / stream-utils for edge builds.
      Covers if/else branching for webpack DCE, TypeScript definite assignment,
      the NEXT_RUNTIME vs real feature flag distinction, and forcing flags false
      for edge in define-env.ts.
    ---
    
    # DCE + Edge
    
    Use this skill when changing conditional `require()` paths, Node-only imports, or edge/runtime branching.
    
    ## DCE-Safe `require()` Pattern
    
    Webpack only DCEs a `require()` when it sits inside the dead branch of an `if/else` whose condition DefinePlugin can evaluate at compile time.
    
    ```ts
    // CORRECT - webpack can eliminate the dead branch
    if (process.env.__NEXT_USE_NODE_STREAMS) {
      require('node:stream')
    } else {
      // web path
    }
    ```
    
    What does NOT work:
    
    - **Early-return/throw guards**: webpack doesn't do control-flow analysis for throws/returns, so the `require()` is still traced.
    - **Bare `if` without `else`**: works for inline `node:*` specifiers but NOT for `require('./some-module')` that pulls a new file into the module graph.
    
    Always test edge changes with `pnpm test-start-webpack` on `test/e2e/app-dir/app/standalone.test.ts` (has edge routes), not with `NEXT_SKIP_ISOLATE=1` which skips the full webpack compilation.
    
    ## TypeScript + DCE Interaction
    
    Use `if/else` (not two independent `if` blocks) when assigning a variable conditionally on `process.env.X`. TypeScript cannot prove exhaustiveness across `if (flag) { x = a }; if (!flag) { x = b }` and will error with "variable used before being assigned". The `if/else` pattern satisfies both TypeScript (definite assignment) and webpack DCE.
    
    ## Compile-Time Switcher Pattern
    
    Platform-specific code (node vs web) can use a single `.ts` switcher module that conditionally `require()`s either `.node.ts` or `.web.ts` into a typed variable, then re-exports the shared runtime API as named exports. Keep the branch as `if/else` so DefinePlugin can dead-code-eliminate the unused `require()`. Keep shared types canonical in `.node.ts`, with `.web.ts` importing them via `import type` and the switcher re-exporting types as needed. Examples: `stream-ops.ts` and `debug-channel-server.ts`.
    
    ## `NEXT_RUNTIME` Is Not a Feature Flag
    
    In user-project webpack server compilers, `process.env.NEXT_RUNTIME` is inlined to `'nodejs'`. Guarding Node-only `require('node:*')` paths with `NEXT_RUNTIME === 'nodejs'` does **not** prune anything. For feature-gated codepaths, guard on the real feature define (e.g. `process.env.__NEXT_USE_NODE_STREAMS`).
    
    ## Edge Runtime Constraints
    
    Edge routes do NOT use pre-compiled runtime bundles. They are compiled by the user's webpack/Turbopack, so `define-env.ts` controls DCE. Feature flags that gate `node:*` imports must be forced to `false` for edge builds in `define-env.ts` (`isEdgeServer ? false : flagValue`), otherwise webpack will try to resolve `node:stream` etc. and fail.
    
    ## `app-page.ts` Template Gotchas
    
    - `app-page.ts` is a build template compiled by the user's bundler. Any `require()` in this file is traced by webpack/turbopack at `next build` time. You cannot require internal modules with relative paths because they won't be resolvable from the user's project. Instead, export new helpers from `entry-base.ts` and access them via `entryBase.*` in the template.
    - Template helpers should stay out of `RenderResult`. If `app-page.ts` needs a Node-stream-only utility, prefer a small dedicated helper module in `server/stream-utils/` (with DCE-safe `if/else` + `require()`).
    
    ## Verification
    
    - Validate edge bundling regressions with `pnpm test-start-webpack test/e2e/app-dir/app/standalone.test.ts`
    - For module-resolution/build-graph fixes, verify without `NEXT_SKIP_ISOLATE=1`
    
    ## Related Skills
    
    - `$flags` - flag wiring (config/schema/define-env/runtime env)
    - `$react-vendoring` - entry-base boundaries and vendored React
    - `$runtime-debug` - reproduction and verification workflow
    
  • .agents/skills/react-vendoring/SKILL.mdskill
    Show content (3921 bytes)
    ---
    name: react-vendoring
    description: >
      React vendoring and react-server layer boundaries. Use when editing
      entry-base.ts, $$compiled.internal.d.ts, compiled/react* packages,
      or taskfile.js copy_vendor_react. Covers the entry-base.ts boundary
      (all react-server-dom-webpack/* imports must go through it), vendored
      React channels, type declarations, Turbopack remap to
      react-server-dom-turbopack, ComponentMod access patterns, and ESLint
      suppression for guarded requires.
    ---
    
    # React Vendoring
    
    Use this skill for changes touching vendored React, `react-server-dom-webpack/*`, or react-server layer boundaries.
    
    ## App Router Vendoring
    
    React is NOT resolved from `node_modules` for App Router. It's vendored into `packages/next/src/compiled/` during `pnpm build` (task: `copy_vendor_react()` in `taskfile.js`). Pages Router resolves React from `node_modules` normally.
    
    - **Two channels**: stable (`compiled/react/`) and experimental (`compiled/react-experimental/`). The runtime bundle webpack config aliases to the correct channel via `makeAppAliases({ experimental })`.
    
    ## `entry-base.ts` Boundary
    
    Only `entry-base.ts` is compiled in rspack's `(react-server)` layer. ALL imports from `react-server-dom-webpack/*` (Flight server/static APIs) must go through `entry-base.ts`. Other files like `stream-ops.node.ts` or `app-render.tsx` must access Flight APIs via the `ComponentMod` parameter (which is the `entry-base.ts` module exposed through the `app-page.ts` build template).
    
    Direct imports from `react-server-dom-webpack/server.node` or `react-server-dom-webpack/static` in files outside `entry-base.ts` will fail at runtime with "The react-server condition must be enabled". Dev mode may mask this error, but production workers fail immediately.
    
    ## Type Declarations
    
    `packages/next/types/$$compiled.internal.d.ts` contains `declare module` blocks for vendored React packages. When adding new APIs (e.g. `renderToPipeableStream`, `prerenderToNodeStream`), you must add type declarations here. The bare specifier types (e.g. `declare module 'react-server-dom-webpack/server'`) are what source code in `src/` imports against.
    
    ## Adding Node.js-Only React APIs
    
    These exist in `.node` builds but not in the type definitions. Steps:
    
    1. Add type declarations to `$$compiled.internal.d.ts`.
    2. Export the API from `entry-base.ts` behind a `process.env` guard.
    3. Access it via `ComponentMod` in other files.
    
    ```typescript
    // In entry-base.ts (react-server layer) only:
    /* eslint-disable import/no-extraneous-dependencies */
    export let renderToPipeableStream: ... | undefined
    if (process.env.__NEXT_USE_NODE_STREAMS) {
      renderToPipeableStream = (
        require('react-server-dom-webpack/server.node') as typeof import('react-server-dom-webpack/server.node')
      ).renderToPipeableStream
    } else {
      renderToPipeableStream = undefined
    }
    /* eslint-enable import/no-extraneous-dependencies */
    
    // In other files, access via ComponentMod:
    ComponentMod.renderToPipeableStream!(payload, clientModules, opts)
    ```
    
    ## ESLint Practical Rule
    
    For guarded runtime `require()` blocks that need `import/no-extraneous-dependencies` suppression, prefer scoped block disable/enable. If using `eslint-disable-next-line`, the comment must be on the line immediately before the `require()` call, NOT before the `const` declaration. When the `const` and `require()` are on different lines, this is error-prone.
    
    ## Turbopack Remap
    
    `react-server-dom-webpack/*` is silently remapped to `react-server-dom-turbopack/*` by Turbopack's import map. Code says "webpack" everywhere, but Turbopack gets its own bindings at runtime. This affects debugging: stack traces and error messages will reference the turbopack variant.
    
    ## Related Skills
    
    - `$flags` - flag wiring (config/schema/define-env/runtime env)
    - `$dce-edge` - DCE-safe require patterns and edge constraints
    - `$runtime-debug` - reproduction and verification workflow
    
  • .agents/skills/flags/SKILL.mdskill
    Show content (2961 bytes)
    ---
    name: flags
    description: >
      How to add or modify Next.js experimental feature flags end-to-end.
      Use when editing config-shared.ts, config-schema.ts, define-env-plugin.ts,
      next-server.ts, export/worker.ts, or module.compiled.js. Covers type
      declaration, zod schema, build-time injection, runtime env plumbing,
      and the decision between runtime env-var branching vs separate bundle variants.
    ---
    
    # Feature Flags
    
    Use this skill when adding or changing framework feature flags in Next.js internals.
    
    ## Required Wiring
    
    All flags need: `config-shared.ts` (type) → `config-schema.ts` (zod). If the flag is consumed in user-bundled code (client components, edge routes, `app-page.ts` template), also add it to `define-env.ts` for build-time injection. Runtime-only flags consumed exclusively in pre-compiled bundles can skip `define-env.ts`.
    
    ## Where the Flag Is Consumed
    
    **Client/bundled code only** (e.g. `__NEXT_PPR` in client components): `define-env.ts` is sufficient. Webpack/Turbopack replaces `process.env.X` at the user's build time.
    
    **Pre-compiled runtime bundles** (e.g. code in `app-render.tsx`): The flag must also be set as a real `process.env` var at runtime, because `app-render.tsx` runs from pre-compiled bundles where `define-env.ts` doesn't reach. Two approaches:
    
    - **Runtime env var**: Set in `next-server.ts` + `export/worker.ts`. Both code paths stay in one bundle. Simple but increases bundle size.
    - **Separate bundle variant**: Add DefinePlugin entry in `next-runtime.webpack-config.js` (scoped to `bundleType === 'app'`), new taskfile tasks, update `module.compiled.js` selector, and still set env var in `next-server.ts` + `export/worker.ts` for bundle selection. Eliminates dead code but adds build complexity.
    
    For runtime flags, also add the field to the `NextConfigRuntime` Pick type in `config-shared.ts`.
    
    ## Runtime-Bundle Model
    
    - Runtime bundles are built by `next-runtime.webpack-config.js` (rspack) via `taskfile.js` bundle tasks.
    - Bundle selection occurs at runtime in `src/server/route-modules/app-page/module.compiled.js` based on `process.env` vars.
    - Variants: `{turbo/webpack} × {experimental/stable/nodestreams/experimental-nodestreams} × {dev/prod}` = up to 16 bundles per route type.
    - `define-env.ts` affects user bundling, not pre-compiled runtime internals.
    - `process.env.X` checks in `app-render.tsx` are either replaced by DefinePlugin at runtime-bundle-build time, or read as actual env vars at server startup. They are NOT affected by the user's defines from `define-env.ts`.
    - **Gotcha**: DefinePlugin entries in `next-runtime.webpack-config.js` must be scoped to the correct `bundleType` (e.g. `app` only, not `server`) to avoid replacing assignment targets in `next-server.ts`.
    
    ## Related Skills
    
    - `$dce-edge` - DCE-safe require patterns and edge constraints
    - `$react-vendoring` - entry-base boundaries and vendored React
    - `$runtime-debug` - reproduction and verification workflow
    
  • .claude-plugin/marketplace.jsonmarketplace
    Show content (455 bytes)
    {
      "name": "nextjs",
      "owner": {
        "name": "Vercel",
        "url": "https://vercel.com"
      },
      "plugins": [
        {
          "name": "cache-components",
          "source": "./plugins/cache-components",
          "description": "Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). Proactively activates in projects with cacheComponents enabled.",
          "version": "1.0.0",
          "author": {
            "name": "Next.js Team"
          }
        }
      ]
    }
    

README

Next.js logo

Next.js

Vercel logo NPM version License Join the community on GitHub

Getting Started

Used by some of the world's largest companies, Next.js enables you to create full-stack web applications by extending the latest React features, and integrating powerful Rust-based JavaScript tooling for the fastest builds.

Documentation

Visit https://nextjs.org/docs to view the full documentation.

Community

The Next.js community can be found on GitHub Discussions where you can ask questions, voice ideas, and share your projects with other people.

To chat with other community members you can join the Next.js Discord server.

Do note that our Code of Conduct applies to all Next.js community channels. Users are highly encouraged to read and adhere to it to avoid repercussions.

Contributing

Contributions to Next.js are welcome and highly appreciated. However, before you jump right into it, we would like you to review our Contribution Guidelines to make sure you have a smooth experience contributing to Next.js.

Good First Issues:

We have a list of good first issues that contain bugs that have a relatively limited scope. This is a great place for newcomers and beginners alike to get started, gain experience, and get familiar with our contribution process.


Security

If you believe you have found a security vulnerability in Next.js, we encourage you to responsibly disclose this and NOT open a public issue.

To participate in our Open Source Software Bug Bounty program, please email responsible.disclosure@vercel.com. We will add you to the program and provide further instructions for submitting your report.