diff --git a/docs/api/components/ServerRouter.md b/docs/api/components/ServerRouter.md
index 813988fff1..bb33c85e0d 100644
--- a/docs/api/components/ServerRouter.md
+++ b/docs/api/components/ServerRouter.md
@@ -10,7 +10,7 @@ title: ServerRouter
[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react_router.ServerRouter.html)
-Rendered at the top of the app in a custom entry.server.tsx.
+Rendered at the top of the app in a custom [entry.server.tsx][entry-server].
## Props
@@ -31,3 +31,5 @@ _No documentation_
[modes: framework]
_No documentation_
+
+[entry-server]: ../framework-conventions/entry.server.tsx
diff --git a/docs/api/data-routers/index.md b/docs/api/data-routers/index.md
index 4c08fcb58a..eddd554727 100644
--- a/docs/api/data-routers/index.md
+++ b/docs/api/data-routers/index.md
@@ -1,4 +1,4 @@
---
title: Data Routers
+order: 4
---
-
diff --git a/docs/api/declarative-routers/index.md b/docs/api/declarative-routers/index.md
index ef2329fd66..f5edcdf78a 100644
--- a/docs/api/declarative-routers/index.md
+++ b/docs/api/declarative-routers/index.md
@@ -1,4 +1,4 @@
---
title: Declarative Routers
+order: 5
---
-
diff --git a/docs/api/framework-conventions/client-modules.md b/docs/api/framework-conventions/client-modules.md
new file mode 100644
index 0000000000..5b0bed6f2c
--- /dev/null
+++ b/docs/api/framework-conventions/client-modules.md
@@ -0,0 +1,114 @@
+---
+title: .client modules
+---
+
+# `.client` modules
+
+[MODES: framework]
+
+## Summary
+
+You may have a file or dependency that uses module side effects in the browser. You can use `*.client.ts` on file names or nest files within `.client` directories to force them out of server bundles.
+
+```ts filename=feature-check.client.ts
+// this would break the server
+export const supportsVibrationAPI =
+ "vibrate" in window.navigator;
+```
+
+Note that values exported from this module will all be `undefined` on the server, so the only places to use them are in [`useEffect`][use_effect] and user events like click handlers.
+
+```ts
+import { supportsVibrationAPI } from "./feature-check.client.ts";
+
+console.log(supportsVibrationAPI);
+// server: undefined
+// client: true | false
+```
+
+## Usage Patterns
+
+### Individual Files
+
+Mark individual files as client-only by adding `.client` to the filename:
+
+```txt
+app/
+├── utils.client.ts 👈 client-only file
+├── feature-detection.client.ts
+└── root.tsx
+```
+
+### Client Directories
+
+Mark entire directories as client-only by using `.client` in the directory name:
+
+```txt
+app/
+├── .client/ 👈 entire directory is client-only
+│ ├── analytics.ts
+│ ├── feature-detection.ts
+│ └── browser-utils.ts
+├── components/
+└── root.tsx
+```
+
+## Examples
+
+### Browser Feature Detection
+
+```ts filename=app/utils/browser.client.ts
+export const canUseDOM = typeof window !== "undefined";
+
+export const hasWebGL = !!window.WebGLRenderingContext;
+
+export const supportsVibrationAPI =
+ "vibrate" in window.navigator;
+```
+
+### Client-Only Libraries
+
+```ts filename=app/analytics.client.ts
+// This would break on the server
+import { track } from "some-browser-only-analytics-lib";
+
+export function trackEvent(eventName: string, data: any) {
+ track(eventName, data);
+}
+```
+
+### Using Client Modules
+
+```tsx filename=app/routes/dashboard.tsx
+import { useEffect } from "react";
+import {
+ canUseDOM,
+ supportsLocalStorage,
+ supportsVibrationAPI,
+} from "../utils/browser.client.ts";
+import { trackEvent } from "../analytics.client.ts";
+
+export default function Dashboard() {
+ useEffect(() => {
+ // These values are undefined on the server
+ if (canUseDOM && supportsVibrationAPI) {
+ console.log("Device supports vibration");
+ }
+
+ // Safe localStorage usage
+ const savedTheme =
+ supportsLocalStorage.getItem("theme");
+ if (savedTheme) {
+ document.body.className = savedTheme;
+ }
+
+ trackEvent("dashboard_viewed", {
+ timestamp: Date.now(),
+ });
+ }, []);
+
+ return
Dashboard
;
+}
+```
+
+[use_effect]: https://react.dev/reference/react/useEffect
diff --git a/docs/api/framework-conventions/entry.client.tsx.md b/docs/api/framework-conventions/entry.client.tsx.md
new file mode 100644
index 0000000000..57adcbd9c0
--- /dev/null
+++ b/docs/api/framework-conventions/entry.client.tsx.md
@@ -0,0 +1,43 @@
+---
+title: entry.client.tsx
+order: 4
+---
+
+# entry.client.tsx
+
+[MODES: framework]
+
+## Summary
+
+
+This file is optional
+
+
+This file is the entry point for the browser and is responsible for hydrating the markup generated by the server in your [server entry module][server-entry]
+
+This is the first piece of code that runs in the browser. You can initialize any other client-side code here, such as client side libraries, add client only providers, etc.
+
+```tsx filename=app/entry.client.tsx
+import { startTransition, StrictMode } from "react";
+import { hydrateRoot } from "react-dom/client";
+import { HydratedRouter } from "react-router/dom";
+
+startTransition(() => {
+ hydrateRoot(
+ document,
+
+
+
+ );
+});
+```
+
+## Generating `entry.client.tsx`
+
+By default, React Router will handle hydrating your app on the client for you. You can reveal the default entry client file with the following:
+
+```shellscript nonumber
+npx react-router reveal
+```
+
+[server-entry]: ./entry.server.tsx
diff --git a/docs/api/framework-conventions/entry.server.tsx.md b/docs/api/framework-conventions/entry.server.tsx.md
new file mode 100644
index 0000000000..657f77b49b
--- /dev/null
+++ b/docs/api/framework-conventions/entry.server.tsx.md
@@ -0,0 +1,162 @@
+---
+title: entry.server.tsx
+order: 5
+---
+
+# entry.server.tsx
+
+[MODES: framework]
+
+## Summary
+
+
+This file is optional
+
+
+This file is the server-side entry point that controls how your React Router application generates HTTP responses on the server.
+
+This module should render the markup for the current page using a [``][serverrouter] element with the `context` and `url` for the current request. This markup will (optionally) be re-hydrated once JavaScript loads in the browser using the [client entry module][client-entry].
+
+## Generating `entry.server.tsx`
+
+By default, React Router will handle generating the HTTP Response for you. You can reveal the default entry server file with the following:
+
+```shellscript nonumber
+npx react-router reveal
+```
+
+## Exports
+
+### `default`
+
+The `default` export of this module is a function that lets you create the response, including HTTP status, headers, and HTML, giving you full control over the way the markup is generated and sent to the client.
+
+```tsx filename=app/entry.server.tsx
+import { PassThrough } from "node:stream";
+import type { EntryContext } from "react-router";
+import { createReadableStreamFromReadable } from "@react-router/node";
+import { ServerRouter } from "react-router";
+import { renderToPipeableStream } from "react-dom/server";
+
+export default function handleRequest(
+ request: Request,
+ responseStatusCode: number,
+ responseHeaders: Headers,
+ routerContext: EntryContext
+) {
+ return new Promise((resolve, reject) => {
+ const { pipe, abort } = renderToPipeableStream(
+ ,
+ {
+ onShellReady() {
+ responseHeaders.set("Content-Type", "text/html");
+
+ const body = new PassThrough();
+ const stream =
+ createReadableStreamFromReadable(body);
+
+ resolve(
+ new Response(stream, {
+ headers: responseHeaders,
+ status: responseStatusCode,
+ })
+ );
+
+ pipe(body);
+ },
+ onShellError(error: unknown) {
+ reject(error);
+ },
+ }
+ );
+ });
+}
+```
+
+### `streamTimeout`
+
+If you are [streaming] responses, you can export an optional `streamTimeout` value (in milliseconds) that will control the amount of time the server will wait for streamed promises to settle before rejecting outstanding promises and closing the stream.
+
+It's recommended to decouple this value from the timeout in which you abort the React renderer. You should always set the React rendering timeout to a higher value so it has time to stream down the underlying rejections from your `streamTimeout`.
+
+```tsx lines=[1-2,13-15]
+// Reject all pending promises from handler functions after 10 seconds
+export const streamTimeout = 10000;
+
+export default function handleRequest(...) {
+ return new Promise((resolve, reject) => {
+ // ...
+
+ const { pipe, abort } = renderToPipeableStream(
+ ,
+ { /* ... */ }
+ );
+
+ // Abort the streaming render pass after 11 seconds to allow the rejected
+ // boundaries to be flushed
+ setTimeout(abort, streamTimeout + 1000);
+ });
+}
+```
+
+### `handleDataRequest`
+
+You can export an optional `handleDataRequest` function that will allow you to modify the response of a data request. These are the requests that do not render HTML, but rather return the `loader` and `action` data to the browser once client-side hydration has occurred.
+
+```tsx
+export function handleDataRequest(
+ response: Response,
+ {
+ request,
+ params,
+ context,
+ }: LoaderFunctionArgs | ActionFunctionArgs
+) {
+ response.headers.set("X-Custom-Header", "value");
+ return response;
+}
+```
+
+### `handleError`
+
+By default, React Router will log encountered server-side errors to the console. If you'd like more control over the logging, or would like to also report these errors to an external service, then you can export an optional `handleError` function which will give you control (and will disable the built-in error logging).
+
+```tsx
+export function handleError(
+ error: unknown,
+ {
+ request,
+ params,
+ context,
+ }: LoaderFunctionArgs | ActionFunctionArgs
+) {
+ if (!request.signal.aborted) {
+ sendErrorToErrorReportingService(error);
+ console.error(formatErrorForJsonLogging(error));
+ }
+}
+```
+
+_Note that you generally want to avoid logging when the request was aborted, since React Router's cancellation and race-condition handling can cause a lot of requests to be aborted._
+
+**Streaming Rendering Errors**
+
+When you are streaming your HTML responses via [`renderToPipeableStream`][rendertopipeablestream] or [`renderToReadableStream`][rendertoreadablestream], your own `handleError` implementation will only handle errors encountered during the initial shell render. If you encounter a rendering error during subsequent streamed rendering you will need to handle these errors manually since the React Router server has already sent the Response by that point.
+
+For `renderToPipeableStream`, you can handle these errors in the `onError` callback function. You will need to toggle a boolean in `onShellReady` so you know if the error was a shell rendering error (and can be ignored) or an async
+
+For an example, please refer to the default [`entry.server.tsx`][node-streaming-entry-server] for Node.
+
+**Thrown Responses**
+
+Note that this does not handle thrown `Response` instances from your `loader`/`action` functions. The intention of this handler is to find bugs in your code which result in unexpected thrown errors. If you are detecting a scenario and throwing a 401/404/etc. `Response` in your `loader`/`action` then it's an expected flow that is handled by your code. If you also wish to log, or send those to an external service, that should be done at the time you throw the response.
+
+[client-entry]: ./entry.client.tsx
+[serverrouter]: ../components/ServerRouter
+[streaming]: ../how-to/suspense
+[rendertopipeablestream]: https://react.dev/reference/react-dom/server/renderToPipeableStream
+[rendertoreadablestream]: https://react.dev/reference/react-dom/server/renderToReadableStream
+[node-streaming-entry-server]: https://github.com/remix-run/react-router/blob/dev/packages/react-router-dev/config/defaults/entry.server.node.tsx
diff --git a/docs/api/framework-conventions/index.md b/docs/api/framework-conventions/index.md
new file mode 100644
index 0000000000..33a5ff320f
--- /dev/null
+++ b/docs/api/framework-conventions/index.md
@@ -0,0 +1,4 @@
+---
+title: Framework Conventions
+order: 3
+---
diff --git a/docs/api/framework-conventions/react-router.config.ts.md b/docs/api/framework-conventions/react-router.config.ts.md
new file mode 100644
index 0000000000..60866f44c3
--- /dev/null
+++ b/docs/api/framework-conventions/react-router.config.ts.md
@@ -0,0 +1,178 @@
+---
+title: react-router.config.ts
+order: 3
+---
+
+# react-router.config.ts
+
+[MODES: framework]
+
+## Summary
+
+
+This file is optional
+
+
+[Reference Documentation ↗](https://api.reactrouter.com/dev/types/_react_router_dev.config.Config.html)
+
+React Router framework configuration file that lets you customize aspects of your React Router application like server-side rendering, directory locations, and build settings.
+
+```tsx filename=react-router.config.ts
+import type { Config } from "@react-router/dev/config";
+
+export default {
+ appDirectory: "app",
+ buildDirectory: "build",
+ ssr: true,
+ prerender: ["/", "/about"],
+} satisfies Config;
+```
+
+## Options
+
+### `appDirectory`
+
+The path to the `app` directory, relative to the root directory. Defaults to `"app"`.
+
+```tsx filename=react-router.config.ts
+export default {
+ appDirectory: "src",
+} satisfies Config;
+```
+
+### `basename`
+
+The React Router app basename. Defaults to `"/"`.
+
+```tsx filename=react-router.config.ts
+export default {
+ basename: "/my-app",
+} satisfies Config;
+```
+
+### `buildDirectory`
+
+The path to the build directory, relative to the project. Defaults to `"build"`.
+
+```tsx filename=react-router.config.ts
+export default {
+ buildDirectory: "dist",
+} satisfies Config;
+```
+
+### `buildEnd`
+
+A function that is called after the full React Router build is complete.
+
+```tsx filename=react-router.config.ts
+export default {
+ buildEnd: async ({ buildManifest, serverBuildPath }) => {
+ // Custom build logic here
+ console.log("Build completed!");
+ },
+} satisfies Config;
+```
+
+### `future`
+
+Enabled future flags for opting into upcoming features.
+
+See [Future Flags][future-flags] for more information.
+
+```tsx filename=react-router.config.ts
+export default {
+ future: {
+ // Enable future flags here
+ },
+} satisfies Config;
+```
+
+### `prerender`
+
+An array of URLs to prerender to HTML files at build time. Can also be a function returning an array to dynamically generate URLs.
+
+See [Pre-Rendering][pre-rendering] for more information.
+
+```tsx filename=react-router.config.ts
+export default {
+ // Static array
+ prerender: ["/", "/about", "/contact"],
+
+ // Or dynamic function
+ prerender: async ({ getStaticPaths }) => {
+ const paths = await getStaticPaths();
+ return ["/", ...paths];
+ },
+} satisfies Config;
+```
+
+### `presets`
+
+An array of React Router plugin config presets to ease integration with other platforms and tools.
+
+See [Presets][presets] for more information.
+
+```tsx filename=react-router.config.ts
+export default {
+ presets: [
+ // Add presets here
+ ],
+} satisfies Config;
+```
+
+### `serverBuildFile`
+
+The file name of the server build output. This file should end in a `.js` extension and should be deployed to your server. Defaults to `"index.js"`.
+
+```tsx filename=react-router.config.ts
+export default {
+ serverBuildFile: "server.js",
+} satisfies Config;
+```
+
+### `serverBundles`
+
+A function for assigning routes to different server bundles. This function should return a server bundle ID which will be used as the bundle's directory name within the server build directory.
+
+See [Server Bundles][server-bundles] for more information.
+
+```tsx filename=react-router.config.ts
+export default {
+ serverBundles: ({ branch }) => {
+ // Return bundle ID based on route branch
+ return branch.some((route) => route.id === "admin")
+ ? "admin"
+ : "main";
+ },
+} satisfies Config;
+```
+
+### `serverModuleFormat`
+
+The output format of the server build. Defaults to `"esm"`.
+
+```tsx filename=react-router.config.ts
+export default {
+ serverModuleFormat: "cjs", // or "esm"
+} satisfies Config;
+```
+
+### `ssr`
+
+If `true`, React Router will server render your application.
+
+If `false`, React Router will pre-render your application and save it as an `index.html` file with your assets so your application can be deployed as a SPA without server-rendering. See ["SPA Mode"][spa-mode] for more information.
+
+Defaults to `true`.
+
+```tsx filename=react-router.config.ts
+export default {
+ ssr: false, // disabled server-side rendering
+} satisfies Config;
+```
+
+[future-flags]: ../../upgrading/future
+[presets]: ../../how-to/presets
+[server-bundles]: ../../how-to/server-bundles
+[pre-rendering]: ../../how-to/pre-rendering
+[spa-mode]: ../../how-to/spa
diff --git a/docs/api/framework-conventions/root.tsx.md b/docs/api/framework-conventions/root.tsx.md
new file mode 100644
index 0000000000..b9822d7b9c
--- /dev/null
+++ b/docs/api/framework-conventions/root.tsx.md
@@ -0,0 +1,199 @@
+---
+title: root.tsx
+order: 1
+---
+
+# root.tsx
+
+[MODES: framework]
+
+## Summary
+
+
+This file is required
+
+
+The "root" route (`app/root.tsx`) is the only _required_ route in your React Router application because it is the parent to all routes and is in charge of rendering the root `` document.
+
+```tsx filename=app/root.tsx
+import { Outlet, Scripts } from "react-router";
+
+import "./global-styles.css";
+
+export default function App() {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+## Components to Render
+
+Because the root route manages your document, it is the proper place to render a handful of "document-level" components React Router provides. These components are to be used once inside your root route and they include everything React Router figured out or built in order for your page to render properly.
+
+```tsx filename=app/root.tsx
+import {
+ Outlet,
+ Scripts,
+ ScrollRestoration,
+} from "react-router";
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ {/* Child routes render here */}
+
+
+ {/* Manages scroll position for client-side transitions */}
+ {/* If you use a nonce-based content security policy for scripts, you must provide the `nonce` prop. Otherwise, omit the nonce prop as shown here. */}
+
+
+ {/* Script tags go here */}
+ {/* If you use a nonce-based content security policy for scripts, you must provide the `nonce` prop. Otherwise, omit the nonce prop as shown here. */}
+
+
+
+ );
+}
+```
+
+If you are are not on React 19 or choosing not to use React's [``][react-link], [``][react-title], and [``][react-meta] components, and instead relying on React Router's [`links`][react-router-links] and [`meta`][react-router-meta] exports, you need to add the following to your root route:
+
+```tsx filename=app/root.tsx
+import { Links, Meta } from "react-router";
+
+export default function App() {
+ return (
+
+
+ {/* All `meta` exports on all routes will render here */}
+
+
+ {/* All `link` exports on all routes will render here */}
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+## Layout Export
+
+The root route supports all [route module exports][route-module].
+
+The root route also supports an additional optional `Layout` export. The `Layout` component serves 2 purposes:
+
+1. Avoid duplicating your document's "app shell" across your root component, `HydrateFallback`, and `ErrorBoundary`
+2. Prevent React from re-mounting your app shell elements when switching between the root component/`HydrateFallback`/`ErrorBoundary` which can cause a FOUC if React removes and re-adds `` tags from your `` component.
+
+`Layout` takes a single `children` prop, which is the `default` export (e.g. `App`), `HydrateFallback`, or `ErrorBoundary`.
+
+```tsx filename=app/root.tsx
+export function Layout({ children }) {
+ return (
+
+
+
+
+
+
+
+
+ {/* children will be the root Component, ErrorBoundary, or HydrateFallback */}
+ {children}
+
+
+
+
+ );
+}
+
+export default function App() {
+ return ;
+}
+
+export function ErrorBoundary() {}
+```
+
+**A note on `useLoaderData`in the `Layout` Component**
+
+`useLoaderData` is not permitted to be used in `ErrorBoundary` components because it is intended for the happy-path route rendering, and its typings have a built-in assumption that the `loader` ran successfully and returned something. That assumption doesn't hold in an `ErrorBoundary` because it could have been the `loader` that threw and triggered the boundary! In order to access loader data in `ErrorBoundary`'s, you can use `useRouteLoaderData` which accounts for the loader data potentially being `undefined`.
+
+Because your `Layout` component is used in both success and error flows, this same restriction holds. If you need to fork logic in your `Layout` depending on if it was a successful request or not, you can use `useRouteLoaderData("root")` and `useRouteError()`.
+
+Because your `` component is used for rendering the `ErrorBoundary`, you should be _very defensive_ to ensure that you can render your `ErrorBoundary` without encountering any render errors. If your `Layout` throws another error trying to render the boundary, then it can't be used and your UI will fall back to the very minimal built-in default `ErrorBoundary`.
+
+```tsx filename=app/root.tsx lines=[6-7,19-29,32-34]
+export function Layout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const data = useRouteLoaderData("root");
+ const error = useRouteError();
+
+ return (
+
+
+
+
+
+
+
+
+
+ {data ? (
+
+ ) : null}
+ {children}
+
+
+
+
+ );
+}
+```
+
+[route-module]: ../start/framework/route-module
+[react-link]: https://react.dev/reference/react-dom/components/link
+[react-meta]: https://react.dev/reference/react-dom/components/meta
+[react-title]: https://react.dev/reference/react-dom/components/title
+[react-router-links]: ../../start/framework/route-module#links
+[react-router-meta]: ../../start/framework/route-module#meta
diff --git a/docs/api/framework-conventions/routes.ts.md b/docs/api/framework-conventions/routes.ts.md
new file mode 100644
index 0000000000..a239937f30
--- /dev/null
+++ b/docs/api/framework-conventions/routes.ts.md
@@ -0,0 +1,67 @@
+---
+title: routes.ts
+order: 2
+---
+
+# routes.ts
+
+[MODES: framework]
+
+## Summary
+
+
+This file is required
+
+
+[Reference Documentation ↗](https://api.reactrouter.com/v7/interfaces/_react_router_dev.routes.RouteConfigEntry.html)
+
+Configuration file that maps URL patterns to route modules in your application.
+
+See the [routing guide][routing] for more information.
+
+## Examples
+
+### Basic
+
+Configure your routes as an array of objects.
+
+```tsx filename=app/routes.ts
+import {
+ type RouteConfig,
+ route,
+} from "@react-router/dev/routes";
+
+export default [
+ route("some/path", "./some/file.tsx"),
+ // pattern ^ ^ module file
+] satisfies RouteConfig;
+```
+
+You can use the following helpers to create route config entries:
+
+- [`route`][route] — Helper function for creating a route config entry
+- [`index`][index] — Helper function for creating a route config entry for an index route
+- [`layout`][layout] — Helper function for creating a route config entry for a layout route
+- [`prefix`][prefix] — Helper function for adding a path prefix to a set of routes without needing to introduce a parent route file
+- [`relative`][relative] — Creates a set of route config helpers that resolve file paths relative to the given directory. Designed to support splitting route config into multiple files within different directories
+
+### File-based Routing
+
+If you prefer to define your routes via file naming conventions rather than configuration, the `@react-router/fs-routes` package provides a [file system routing convention][file-route-conventions]:
+
+```ts filename=app/routes.ts
+import { type RouteConfig } from "@react-router/dev/routes";
+import { flatRoutes } from "@react-router/fs-routes";
+
+export default flatRoutes() satisfies RouteConfig;
+```
+
+### Route Helpers
+
+[routing]: ../../start/framework/routing
+[route]: https://api.reactrouter.com/dev/functions/_react_router_dev.routes.route.html
+[index]: https://api.reactrouter.com/dev/functions/_react_router_dev.routes.index.html
+[layout]: https://api.reactrouter.com/dev/functions/_react_router_dev.routes.layout.html
+[prefix]: https://api.reactrouter.com/dev/functions/_react_router_dev.routes.prefix.html
+[relative]: https://api.reactrouter.com/dev/functions/_react_router_dev.routes.relative.html
+[file-route-conventions]: ../../how-to/file-route-conventions
diff --git a/docs/api/framework-conventions/server-modules.md b/docs/api/framework-conventions/server-modules.md
new file mode 100644
index 0000000000..0bb842bf29
--- /dev/null
+++ b/docs/api/framework-conventions/server-modules.md
@@ -0,0 +1,145 @@
+---
+title: .server modules
+---
+
+# `.server` modules
+
+[MODES: framework]
+
+## Summary
+
+Server-only modules that are excluded from client bundles and only run on the server.
+
+```ts filename=auth.server.ts
+// This would expose secrets on the client
+export const JWT_SECRET = process.env.JWT_SECRET;
+
+export function validateToken(token: string) {
+ // Server-only authentication logic
+}
+```
+
+`.server` modules are a good way to explicitly mark entire modules as server-only. The build will fail if any code in a `.server` file or `.server` directory accidentally ends up in the client module graph.
+
+## Usage Patterns
+
+### Individual Files
+
+Mark individual files as server-only by adding `.server` to the filename:
+
+```txt
+app/
+├── auth.server.ts 👈 server-only file
+├── database.server.ts
+├── email.server.ts
+└── root.tsx
+```
+
+### Server Directories
+
+Mark entire directories as server-only by using `.server` in the directory name:
+
+```txt
+app/
+├── .server/ 👈 entire directory is server-only
+│ ├── auth.ts
+│ ├── database.ts
+│ └── email.ts
+├── components/
+└── root.tsx
+```
+
+## Examples
+
+### Database Connection
+
+```ts filename=app/utils/db.server.ts
+import { PrismaClient } from "@prisma/client";
+
+// This would expose database credentials on the client
+const db = new PrismaClient({
+ datasources: {
+ db: {
+ url: process.env.DATABASE_URL,
+ },
+ },
+});
+
+export { db };
+```
+
+### Authentication Utilities
+
+```ts filename=app/utils/auth.server.ts
+import jwt from "jsonwebtoken";
+import bcrypt from "bcryptjs";
+
+const JWT_SECRET = process.env.JWT_SECRET!;
+
+export function hashPassword(password: string) {
+ return bcrypt.hash(password, 10);
+}
+
+export function verifyPassword(
+ password: string,
+ hash: string
+) {
+ return bcrypt.compare(password, hash);
+}
+
+export function createToken(userId: string) {
+ return jwt.sign({ userId }, JWT_SECRET, {
+ expiresIn: "7d",
+ });
+}
+
+export function verifyToken(token: string) {
+ return jwt.verify(token, JWT_SECRET) as {
+ userId: string;
+ };
+}
+```
+
+### Using Server Modules
+
+```tsx filename=app/routes/login.tsx
+import type { ActionFunctionArgs } from "react-router";
+import { redirect } from "react-router";
+import {
+ hashPassword,
+ createToken,
+} from "../utils/auth.server";
+import { db } from "../utils/db.server";
+
+export async function action({
+ request,
+}: ActionFunctionArgs) {
+ const formData = await request.formData();
+ const email = formData.get("email") as string;
+ const password = formData.get("password") as string;
+
+ // Server-only operations
+ const hashedPassword = await hashPassword(password);
+ const user = await db.user.create({
+ data: { email, password: hashedPassword },
+ });
+
+ const token = createToken(user.id);
+
+ return redirect("/dashboard", {
+ headers: {
+ "Set-Cookie": `token=${token}; HttpOnly; Secure; SameSite=Strict`,
+ },
+ });
+}
+
+export default function Login() {
+ return (
+
+ );
+}
+```
diff --git a/docs/explanation/special-files.md b/docs/explanation/special-files.md
index 9447977159..ca8192f2e8 100644
--- a/docs/explanation/special-files.md
+++ b/docs/explanation/special-files.md
@@ -1,358 +1,16 @@
---
title: Special Files
+hidden: true
---
# Special Files
-There are a few special files that React Router looks for in your project. Not all of these files are required
+The content of this page has been moved to the following:
-## react-router.config.ts
-
-**This file is optional**
-
-The config file is used to configure certain aspects of your app, such as whether you are using server-side rendering, where certain directories are located, and more.
-
-```tsx filename=react-router.config.ts
-import type { Config } from "@react-router/dev/config";
-
-export default {
- // Config options...
-} satisfies Config;
-```
-
-See the details on [react-router config API][react-router-config] for more information.
-
-## root.tsx
-
-**This file is required**
-
-The "root" route (`app/root.tsx`) is the only _required_ route in your React Router application because it is the parent to all routes in your `routes/` directory and is in charge of rendering the root `` document.
-
-Because the root route manages your document, it is the proper place to render a handful of "document-level" components React Router provides. These components are to be used once inside your root route and they include everything React Router figured out or built in order for your page to render properly.
-
-```tsx filename=app/root.tsx
-import type { LinksFunction } from "react-router";
-import {
- Links,
- Meta,
- Outlet,
- Scripts,
- ScrollRestoration,
-} from "react-router";
-
-import "./global-styles.css";
-
-export default function App() {
- return (
-
-
-
-
-
- {/* All `meta` exports on all routes will render here */}
-
-
- {/* All `link` exports on all routes will render here */}
-
-
-
- {/* Child routes render here */}
-
-
- {/* Manages scroll position for client-side transitions */}
- {/* If you use a nonce-based content security policy for scripts, you must provide the `nonce` prop. Otherwise, omit the nonce prop as shown here. */}
-
-
- {/* Script tags go here */}
- {/* If you use a nonce-based content security policy for scripts, you must provide the `nonce` prop. Otherwise, omit the nonce prop as shown here. */}
-
-
-
- );
-}
-```
-
-### Layout export
-
-The root route supports all [route module exports][route-module].
-
-The root route also supports an additional optional `Layout` export. The `Layout` component serves 2 purposes:
-
-1. Avoid duplicating your document's "app shell" across your root component, `HydrateFallback`, and `ErrorBoundary`
-2. Prevent React from re-mounting your app shell elements when switching between the root component/`HydrateFallback`/`ErrorBoundary` which can cause a FOUC if React removes and re-adds `` tags from your `` component.
-
-```tsx filename=app/root.tsx lines=[10-31]
-export function Layout({ children }) {
- return (
-
-
-
-
-
-
-
-
- {/* children will be the root Component, ErrorBoundary, or HydrateFallback */}
- {children}
-
-
-
-
- );
-}
-
-export default function App() {
- return ;
-}
-
-export function ErrorBoundary() {}
-```
-
-**A note on `useLoaderData`in the `Layout` Component**
-
-`useLoaderData` is not permitted to be used in `ErrorBoundary` components because it is intended for the happy-path route rendering, and its typings have a built-in assumption that the `loader` ran successfully and returned something. That assumption doesn't hold in an `ErrorBoundary` because it could have been the `loader` that threw and triggered the boundary! In order to access loader data in `ErrorBoundary`'s, you can use `useRouteLoaderData` which accounts for the loader data potentially being `undefined`.
-
-Because your `Layout` component is used in both success and error flows, this same restriction holds. If you need to fork logic in your `Layout` depending on if it was a successful request or not, you can use `useRouteLoaderData("root")` and `useRouteError()`.
-
-Because your `` component is used for rendering the `ErrorBoundary`, you should be _very defensive_ to ensure that you can render your `ErrorBoundary` without encountering any render errors. If your `Layout` throws another error trying to render the boundary, then it can't be used and your UI will fall back to the very minimal built-in default `ErrorBoundary`.
-
-```tsx filename=app/root.tsx lines=[6-7,19-29,32-34]
-export function Layout({
- children,
-}: {
- children: React.ReactNode;
-}) {
- const data = useRouteLoaderData("root");
- const error = useRouteError();
-
- return (
-
-
-
-
-
-
-
-
-
- {data ? (
-
- ) : null}
- {children}
-
-
-
-
- );
-}
-```
-
-## routes.ts
-
-**This file is required**
-
-The `routes.ts` file is used to configure which url patterns are matched to which route modules.
-
-```tsx filename=app/routes.ts
-import {
- type RouteConfig,
- route,
-} from "@react-router/dev/routes";
-
-export default [
- route("some/path", "./some/file.tsx"),
- // pattern ^ ^ module file
-] satisfies RouteConfig;
-```
-
-See the [routing guide][routing] for more information.
-
-## entry.client.tsx
-
-**This file is optional**
-
-By default, React Router will handle hydrating your app on the client for you. You can reveal the default entry client file with the following:
-
-```shellscript nonumber
-react-router reveal
-```
-
-This file is the entry point for the browser and is responsible for hydrating the markup generated by the server in your [server entry module][server-entry], however you can also initialize any other client-side code here.
-
-```tsx filename=app/entry.client.tsx
-import { startTransition, StrictMode } from "react";
-import { hydrateRoot } from "react-dom/client";
-import { HydratedRouter } from "react-router/dom";
-
-startTransition(() => {
- hydrateRoot(
- document,
-
-
-
- );
-});
-```
-
-This is the first piece of code that runs in the browser. You can initialize client side libraries, add client only providers, etc.
-
-## entry.server.tsx
-
-**This file is optional**
-
-By default, React Router will handle generating the HTTP Response for you. You can reveal the default entry server file with the following:
-
-```shellscript nonumber
-react-router reveal
-```
-
-The `default` export of this module is a function that lets you create the response, including HTTP status, headers, and HTML, giving you full control over the way the markup is generated and sent to the client.
-
-This module should render the markup for the current page using a `` element with the `context` and `url` for the current request. This markup will (optionally) be re-hydrated once JavaScript loads in the browser using the [client entry module][client-entry].
-
-### `streamTimeout`
-
-If you are [streaming] responses, you can export an optional `streamTimeout` value (in milliseconds) that will control the amount of time the server will wait for streamed promises to settle before rejecting outstanding promises them and closing the stream.
-
-It's recommended to decouple this value from the timeout in which you abort the React renderer. You should always set the React rendering timeout to a higher value so it has time to stream down the underlying rejections from your `streamTimeout`.
-
-```tsx lines=[1-2,13-15]
-// Reject all pending promises from handler functions after 10 seconds
-export const streamTimeout = 10000;
-
-export default function handleRequest(...) {
- return new Promise((resolve, reject) => {
- // ...
-
- const { pipe, abort } = renderToPipeableStream(
- ,
- { /* ... */ }
- );
-
- // Abort the streaming render pass after 11 seconds to allow the rejected
- // boundaries to be flushed
- setTimeout(abort, streamTimeout + 1000);
- });
-}
-```
-
-### `handleDataRequest`
-
-You can export an optional `handleDataRequest` function that will allow you to modify the response of a data request. These are the requests that do not render HTML, but rather return the loader and action data to the browser once client-side hydration has occurred.
-
-```tsx
-export function handleDataRequest(
- response: Response,
- {
- request,
- params,
- context,
- }: LoaderFunctionArgs | ActionFunctionArgs
-) {
- response.headers.set("X-Custom-Header", "value");
- return response;
-}
-```
-
-### `handleError`
-
-By default, React Router will log encountered server-side errors to the console. If you'd like more control over the logging, or would like to also report these errors to an external service, then you can export an optional `handleError` function which will give you control (and will disable the built-in error logging).
-
-```tsx
-export function handleError(
- error: unknown,
- {
- request,
- params,
- context,
- }: LoaderFunctionArgs | ActionFunctionArgs
-) {
- if (!request.signal.aborted) {
- sendErrorToErrorReportingService(error);
- console.error(formatErrorForJsonLogging(error));
- }
-}
-```
-
-_Note that you generally want to avoid logging when the request was aborted, since React Router's cancellation and race-condition handling can cause a lot of requests to be aborted._
-
-### Streaming Rendering Errors
-
-When you are streaming your HTML responses via [`renderToPipeableStream`][rendertopipeablestream] or [`renderToReadableStream`][rendertoreadablestream], your own `handleError` implementation will only handle errors encountered during the initial shell render. If you encounter a rendering error during subsequent streamed rendering you will need to handle these errors manually since the React Router server has already sent the Response by that point.
-
-For `renderToPipeableStream`, you can handle these errors in the `onError` callback function. You will need to toggle a boolean in `onShellReady` so you know if the error was a shell rendering error (and can be ignored) or an async
-
-For an example, please refer to the default [`entry.server.tsx`][node-streaming-entry-server] for Node.
-
-**Thrown Responses**
-
-Note that this does not handle thrown `Response` instances from your `loader`/`action` functions. The intention of this handler is to find bugs in your code which result in unexpected thrown errors. If you are detecting a scenario and throwing a 401/404/etc. `Response` in your `loader`/`action` then it's an expected flow that is handled by your code. If you also wish to log, or send those to an external service, that should be done at the time you throw the response.
-
-## `.server` modules
-
-While not strictly necessary, `.server` modules are a good way to explicitly mark entire modules as server-only.
-The build will fail if any code in a `.server` file or `.server` directory accidentally ends up in the client module graph.
-
-```txt
-app
-├── .server 👈 marks all files in this directory as server-only
-│ ├── auth.ts
-│ └── db.ts
-├── cms.server.ts 👈 marks this file as server-only
-├── root.tsx
-└── routes.ts
-```
-
-`.server` modules must be within your app directory.
-
-Refer to the Route Module section in the sidebar for more information.
-
-## `.client` modules
-
-While uncommon, you may have a file or dependency that uses module side effects in the browser. You can use `*.client.ts` on file names or nest files within `.client` directories to force them out of server bundles.
-
-```ts filename=feature-check.client.ts
-// this would break the server
-export const supportsVibrationAPI =
- "vibrate" in window.navigator;
-```
-
-Note that values exported from this module will all be `undefined` on the server, so the only places to use them are in [`useEffect`][use_effect] and user events like click handlers.
-
-```ts
-import { supportsVibrationAPI } from "./feature-check.client.ts";
-
-console.log(supportsVibrationAPI);
-// server: undefined
-// client: true | false
-```
-
-[react-router-config]: https://api.reactrouter.com/v7/types/_react_router_dev.config.Config.html
-[route-module]: ../start/framework/route-module
-[routing]: ../start/framework/routing
-[server-entry]: #entryservertsx
-[client-entry]: #entryclienttsx
-[rendertopipeablestream]: https://react.dev/reference/react-dom/server/renderToPipeableStream
-[rendertoreadablestream]: https://react.dev/reference/react-dom/server/renderToReadableStream
-[node-streaming-entry-server]: https://github.com/remix-run/react-router/blob/dev/packages/react-router-dev/config/defaults/entry.server.node.tsx
-[streaming]: ../how-to/suspense
-[use_effect]: https://react.dev/reference/react/useEffect
+- [`react-router.config.ts`](../api/framework-conventions/react-router.config.ts) - Optional configuration file for your app
+- [`root.tsx`](../api/framework-conventions/root.tsx) - Required root route that renders the HTML document
+- [`routes.ts`](../api/framework-conventions/routes.ts) - Required route configuration mapping URLs to components
+- [`entry.client.tsx`](../api/framework-conventions/entry.client.tsx) - Optional client-side entry point for hydration
+- [`entry.server.tsx`](../api/framework-conventions/entry.server.tsx) - Optional server-side entry point for rendering
+- [`.server` modules](../api/framework-conventions/server-modules) - Server-only modules excluded from client bundles
+- [`.client` modules](../api/framework-conventions/client-modules) - Client-only modules excluded from server bundles