react-router
Version:
Declarative routing for React
1,343 lines (1,331 loc) • 92.8 kB
text/typescript
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