vite-plugin-react

repository·main·Indexed 22 days ago

https://github.com/vitejs/vite-plugin-react

Official Vite plugins for React, providing support for standard React development via @vitejs/plugin-react (Babel) and @vitejs/plugin-react-swc (SWC), as well as React Server Components (RSC) via @vitejs/plugin-rsc. Features include Fast Refresh, automatic JSX runtime, React Compiler support, and advanced RSC manifest generation for route-aware server actions.

Tokens
23K
Snippets
84
Records
114
Agent score
77%

What's inside vite-plugin-react

  1. Overview of @vitejs/plugin-rsc

    main

    The @vitejs/plugin-rsc package provides React Server Components (RSC) support for Vite. It is designed to be:

    • Framework-agnostic: Implements low-level RSC bundler features and provides the react-server-dom API without framework-specific abstractions.
    • Runtime-agnostic: Built on the Vite environment API, making it compatible with various runtimes (e.g., Cloudflare Workers).
    • HMR-enabled: Supports Hot Module Replacement for both client and server components.
    • CSS-ready: Automatically handles CSS code-splitting for both client and server components, injecting them upon rendering.
  2. Overview of Vite Plugin React and RSC packages

    main

    The vite-plugin-react repository provides official Vite plugins for React development. It is divided into three primary functional areas:

    1. Standard React Plugin (@vitejs/plugin-react): The default plugin for React projects using Babel for transformations.
    2. SWC-based React Plugin (@vitejs/plugin-react-swc): A high-performance alternative that uses SWC (Speedy Web Compiler) instead of Babel.
    3. React Server Components Plugin (@vitejs/plugin-rsc): Support for React Server Components (RSC) within the Vite ecosystem.

    For detailed installation and configuration instructions, refer to the specific package documentation linked in the package list.

  3. Prevent environment leakage with `server-only` and `client-only`

    main

    The plugin provides built-in validation for server-only and client-only imports to prevent sensitive code from leaking into client bundles or browser-specific code from being used on the server.

    • Importing server-only in a client build will trigger a build-time error.
    • Importing client-only in a server build will trigger a build-time error.

    Note: You do not need to install official npm packages for these; the plugin intercepts these imports internally. This validation can be disabled by setting validateImports: false in the plugin options.

    // client.js
    'use client'
    import { getData } from './server-utils.js' // ❌ 'server-only' cannot be imported in client build
  4. React compatibility for RSC performance tracks

    main

    When observing RSC performance tracks in Chrome DevTools, the visibility of async component spans depends on the React version's ability to recover debug information during the performance flush.

    • React 19.2.8 and earlier: These versions may show track markers in Chrome without the corresponding async component spans. This happens because debug information moved from initialized child chunks onto their resolved values can be lost during the performance flush.
    • React 19.3.0+ (or versions with fix #34839): React recovers moved debug information from the resolved value during the performance flush, allowing the full component spans to appear in the DevTools tracks.
    • Workaround for older React versions: Frameworks like Waku apply a transform (equivalent to recovering debug information) to support RSC performance tracking on React versions that lack the native fix.
  5. Understand the Partial Prerendering (PPR) Request Flow

    main

    In a Partial Prerendering (PPR) architecture, a request is fulfilled by combining persisted static outputs with fresh request-time data. The flow works as follows:

    1. Response Prefix: The server immediately streams a persisted HTML prelude (static output).
    2. RSC Render: In parallel, @vitejs/plugin-rsc/rsc/server's renderToReadableStream() produces fresh Flight data. In this phase, markDynamic() no longer suspends, and DynamicContent can access current request data.
    3. Response Suffix: react-dom/server.edge's resume() method combines the decoded fresh Flight tree with persisted postponed state to produce the HTML continuation.
    4. Hydration: The fresh Flight stream is split: one branch is consumed by React DOM for the HTML continuation, and the other is injected into the response as browser hydration data.

    This model allows an edge or CDN to serve the static prelude while a dynamic backend produces the resumed stream.

  6. Ensure consistent component exports for React Refresh

    main

    For React Fast Refresh to work correctly, files should ideally only export React components. If a module exports incompatible types (like non-component constants), the module may be invalidated, causing HMR to trigger a full reload.

    To minimize impact, the plugin only invalidates the module when the value of the non-component export changes. You can use ESLint (eslint-plugin-react-refresh) or Oxlint to catch these issues.

  7. Ensure consistent component exports for Fast Refresh

    main

    For React Fast Refresh to work correctly, files should only export React components.

    If a module exports incompatible types (like non-component constants), the module will be invalidated and HMR will trigger a full reload. While the plugin attempts to mitigate this by only invalidating when the value of a constant changes, it is best practice to keep exports limited to components.

    To catch these issues, use linting rules from eslint-plugin-react-refresh, oxc, or biome.

  8. Override `__VITE_ENVIRONMENT_RUNNER_IMPORT__` for custom environments

    main

    The global __VITE_ENVIRONMENT_RUNNER_IMPORT__ function is used by import.meta.viteRsc.loadModule to import modules in a target environment during development. While the plugin provides a default implementation that uses the environment's module runner, frameworks with custom setups (like separate workers) can override this global to provide custom module loading logic.

    // Custom logic to import module between multiple environments inside worker
    globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__ = async (environmentName, id) => {
      return myWorkerRunners[environmentname].import(id)
    }
  9. How to implement custom Server Function directives with @vitejs/plugin-rsc

    main

    When extending React Server Components (RSC) with custom directives (e.g., "use custom-server" instead of the built-in "use server"), responsibilities are split between two layers:

    1. Directive Owner (Third-party Plugin): Responsible for transforming the custom syntax and registering the function with the React runtime.
    2. Bundler Layer (@vitejs/plugin-rsc): Responsible for module identity, graph visibility, manifests, and reference resolution.

    To integrate a custom directive, a third-party Vite plugin should transform the custom syntax and report its exports as server reference claims. @vitejs/plugin-rsc will then aggregate these custom claims with the built-in "use server" claims. This allows custom syntax and metadata policies to remain decoupled from the RSC plugin while maintaining a single canonical reference identity for the bundler.

  10. How the PPR Build Flow works

    main

    The build process for PPR involves three distinct steps to ensure a reusable static shell is created around dynamic content:

    1. Warm the RSC cache: Use @vitejs/plugin-rsc/rsc/static prerender() to discover cache misses and wait for static work to complete.
    2. Capture partial Flight: Perform a second pass with the same prerender() function against the warm cache. This pass captures the static Flight prefix up to the point where DynamicContent (via markDynamic()) suspends.
    3. Capture resumable HTML: Use react-dom/static.edge prerender() to consume the partial Flight tree. This produces an HTML prelude and a serializable postponed state that describes how to resume rendering at request time.

    This multi-phase approach ensures that cache discovery is explicit and that the final artifact (the Flight prelude) is generated with precise control over readiness and cutoff.

  11. Understand Cross-Environment Action Reachability

    main

    Cross-Environment Action Reachability is a feature in @vitejs/plugin-rsc that enables route-aware dispatch for retained server actions.

    In a typical scenario, a server action (Action A) might be reachable via the application graph of Route /a but not Route /b. If a user navigates from /a to /b while retaining the server reference for Action A, the plugin allows the action to be invoked on Route /b by redispatching the request to the correct route's middleware.

    Behavior by Environment

    ModeExecution PathResultRendered Page
    Production/a middlewareACTION_A_OK:MIDDLEWARE_A/b
    Development/b middlewareACTION_A_OK:MIDDLEWARE_B/b

    In Production, a generated route-action manifest allows the RSC handler to redispatch the action request through the middleware of the route that actually reaches the action, while preserving the current render URL. In Development, this redispatch is skipped to simplify debugging, and the action executes on the current route.

  12. Understand the Client-first RSC architecture

    main

    The Client-first RSC (React Server Components) model allows rendering RSC values inside a client-owned page. In this pattern, the client page reads a cached RSC-function promise using React use, allowing the page to maintain ordinary client state while integrating server-side logic.

    Key architectural components in this implementation include:

    • Co-located Routes: routes/page.tsx contains both the page component and the RSC-function handler.
    • RSC Runtime: runtime.tsx provides a callable RSC-function stub and caches the promise to support React Suspense.
    • RSC Execution: entry.rsc.tsx executes RSC functions and encodes results as Flight streams.
    • SSR Configuration: entry.ssr.tsx sets up an in-process RSC caller for HTML rendering.
    • Browser Configuration: entry.browser.tsx sets up an HTTP RSC caller for hydration.

    Note: In this current sketch, SSR and the browser execute the RSC function independently to keep serialization transport separate from the core client-first model. There is no SSR-to-browser data handoff implemented in this specific example.