new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

React Router · API · all subjects

router setup & creation

30 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Reveal client entry command

To reveal the default entry.client.tsx file if it is not visible in your app directory, run the CLI command: react-router reveal entry.client

Express server example with RSC Framework Mode build output

Example Express server using RSC Framework Mode build output: ```tsx import express from "express"; import requestHandler from "./build/server/index.js"; import { createRequestListener } from "@remix-run/node-fetch-server"; const app = express(); app.use( "/assets", express.static("build/client/assets", { immutable: true, maxAge: "1y", }), ); app.use(express.static("build/client")); app.use(createRequestListener(requestHandler)); app.listen(3000); ```

RSC Framework Mode Vite plugin export name

RSC Framework Mode uses an unstable Vite plugin exported as `unstable_reactRouterRSC` from @react-router/dev/vite. This plugin has a peer dependency on the experimental @vitejs/plugin-rsc plugin, which should be placed after the React Router RSC plugin in the Vite config.

RSC Framework Mode Vite config example

Example Vite configuration for RSC Framework Mode: ```tsx import { defineConfig } from "vite"; import { unstable_reactRouterRSC as reactRouterRSC } from "@react-router/dev/vite"; import rsc from "@vitejs/plugin-rsc"; export default defineConfig({ plugins: [reactRouterRSC(), rsc()], }); ```

RSC Framework Mode build output exports default request handler

The RSC Framework Mode server build file (build/server/index.js) exports a default request handler function with signature `(request: Request) => Promise<Response>` for document and data requests. This can be converted to a standard Node.js request listener using `createRequestListener` from @remix-run/node-fetch-server.

RSC Framework Mode custom entry files

RSC Framework Mode supports custom entry files in the app directory: app/entry.rsc.ts (or .tsx) for custom RSC server entry, app/entry.ssr.ts (or .tsx) for custom SSR server entry, and app/entry.client.tsx for custom client entry. If not found, React Router uses default entries. Use `react-router reveal entry.client`, `react-router reveal entry.rsc`, and `react-router reveal entry.ssr` to inspect generated defaults.

Custom RSC entry file required exports

Custom entry files must maintain required exports: entry.rsc.ts must export a default object with a `fetch` method, entry.ssr.ts must export a `generateHTML` function, and entry.client.tsx should handle client-side hydration.

RSC Framework Mode unsupported config options

The following options from react-router.config.ts are not currently supported in RSC Framework Mode: buildEnd, presets, serverBundles, and splitRouteModules.

RSC Data Mode Vite config example

Example Vite configuration for RSC Data Mode: ```ts import rsc from "@vitejs/plugin-rsc/plugin"; import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [ react(), rsc({ entries: { client: "src/entry.browser.tsx", rsc: "src/entry.rsc.tsx", ssr: "src/entry.ssr.tsx", }, }), ], }); ```

RSC Data Mode entry points overview

RSC Data Mode requires three entry points: (1) entry.ssr.tsx handles incoming requests, fetches RSC payload, and converts to HTML; (2) entry.rsc.tsx (React Server) matches requests to routes and generates RSC payloads; (3) entry.browser.tsx (Browser) hydrates generated HTML and sets callServer function for post-hydration server actions.

RSC entry.ssr.tsx example with Vite

Example entry.ssr.tsx for Vite RSC: ```tsx import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr"; import { renderToReadableStream as renderHTMLToReadableStream } from "react-dom/server.edge"; import { unstable_routeRSCServerRequest as routeRSCServerRequest, unstable_RSCStaticRouter as RSCStaticRouter, } from "react-router"; export async function generateHTML( request: Request, serverResponse: Response, ): Promise<Response> { return await routeRSCServerRequest({ request, serverResponse, createFromReadableStream, async renderHTML(getPayload, options) { const payload = await getPayload(); const formState = payload.type === "render" ? await payload.formState : undefined; const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent( "index", ); return await renderHTMLToReadableStream( <RSCStaticRouter getPayload={getPayload} />, { ...options, bootstrapScriptContent, formState, signal: request.signal, }, ); }, }); } ```

RSC entry.rsc.tsx example with Vite

Example entry.rsc.tsx for Vite RSC: ```tsx import { createTemporaryReferenceSet, decodeAction, decodeFormState, decodeReply, loadServerAction, renderToReadableStream, } from "@vitejs/plugin-rsc/rsc"; import { unstable_matchRSCServerRequest as matchRSCServerRequest } from "react-router"; import { routes } from "./routes/config"; function fetchServer(request: Request) { return matchRSCServerRequest({ createTemporaryReferenceSet, decodeAction, decodeFormState, decodeReply, loadServerAction, request, routes: routes(), generateResponse(match, options) { return new Response( renderToReadableStream(match.payload, options), { status: match.statusCode, headers: match.headers, }, ); }, }); } export default async function handler(request: Request) { const ssr = await import.meta.viteRsc.loadModule< typeof import("./entry.ssr") >("ssr", "index"); return ssr.generateHTML( request, await fetchServer(request), ); } ```

