diff --git a/docs/explanation/special-files.md b/docs/explanation/special-files.md new file mode 100644 index 0000000000..4abf8262b5 --- /dev/null +++ b/docs/explanation/special-files.md @@ -0,0 +1,291 @@ +--- +title: Special Files +--- + +# Special Files + +There are a few special files that React Router looks for in your project. Not all of these files are required + +## 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 [config API](https://api.reactrouter.com/v7/types/_react_router_dev.config.Config.html) 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 ( + + + + + + +