UNPKG

react-router

Version:
1,343 lines (1,331 loc) 92.8 kB
import './components-CuPfnyiZ.mjs'; import * as React from 'react'; import { az as RouteManifest, M as MiddlewareEnabled, o as unstable_RouterContextProvider, j as LoaderFunctionArgs, i as ActionFunctionArgs, J as StaticHandlerContext, H as HydrationState, z as DataRouteObject, a2 as HTMLFormMethod, a0 as FormEncType, a as RelativeRoutingType, T as To, aA as History, E as GetScrollRestorationKeyFunction, e as RouterInit, F as FutureConfig$1, D as DataStrategyFunction, P as PatchRoutesOnNavigationFunction, q as NavigateOptions, K as Fetcher, f as RouteObject, R as Router, B as BlockerFunction, c as Location, aB as CreateStaticHandlerOptions$1, C as StaticHandler } from './context-DohQKLID.mjs'; import { o as ServerRouteModule, A as AppLoadContext, c as RouteModules, h as ClientLoaderFunction, m as PageLinkDescriptor, d as SerializeFrom } from './route-data-CpB5xtMm.mjs'; type ServerRouteManifest = RouteManifest<Omit<ServerRoute, "children">>; interface ServerRoute extends Route { children: ServerRoute[]; module: ServerRouteModule; } type OptionalCriticalCss = CriticalCss | undefined; /** * The output of the compiler for the server build. */ interface ServerBuild { entry: { module: ServerEntryModule; }; routes: ServerRouteManifest; assets: AssetsManifest; basename?: string; publicPath: string; assetsBuildDirectory: string; future: FutureConfig; ssr: boolean; unstable_getCriticalCss?: (args: { pathname: string; }) => OptionalCriticalCss | Promise<OptionalCriticalCss>; /** * @deprecated This is now done via a custom header during prerendering */ isSpaMode: boolean; prerender: string[]; routeDiscovery: { mode: "lazy" | "initial"; manifestPath: string; }; } interface HandleDocumentRequestFunction { (request: Request, responseStatusCode: number, responseHeaders: Headers, context: EntryContext, loadContext: MiddlewareEnabled extends true ? unstable_RouterContextProvider : AppLoadContext): Promise<Response> | Response; } interface HandleDataRequestFunction { (response: Response, args: LoaderFunctionArgs | ActionFunctionArgs): Promise<Response> | Response; } interface HandleErrorFunction { (error: unknown, args: LoaderFunctionArgs | ActionFunctionArgs): void; } /** * A module that serves as the entry point for a Remix app during server * rendering. */ interface ServerEntryModule { default: HandleDocumentRequestFunction; handleDataRequest?: HandleDataRequestFunction; handleError?: HandleErrorFunction; streamTimeout?: number; } type SerializedError = { message: string; stack?: string; }; interface FrameworkContextObject { manifest: AssetsManifest; routeModules: RouteModules; criticalCss?: CriticalCss; serverHandoffString?: string; future: FutureConfig; ssr: boolean; isSpaMode: boolean; routeDiscovery: ServerBuild["routeDiscovery"]; serializeError?(error: Error): SerializedError; renderMeta?: { didRenderScripts?: boolean; streamCache?: Record<number, Promise<void> & { result?: { done: boolean; value: string; }; error?: unknown; }>; }; } interface EntryContext extends FrameworkContextObject { staticHandlerContext: StaticHandlerContext; serverHandoffStream?: ReadableStream<Uint8Array>; } interface FutureConfig { unstable_subResourceIntegrity: boolean; unstable_middleware: boolean; } type CriticalCss = string | { rel: "stylesheet"; href: string; }; interface AssetsManifest { entry: { imports: string[]; module: string; }; routes: RouteManifest<EntryRoute>; url: string; version: string; hmr?: { timestamp?: number; runtime: string; }; sri?: Record<string, string> | true; } interface Route { index?: boolean; caseSensitive?: boolean; id: string; parentId?: string; path?: string; } interface EntryRoute extends Route { hasAction: boolean; hasLoader: boolean; hasClientAction: boolean; hasClientLoader: boolean; hasClientMiddleware: boolean; hasErrorBoundary: boolean; imports?: string[]; css?: string[]; module: string; clientActionModule: string | undefined; clientLoaderModule: string | undefined; clientMiddlewareModule: string | undefined; hydrateFallbackModule: string | undefined; parentId?: string; } declare function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation: Set<string>, manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState, ssr: boolean, isSpaMode: boolean): DataRouteObject[]; declare function createClientRoutes(manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState | null, ssr: boolean, isSpaMode: boolean, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>, needsRevalidation?: Set<string>): DataRouteObject[]; declare function shouldHydrateRouteLoader(routeId: string, clientLoader: ClientLoaderFunction | undefined, hasLoader: boolean, isSpaMode: boolean): boolean; type ParamKeyValuePair = [string, string]; type URLSearchParamsInit = string | ParamKeyValuePair[] | Record<string, string | string[]> | URLSearchParams; /** Creates a URLSearchParams object using the given initializer. This is identical to `new URLSearchParams(init)` except it also supports arrays as values in the object form of the initializer instead of just strings. This is convenient when you need multiple values for a given key, but don't want to use an array initializer. For example, instead of: ```tsx let searchParams = new URLSearchParams([ ['sort', 'name'], ['sort', 'price'] ]); ``` you can do: ``` let searchParams = createSearchParams({ sort: ['name', 'price'] }); ``` @category Utils */ declare function createSearchParams(init?: URLSearchParamsInit): URLSearchParams; type JsonObject = { [Key in string]: JsonValue; } & { [Key in string]?: JsonValue | undefined; }; type JsonArray = JsonValue[] | readonly JsonValue[]; type JsonPrimitive = string | number | boolean | null; type JsonValue = JsonPrimitive | JsonObject | JsonArray; type SubmitTarget = HTMLFormElement | HTMLButtonElement | HTMLInputElement | FormData | URLSearchParams | JsonValue | null; /** * Submit options shared by both navigations and fetchers */ interface SharedSubmitOptions { /** * The HTTP method used to submit the form. Overrides `<form method>`. * Defaults to "GET". */ method?: HTMLFormMethod; /** * The action URL path used to submit the form. Overrides `<form action>`. * Defaults to the path of the current route. */ action?: string; /** * The encoding used to submit the form. Overrides `<form encType>`. * Defaults to "application/x-www-form-urlencoded". */ encType?: FormEncType; /** * Determines whether the form action is relative to the route hierarchy or * the pathname. Use this if you want to opt out of navigating the route * hierarchy and want to instead route based on /-delimited URL segments */ relative?: RelativeRoutingType; /** * In browser-based environments, prevent resetting scroll after this * navigation when using the <ScrollRestoration> component */ preventScrollReset?: boolean; /** * Enable flushSync for this submission's state updates */ flushSync?: boolean; } /** * Submit options available to fetchers */ interface FetcherSubmitOptions extends SharedSubmitOptions { } /** * Submit options available to navigations */ interface SubmitOptions extends FetcherSubmitOptions { /** * Set `true` to replace the current entry in the browser's history stack * instead of creating a new one (i.e. stay on "the same page"). Defaults * to `false`. */ replace?: boolean; /** * State object to add to the history stack entry for this navigation */ state?: any; /** * Indicate a specific fetcherKey to use when using navigate=false */ fetcherKey?: string; /** * navigate=false will use a fetcher instead of a navigation */ navigate?: boolean; /** * Enable view transitions on this submission navigation */ viewTransition?: boolean; } declare const FrameworkContext: React.Context<FrameworkContextObject | undefined>; /** * Defines the discovery behavior of the link: * * - "render" - default, discover the route when the link renders * - "none" - don't eagerly discover, only discover if the link is clicked */ type DiscoverBehavior = "render" | "none"; /** * Defines the prefetching behavior of the link: * * - "none": Never fetched * - "intent": Fetched when the user focuses or hovers the link * - "render": Fetched when the link is rendered * - "viewport": Fetched when the link is in the viewport */ type PrefetchBehavior = "intent" | "render" | "none" | "viewport"; /** * Props for the {@link Links} component. * * @category Types */ interface LinksProps { /** * A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce) * attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) * element */ nonce?: string | undefined; } /** * Renders all the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) * tags created by the route module's [`links`](../../start/framework/route-module#links) * export. You should render it inside the [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head) * of your document. * * @example * import { Links } from "react-router"; * * export default function Root() { * return ( * <html> * <head> * <Links /> * </head> * <body></body> * </html> * ); * } * * @public * @category Components * @mode framework * @param props Props * @param {LinksProps.nonce} props.nonce n/a * @returns A collection of React elements for [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) * tags */ declare function Links({ nonce }: LinksProps): React.JSX.Element; /** * Renders [`<link rel=prefetch|modulepreload>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/rel) * tags for modules and data of another page to enable an instant navigation to * that page. [`<Link prefetch>`](./Link#prefetch) uses this internally, but you * can render it to prefetch a page for any other reason. * * For example, you may render one of this as the user types into a search field * to prefetch search results before they click through to their selection. * * @example * import { PrefetchPageLinks } from "react-router"; * * <PrefetchPageLinks page="/absolute/path" /> * * @public * @category Components * @mode framework * @param props Props * @param {PageLinkDescriptor.page} props.page n/a * @param props.linkProps Additional props to spread onto the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) * tags, such as [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/crossOrigin), * [`integrity`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/integrity), * [`rel`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/rel), * etc. * @returns A collection of React elements for [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) * tags */ declare function PrefetchPageLinks({ page, ...linkProps }: PageLinkDescriptor): React.JSX.Element | null; /** * Renders all the [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) * tags created by the route module's [`meta`](../../start/framework/route-module#meta) * export. You should render it inside the [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head) * of your document. * * @example * import { Meta } from "react-router"; * * export default function Root() { * return ( * <html> * <head> * <Meta /> * </head> * </html> * ); * } * * @public * @category Components * @mode framework * @returns A collection of React elements for [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) * tags */ declare function Meta(): React.JSX.Element; /** * A couple common attributes: * * - `<Scripts crossOrigin>` for hosting your static assets on a different * server than your app. * - `<Scripts nonce>` to support a [content security policy for scripts](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) * with [nonce-sources](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources) * for your [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) * tags. * * You cannot pass through attributes such as [`async`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/async), * [`defer`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/defer), * [`noModule`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/noModule), * [`src`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/src), * or [`type`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/type), * because they are managed by React Router internally. * * @category Types */ type ScriptsProps = Omit<React.HTMLProps<HTMLScriptElement>, "async" | "children" | "dangerouslySetInnerHTML" | "defer" | "noModule" | "src" | "suppressHydrationWarning" | "type"> & { /** * A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce) * attribute to render on the [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) * element */ nonce?: string | undefined; }; /** * Renders the client runtime of your app. It should be rendered inside the * [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body) * of the document. * * If server rendering, you can omit `<Scripts/>` and the app will work as a * traditional web app without JavaScript, relying solely on HTML and browser * behaviors. * * @example * import { Scripts } from "react-router"; * * export default function Root() { * return ( * <html> * <head /> * <body> * <Scripts /> * </body> * </html> * ); * } * * @public * @category Components * @mode framework * @param scriptProps Additional props to spread onto the [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) * tags, such as [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/crossOrigin), * [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce), * etc. * @returns A collection of React elements for [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) * tags */ declare function Scripts(scriptProps: ScriptsProps): React.JSX.Element | null; /** * @category Data Routers */ interface DOMRouterOpts { /** * Basename path for the application. */ basename?: string; /** * A function that returns an {@link unstable_RouterContextProvider} instance * which is provided as the `context` argument to client [`action`](../../start/data/route-object#action)s, * [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware). * This function is called to generate a fresh `context` instance on each * navigation or fetcher call. * * ```tsx * import { * unstable_createContext, * unstable_RouterContextProvider, * } from "react-router"; * * const apiClientContext = unstable_createContext<APIClient>(); * * function createBrowserRouter(routes, { * unstable_getContext() { * let context = new unstable_RouterContextProvider(); * context.set(apiClientContext, getApiClient()); * return context; * } * }) * ``` */ unstable_getContext?: RouterInit["unstable_getContext"]; /** * Future flags to enable for the router. */ future?: Partial<FutureConfig$1>; /** * When Server-Rendering and opting-out of automatic hydration, the * `hydrationData` option allows you to pass in hydration data from your * server-render. This will almost always be a subset of data from the * {@link StaticHandlerContext} value you get back from the {@link StaticHandler}'s * `query` method: * * ```tsx * const router = createBrowserRouter(routes, { * hydrationData: { * loaderData: { * // [routeId]: serverLoaderData * }, * // may also include `errors` and/or `actionData` * }, * }); * ``` * * **Partial Hydration Data** * * You will almost always include a complete set of `loaderData` to hydrate a * server-rendered app. But in advanced use-cases (such as Framework Mode's * [`clientLoader`](../../start/framework/route-module#clientLoader)), you may * want to include `loaderData` for only some routes that were loaded/rendered * on the server. This allows you to hydrate _some_ of the routes (such as the * app layout/shell) while showing a `HydrateFallback` component and running * the [`loader`](../../start/data/route-object#loader)s for other routes * during hydration. * * A route [`loader`](../../start/data/route-object#loader) will run during * hydration in two scenarios: * * 1. No hydration data is provided * In these cases the `HydrateFallback` component will render on initial * hydration * 2. The `loader.hydrate` property is set to `true` * This allows you to run the [`loader`](../../start/data/route-object#loader) * even if you did not render a fallback on initial hydration (i.e., to * prime a cache with hydration data) * * ```tsx * const router = createBrowserRouter( * [ * { * id: "root", * loader: rootLoader, * Component: Root, * children: [ * { * id: "index", * loader: indexLoader, * HydrateFallback: IndexSkeleton, * Component: Index, * }, * ], * }, * ], * { * hydrationData: { * loaderData: { * root: "ROOT DATA", * // No index data provided * }, * }, * } * ); * ``` */ hydrationData?: HydrationState; /** * Override the default data strategy of running loaders in parallel. * See {@link DataStrategyFunction}. * * <docs-warning>This is a low-level API intended for advanced use-cases. This * overrides React Router's internal handling of * [`action`](../../start/data/route-object#action)/[`loader`](../../start/data/route-object#loader) * execution, and if done incorrectly will break your app code. Please use * with caution and perform the appropriate testing.</docs-warning> * * By default, React Router is opinionated about how your data is loaded/submitted - * and most notably, executes all of your [`loader`](../../start/data/route-object#loader)s * in parallel for optimal data fetching. While we think this is the right * behavior for most use-cases, we realize that there is no "one size fits all" * solution when it comes to data fetching for the wide landscape of * application requirements. * * The `dataStrategy` option gives you full control over how your [`action`](../../start/data/route-object#action)s * and [`loader`](../../start/data/route-object#loader)s are executed and lays * the foundation to build in more advanced APIs such as middleware, context, * and caching layers. Over time, we expect that we'll leverage this API * internally to bring more first class APIs to React Router, but until then * (and beyond), this is your way to add more advanced functionality for your * application's data needs. * * The `dataStrategy` function should return a key/value-object of * `routeId` -> {@link DataStrategyResult} and should include entries for any * routes where a handler was executed. A `DataStrategyResult` indicates if * the handler was successful or not based on the `DataStrategyResult.type` * field. If the returned `DataStrategyResult.result` is a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response), * React Router will unwrap it for you (via [`res.json`](https://developer.mozilla.org/en-US/docs/Web/API/Response/json) * or [`res.text`](https://developer.mozilla.org/en-US/docs/Web/API/Response/text)). * If you need to do custom decoding of a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) * but want to preserve the status code, you can use the `data` utility to * return your decoded data along with a `ResponseInit`. * * <details> * <summary><b>Example <code>dataStrategy</code> Use Cases</b></summary> * * **Adding logging** * * In the simplest case, let's look at hooking into this API to add some logging * for when our route [`action`](../../start/data/route-object#action)s/[`loader`](../../start/data/route-object#loader)s * execute: * * ```tsx * let router = createBrowserRouter(routes, { * async dataStrategy({ matches, request }) { * const matchesToLoad = matches.filter((m) => m.shouldLoad); * const results: Record<string, DataStrategyResult> = {}; * await Promise.all( * matchesToLoad.map(async (match) => { * console.log(`Processing ${match.route.id}`); * results[match.route.id] = await match.resolve();; * }) * ); * return results; * }, * }); * ``` * * **Middleware** * * Let's define a middleware on each route via [`handle`](../../start/data/route-object#handle) * and call middleware sequentially first, then call all * [`loader`](../../start/data/route-object#loader)s in parallel - providing * any data made available via the middleware: * * ```ts * const routes = [ * { * id: "parent", * path: "/parent", * loader({ request }, context) { * // ... * }, * handle: { * async middleware({ request }, context) { * context.parent = "PARENT MIDDLEWARE"; * }, * }, * children: [ * { * id: "child", * path: "child", * loader({ request }, context) { * // ... * }, * handle: { * async middleware({ request }, context) { * context.child = "CHILD MIDDLEWARE"; * }, * }, * }, * ], * }, * ]; * * let router = createBrowserRouter(routes, { * async dataStrategy({ matches, params, request }) { * // Run middleware sequentially and let them add data to `context` * let context = {}; * for (const match of matches) { * if (match.route.handle?.middleware) { * await match.route.handle.middleware( * { request, params }, * context * ); * } * } * * // Run loaders in parallel with the `context` value * let matchesToLoad = matches.filter((m) => m.shouldLoad); * let results = await Promise.all( * matchesToLoad.map((match, i) => * match.resolve((handler) => { * // Whatever you pass to `handler` will be passed as the 2nd parameter * // to your loader/action * return handler(context); * }) * ) * ); * return results.reduce( * (acc, result, i) => * Object.assign(acc, { * [matchesToLoad[i].route.id]: result, * }), * {} * ); * }, * }); * ``` * * **Custom Handler** * * It's also possible you don't even want to define a [`loader`](../../start/data/route-object#loader) * implementation at the route level. Maybe you want to just determine the * routes and issue a single GraphQL request for all of your data? You can do * that by setting your `route.loader=true` so it qualifies as "having a * loader", and then store GQL fragments on `route.handle`: * * ```ts * const routes = [ * { * id: "parent", * path: "/parent", * loader: true, * handle: { * gql: gql` * fragment Parent on Whatever { * parentField * } * `, * }, * children: [ * { * id: "child", * path: "child", * loader: true, * handle: { * gql: gql` * fragment Child on Whatever { * childField * } * `, * }, * }, * ], * }, * ]; * * let router = createBrowserRouter(routes, { * async dataStrategy({ matches, params, request }) { * // Compose route fragments into a single GQL payload * let gql = getFragmentsFromRouteHandles(matches); * let data = await fetchGql(gql); * // Parse results back out into individual route level `DataStrategyResult`'s * // keyed by `routeId` * let results = parseResultsFromGql(data); * return results; * }, * }); * ``` *</details> */ dataStrategy?: DataStrategyFunction; /** * Lazily define portions of the route tree on navigations. * See {@link PatchRoutesOnNavigationFunction}. * * By default, React Router wants you to provide a full route tree up front via * `createBrowserRouter(routes)`. This allows React Router to perform synchronous * route matching, execute loaders, and then render route components in the most * optimistic manner without introducing waterfalls. The tradeoff is that your * initial JS bundle is larger by definition — which may slow down application * start-up times as your application grows. * * To combat this, we introduced [`route.lazy`](../../start/data/route-object#lazy) * in [v6.9.0](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v690) * which lets you lazily load the route _implementation_ ([`loader`](../../start/data/route-object#loader), * [`Component`](../../start/data/route-object#Component), etc.) while still * providing the route _definition_ aspects up front (`path`, `index`, etc.). * This is a good middle ground. React Router still knows about your route * definitions (the lightweight part) up front and can perform synchronous * route matching, but then delay loading any of the route implementation * aspects (the heavier part) until the route is actually navigated to. * * In some cases, even this doesn't go far enough. For huge applications, * providing all route definitions up front can be prohibitively expensive. * Additionally, it might not even be possible to provide all route definitions * up front in certain Micro-Frontend or Module-Federation architectures. * * This is where `patchRoutesOnNavigation` comes in ([RFC](https://github.com/remix-run/react-router/discussions/11113)). * This API is for advanced use-cases where you are unable to provide the full * route tree up-front and need a way to lazily "discover" portions of the route * tree at runtime. This feature is often referred to as ["Fog of War"](https://en.wikipedia.org/wiki/Fog_of_war), * because similar to how video games expand the "world" as you move around - * the router would be expanding its routing tree as the user navigated around * the app - but would only ever end up loading portions of the tree that the * user visited. * * `patchRoutesOnNavigation` will be called anytime React Router is unable to * match a `path`. The arguments include the `path`, any partial `matches`, * and a `patch` function you can call to patch new routes into the tree at a * specific location. This method is executed during the `loading` portion of * the navigation for `GET` requests and during the `submitting` portion of * the navigation for non-`GET` requests. * * <details> * <summary><b>Example <code>patchRoutesOnNavigation</code> Use Cases</b></summary> * * **Patching children into an existing route** * * ```tsx * const router = createBrowserRouter( * [ * { * id: "root", * path: "/", * Component: RootComponent, * }, * ], * { * async patchRoutesOnNavigation({ patch, path }) { * if (path === "/a") { * // Load/patch the `a` route as a child of the route with id `root` * let route = await getARoute(); * // ^ { path: 'a', Component: A } * patch("root", [route]); * } * }, * } * ); * ``` * * In the above example, if the user clicks a link to `/a`, React Router * won't match any routes initially and will call `patchRoutesOnNavigation` * with a `path = "/a"` and a `matches` array containing the root route * match. By calling `patch('root', [route])`, the new route will be added * to the route tree as a child of the `root` route and React Router will * perform matching on the updated routes. This time it will successfully * match the `/a` path and the navigation will complete successfully. * * **Patching new root-level routes** * * If you need to patch a new route to the top of the tree (i.e., it doesn't * have a parent), you can pass `null` as the `routeId`: * * ```tsx * const router = createBrowserRouter( * [ * { * id: "root", * path: "/", * Component: RootComponent, * }, * ], * { * async patchRoutesOnNavigation({ patch, path }) { * if (path === "/root-sibling") { * // Load/patch the `/root-sibling` route as a sibling of the root route * let route = await getRootSiblingRoute(); * // ^ { path: '/root-sibling', Component: RootSibling } * patch(null, [route]); * } * }, * } * ); * ``` * * **Patching subtrees asynchronously** * * You can also perform asynchronous matching to lazily fetch entire sections * of your application: * * ```tsx * let router = createBrowserRouter( * [ * { * path: "/", * Component: Home, * }, * ], * { * async patchRoutesOnNavigation({ patch, path }) { * if (path.startsWith("/dashboard")) { * let children = await import("./dashboard"); * patch(null, children); * } * if (path.startsWith("/account")) { * let children = await import("./account"); * patch(null, children); * } * }, * } * ); * ``` * * <docs-info>If in-progress execution of `patchRoutesOnNavigation` is * interrupted by a later navigation, then any remaining `patch` calls in * the interrupted execution will not update the route tree because the * operation was cancelled.</docs-info> * * **Co-locating route discovery with route definition** * * If you don't wish to perform your own pseudo-matching, you can leverage * the partial `matches` array and the [`handle`](../../start/data/route-object#handle) * field on a route to keep the children definitions co-located: * * ```tsx * let router = createBrowserRouter( * [ * { * path: "/", * Component: Home, * }, * { * path: "/dashboard", * children: [ * { * // If we want to include /dashboard in the critical routes, we need to * // also include it's index route since patchRoutesOnNavigation will not be * // called on a navigation to `/dashboard` because it will have successfully * // matched the `/dashboard` parent route * index: true, * // ... * }, * ], * handle: { * lazyChildren: () => import("./dashboard"), * }, * }, * { * path: "/account", * children: [ * { * index: true, * // ... * }, * ], * handle: { * lazyChildren: () => import("./account"), * }, * }, * ], * { * async patchRoutesOnNavigation({ matches, patch }) { * let leafRoute = matches[matches.length - 1]?.route; * if (leafRoute?.handle?.lazyChildren) { * let children = * await leafRoute.handle.lazyChildren(); * patch(leafRoute.id, children); * } * }, * } * ); * ``` * * **A note on routes with parameters** * * Because React Router uses ranked routes to find the best match for a * given path, there is an interesting ambiguity introduced when only a * partial route tree is known at any given point in time. If we match a * fully static route such as `path: "/about/contact-us"` then we know we've * found the right match since it's composed entirely of static URL segments. * Thus, we do not need to bother asking for any other potentially * higher-scoring routes. * * However, routes with parameters (dynamic or splat) can't make this * assumption because there might be a not-yet-discovered route that scores * higher. Consider a full route tree such as: * * ```tsx * // Assume this is the full route tree for your app * const routes = [ * { * path: "/", * Component: Home, * }, * { * id: "blog", * path: "/blog", * Component: BlogLayout, * children: [ * { path: "new", Component: NewPost }, * { path: ":slug", Component: BlogPost }, * ], * }, * ]; * ``` * * And then assume we want to use `patchRoutesOnNavigation` to fill this in * as the user navigates around: * * ```tsx * // Start with only the index route * const router = createBrowserRouter( * [ * { * path: "/", * Component: Home, * }, * ], * { * async patchRoutesOnNavigation({ patch, path }) { * if (path === "/blog/new") { * patch("blog", [ * { * path: "new", * Component: NewPost, * }, * ]); * } else if (path.startsWith("/blog")) { * patch("blog", [ * { * path: ":slug", * Component: BlogPost, * }, * ]); * } * }, * } * ); * ``` * * If the user were to a blog post first (i.e., `/blog/my-post`) we would * patch in the `:slug` route. Then, if the user navigated to `/blog/new` to * write a new post, we'd match `/blog/:slug` but it wouldn't be the _right_ * match! We need to call `patchRoutesOnNavigation` just in case there * exists a higher-scoring route we've not yet discovered, which in this * case there is. * * So, anytime React Router matches a path that contains at least one param, * it will call `patchRoutesOnNavigation` and match routes again just to * confirm it has found the best match. * * If your `patchRoutesOnNavigation` implementation is expensive or making * side effect [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) * calls to a backend server, you may want to consider tracking previously * seen routes to avoid over-fetching in cases where you know the proper * route has already been found. This can usually be as simple as * maintaining a small cache of prior `path` values for which you've already * patched in the right routes: * * ```tsx * let discoveredRoutes = new Set(); * * const router = createBrowserRouter(routes, { * async patchRoutesOnNavigation({ patch, path }) { * if (discoveredRoutes.has(path)) { * // We've seen this before so nothing to patch in and we can let the router * // use the routes it already knows about * return; * } * * discoveredRoutes.add(path); * * // ... patch routes in accordingly * }, * }); * ``` * </details> */ patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction; /** * [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object * override. Defaults to the global `window` instance. */ window?: Window; } /** * Create a new {@link DataRouter| data router} that manages the application * path via [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) * and [`history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState). * * @public * @category Data Routers * @mode data * @param routes Application routes * @param opts Options * @param {DOMRouterOpts.basename} opts.basename n/a * @param {DOMRouterOpts.dataStrategy} opts.dataStrategy n/a * @param {DOMRouterOpts.future} opts.future n/a * @param {DOMRouterOpts.unstable_getContext} opts.unstable_getContext n/a * @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a * @param {DOMRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a * @param {DOMRouterOpts.window} opts.window n/a * @returns An initialized {@link DataRouter| data router} to pass to {@link RouterProvider | `<RouterProvider>`} */ declare function createBrowserRouter(routes: RouteObject[], opts?: DOMRouterOpts): Router; /** * Create a new {@link DataRouter| data router} that manages the application * path via the URL [`hash`]https://developer.mozilla.org/en-US/docs/Web/API/URL/hash). * * @public * @category Data Routers * @mode data * @param routes Application routes * @param opts Options * @param {DOMRouterOpts.basename} opts.basename n/a * @param {DOMRouterOpts.future} opts.future n/a * @param {DOMRouterOpts.unstable_getContext} opts.unstable_getContext n/a * @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a * @param {DOMRouterOpts.dataStrategy} opts.dataStrategy n/a * @param {DOMRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a * @param {DOMRouterOpts.window} opts.window n/a * @returns An initialized {@link DataRouter| data router} to pass to {@link RouterProvider | `<RouterProvider>`} */ declare function createHashRouter(routes: RouteObject[], opts?: DOMRouterOpts): Router; /** * @category Types */ interface BrowserRouterProps { /** * Application basename */ basename?: string; /** * {@link Route | `<Route>`} components describing your route configuration */ children?: React.ReactNode; /** * [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object * override. Defaults to the global `window` instance */ window?: Window; } /** * A declarative {@link Router | `<Router>`} using the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History) * API for client-side routing. * * @public * @category Declarative Routers * @mode declarative * @param props Props * @param {BrowserRouterProps.basename} props.basename n/a * @param {BrowserRouterProps.children} props.children n/a * @param {BrowserRouterProps.window} props.window n/a * @returns A declarative {@link Router | `<Router>`} using the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History) * API for client-side routing. */ declare function BrowserRouter({ basename, children, window, }: BrowserRouterProps): React.JSX.Element; /** * @category Types */ interface HashRouterProps { /** * Application basename */ basename?: string; /** * {@link Route | `<Route>`} components describing your route configuration */ children?: React.ReactNode; /** * [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object * override. Defaults to the global `window` instance */ window?: Window; } /** * A declarative {@link Router | `<Router>`} that stores the location in the * [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash) portion * of the URL so it is not sent to the server. * * @public * @category Declarative Routers * @mode declarative * @param props Props * @param {HashRouterProps.basename} props.basename n/a * @param {HashRouterProps.children} props.children n/a * @param {HashRouterProps.window} props.window n/a * @returns A declarative {@link Router | `<Router>`} using the URL [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash) * for client-side routing. */ declare function HashRouter({ basename, children, window }: HashRouterProps): React.JSX.Element; /** * @category Types */ interface HistoryRouterProps { /** * Application basename */ basename?: string; /** * {@link Route | `<Route>`} components describing your route configuration */ children?: React.ReactNode; /** * A {@link History} implementation for use by the router */ history: History; } /** * A declarative {@link Router | `<Router>`} that accepts a pre-instantiated * `history` object. * It's important to note that using your own `history` object is highly discouraged * and may add two versions of the `history` library to your bundles unless you use * the same version of the `history` library that React Router uses internally. * * @name unstable_HistoryRouter * @public * @category Declarative Routers * @mode declarative * @param props Props * @param {HistoryRouterProps.basename} props.basename n/a * @param {HistoryRouterProps.children} props.children n/a * @param {HistoryRouterProps.history} props.history n/a * @returns A declarative {@link Router | `<Router>`} using the provided history * implementation for client-side routing. */ declare function HistoryRouter({ basename, children, history, }: HistoryRouterProps): React.JSX.Element; declare namespace HistoryRouter { var displayName: string; } /** * @category Types */ interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> { /** * Defines the link discovery behavior * * ```tsx * <Link /> // default ("render") * <Link discover="render" /> * <Link discover="none" /> * ``` * * - **render** — default, discover the route when the link renders * - **none** — don't eagerly discover, only discover if the link is clicked */ discover?: DiscoverBehavior; /** * Defines the data and module prefetching behavior for the link. * * ```tsx * <Link /> // default * <Link prefetch="none" /> * <Link prefetch="intent" /> * <Link prefetch="render" /> * <Link prefetch="viewport" /> * ``` * * - **none** — default, no prefetching * - **intent** — prefetches when the user hovers or focuses the link * - **render** — prefetches when the link renders * - **viewport** — prefetches when the link is in the viewport, very useful for mobile * * Prefetching is done with HTML [`<link rel="prefetch">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) * tags. They are inserted after the link. * * ```tsx * <a href="..." /> * <a href="..." /> * <link rel="prefetch" /> // might conditionally render * ``` * * Because of this, if you are using `nav :last-child` you will need to use * `nav :last-of-type` so the styles don't conditionally fall off your last link * (and any other similar selectors). */ prefetch?: PrefetchBehavior; /** * Will use document navigation instead of client side routing when the link is * clicked: the browser will handle the transition normally (as if it were an * [`<a href>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)). * * ```tsx * <Link to="/logout" reloadDocument /> * ``` */ reloadDocument?: boolean; /** * Replaces the current entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History) * stack instead of pushing a new one onto it. * * ```tsx * <Link replace /> * ``` * * ``` * # with a history stack like this * A -> B * * # normal link click pushes a new entry * A -> B -> C * * # but with `replace`, B is replaced by C * A -> C * ``` */ replace?: boolean; /** * Adds persistent client side routing state to the next location. * * ```tsx * <Link to="/somewhere/else" state={{ some: "value" }} /> * ``` * * The location state is accessed from the `location`. * * ```tsx * function SomeComp() { * const location = useLocation(); * location.state; // { some: "value" } * } * ``` * * This state is inaccessible on the server as it is implemented on top of * [`history.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state) */ state?: any; /** * Prevents the scroll position from being reset to the top of the window when * the link is clicked and the app is using {@link ScrollRestoration}. This only * prevents new locations resetting scroll to the top, scroll position will be * restored for back/forward button navigation. * * ```tsx * <Link to="?tab=one" preventScrollReset /> * ``` */ preventScrollReset?: boolean; /** * Defines the relative path behavior for the link. * * ```tsx * <Link to=".." /> // default: "route" * <Link relative="route" /> * <Link relative="path" /> * ``` * * Consider a route hierarchy where a parent route pattern is `"blog"` and a child * route pattern is `"blog/:slug/edit"`. * * - **route** — default, resolves the link relative to the route pattern. In the * example above, a relative link of `"..."` will remove both `:slug/edit` segments * back to `"/blog"`. * - **path** — relative to the path so `"..."` will only remove one URL segment up * to `"/blog/:slug"` * * Note that index routes and layout routes do not have paths so they are not * included in the relative path calculation. */ relative?: RelativeRoutingType; /** * Can be a string or a partial {@link Path}: * * ```tsx * <Link to="/some/path" /> * * <Link * to={{ * pathname: "/some/path", * search: "?query=string", * hash: "#hash", * }} * /> * ``` */ to: To; /** * Enables a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) * for this navigation. * * ```jsx * <Link to={to} viewTransition> * Click me * </Link> * ``` * * To apply specific styles for the transition, see {@link useViewTransitionState} */ viewTransition?: boolean; } /** * A progressively enhanced [`<a href>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) * wrapper to enable navigation with client-side routing. * * @example * import { Link } from "react-router"; * * <Link to="/dashboard">Dashboard</Link>; * * <Link * to={{ * pathname: "/some/path", * search: "?query=string", * hash: "#hash", * }} * />; * * @public * @category Components * @param {LinkProps.discover} props.discover [modes: framework] n/a * @param {LinkProps.prefetch} props.prefetch [modes: framework] n/a * @param {LinkProps.preventScrollReset} props.preventScrollReset [modes: framework, data] n/a * @param {LinkProps.rel