RSC entry.browser.tsx example with Vite

Example entry.browser.tsx for Vite RSC: ```tsx import { createFromReadableStream, createTemporaryReferenceSet, encodeReply, setServerCallback, } from "@vitejs/plugin-rsc/browser"; import { startTransition, StrictMode } from "react"; import { hydrateRoot } from "react-dom/client"; import { unstable_createCallServer as createCallServer, unstable_getRSCStream as getRSCStream, unstable_RSCHydratedRouter as RSCHydratedRouter, type unstable_RSCPayload as RSCPayload, } from "react-router/dom"; setServerCallback( createCallServer({ createFromReadableStream, createTemporaryReferenceSet, encodeReply, }), ); createFr omReadableStream<RSCPayload>(getRSCStream()).then( (payload) => { startTransition(async () => { const formState = payload.type === "render" ? await payload.formState : undefined; hydrateRoot( document, <StrictMode> <RSCHydratedRouter createFromReadableStream={ createFromReadableStream } payload={payload} /> </StrictMode>, { formState, }, ); }); }, ); ```

Server and SSR separation in RSC

Even though an RSC application has both a React Server and a server responsible for request handling/SSR, these do not need to be 2 separate servers. They can be 2 separate module graphs within the same server, which is important because React behaves differently when generating RSC payloads versus generating HTML for client hydration.

Default RSC entries location

Default RSC entries are located at: @react-router/dev/config/default-rsc-entries/entry.rsc, @react-router/dev/config/default-rsc-entries/entry.ssr, and @react-router/dev/config/default-rsc-entries/entry.client. They can be viewed on GitHub or directly navigated in node_modules/@react-router/dev/dist/config/default-rsc-entries/.

RSC Framework Mode custom entry basic override pattern

To create a custom RSC entry file, import the default entry and wrap or extend its behavior. The default export should maintain required structure (fetch method for entry.rsc.ts, generateHTML function for entry.ssr.tsx). Include `if (import.meta.hot) { import.meta.hot.accept(); }` for HMR support.

Router disposal and HMR integration

Since the router is created outside the React tree, it can be disposed on HMR using: if (import.meta.hot) { import.meta.hot.dispose(() => router.dispose()); }

History inlined as router implementation detail

The history library was inlined into the router as an implementation detail rather than kept as a separate dependency. This allows the router to eventually adopt the Navigation API without breaking changes and simplifies the data-aware routing model where history and routing are more intertwined.

createRoutesFromElements helper function

The createRoutesFromElements function (aliased as createRoutesFromChildren) allows developers who prefer JSX notation to convert JSX Route elements into a route object array for createBrowserRouter. Example: const routes = createRoutesFromElements(<Route path='/' element={<Layout />}><Route index element={<Home />} /></Route>);

createBrowserRouter and RouterProvider pattern

The router singleton is created outside the React tree using createBrowserRouter() which accepts an array of route objects. The router is then passed to RouterProvider: const router = createBrowserRouter([...]); return <RouterProvider router={router} />. This replaces the old DataBrowserRouter component.

Feature flag implementation for strangler pattern

The server-side migration uses a strangler pattern with a runtime-agnostic feature flag (ENABLE_REMIX_ROUTER) to enable new behavior. This flag is initially committed as false and toggled to true during local development and testing. When enabled, the new static handler processes requests in parallel and asserts that results match the old approach. The flag cannot use process.env since the code is runtime agnostic, so a local hardcoded variable in server.ts is used instead.

Config APIs exported from @react-router/dev/config

Configuration object exported from `react-router.config.ts` should satisfy the `Config` type from `@react-router/dev/config`, following the pattern of dev-time APIs scoped to particular files like `@react-router/dev/routes` and `@react-router/dev/vite`.

react-router.config.ts is optional but recommended

While the `react-router.config.ts` file is optional and its absence is not treated as an error, it is recommended and included as a blank file in all official templates for discoverability and self-documentation of config options.

Vite plugin no longer accepts config options

The Vite plugin will no longer accept framework config options. All framework configuration options are now handled exclusively through the dedicated `react-router.config.ts` file.

Motivation for dedicated react-router.config.ts file

The dedicated config file decouples framework config from Vite, enables granular config watching for CLI tools like `react-router typegen --watch`, avoids unnecessary dev server reloads on config changes, and improves documentation by separating framework config from Vite config.

react-router.config.ts uses default export

The configuration object in `react-router.config.ts` is exported as a default export, following the pattern of other JavaScript build tool configurations.

react-router.config.ts file location and purpose

A dedicated `react-router.config.ts` or `react-router.config.js` file is introduced in the root of the project to decouple framework config from Vite and enable granular config watching for CLI tools.

Create a new React Router app with npm

To create a new React Router app, run the command 'npm create react-router'.

Architect integration with React Router

The @react-router/architect package provides a server request handler for React Router that works with Architect servers (as documented at arc.codes).

@react-router/architect package installation

The @react-router/architect package is installed via npm with the command: npm install @react-router/architect. This package provides an Architect server request handler for React Router.

Give your agent this brain