react-router
Version:
Declarative routing for React
1,386 lines (1,384 loc) • 72.8 kB
TypeScript
import { History, To } from "../router/history.js";
import { DataStrategyFunction, HTMLFormMethod, PatchRoutesOnNavigationFunction, RouteObject } from "../router/utils.js";
import { BlockerFunction, Fetcher, FutureConfig, GetScrollRestorationKeyFunction, HydrationState, RelativeRoutingType, Router, RouterInit } from "../router/router.js";
import { NavigateOptions } from "../context.js";
import { FetcherSubmitOptions, SubmitOptions, SubmitTarget, URLSearchParamsInit } from "./dom.js";
import { DiscoverBehavior, PrefetchBehavior, ScriptsProps } from "./ssr/components.js";
import { SerializeFrom } from "../types/route-data.js";
import { ClientInstrumentation } from "../router/instrumentation.js";
import * as React$1 from "react";
//#region lib/dom/lib.d.ts
/**
* @category Data Routers
*/
interface DOMRouterOpts {
/**
* Basename path for the application.
*/
basename?: string;
/**
* A function that returns an {@link 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 {
* createContext,
* RouterContextProvider,
* } from "react-router";
*
* const apiClientContext = createContext<APIClient>();
*
* function createBrowserRouter(routes, {
* getContext() {
* let context = new RouterContextProvider();
* context.set(apiClientContext, getApiClient());
* return context;
* }
* })
* ```
*/
getContext?: RouterInit["getContext"];
/**
* Future flags to enable for the router.
*/
future?: Partial<FutureConfig>;
/**
* 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;
/**
* Array of instrumentation objects allowing you to instrument the router and
* individual routes prior to router initialization (and on any subsequently
* added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
* mostly useful for observability such as wrapping navigations, fetches,
* as well as route loaders/actions/middlewares with logging and/or performance
* tracing. See the [docs](../../how-to/instrumentation) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* instrumentations: [logging]
* });
*
*
* let logging = {
* router({ instrument }) {
* instrument({
* navigate: (impl, info) => logExecution(`navigate ${info.to}`, impl),
* fetch: (impl, info) => logExecution(`fetch ${info.to}`, impl)
* });
* },
* route({ instrument, id }) {
* instrument({
* middleware: (impl, info) => logExecution(
* `middleware ${info.request.url} (route ${id})`,
* impl
* ),
* loader: (impl, info) => logExecution(
* `loader ${info.request.url} (route ${id})`,
* impl
* ),
* action: (impl, info) => logExecution(
* `action ${info.request.url} (route ${id})`,
* impl
* ),
* })
* }
* };
*
* async function logExecution(label: string, impl: () => Promise<void>) {
* let start = performance.now();
* console.log(`start ${label}`);
* await impl();
* let duration = Math.round(performance.now() - start);
* console.log(`end ${label} (${duration}ms)`);
* }
* ```
*/
instrumentations?: ClientInstrumentation[];
/**
* Override the default data strategy of running loaders in parallel -
* see the [docs](../../how-to/data-strategy) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* async dataStrategy({
* matches,
* request,
* runClientMiddleware,
* }) {
* const matchesToLoad = matches.filter((m) =>
* m.shouldCallHandler(),
* );
*
* const results: Record<string, DataStrategyResult> = {};
* await runClientMiddleware(() =>
* Promise.all(
* matchesToLoad.map(async (match) => {
* results[match.route.id] = await match.resolve();
* }),
* ),
* );
* return results;
* },
* });
* ```
*/
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).
*
* Data Routers should not be held in React state. You should create your router
* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
*
* @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.getContext} opts.getContext n/a
* @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a
* @param {DOMRouterOpts.instrumentations} opts.instrumentations 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).
*
* Data Routers should not be held in React state. You should create your router
* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
*
* @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.getContext} opts.getContext n/a
* @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a
* @param {DOMRouterOpts.instrumentations} opts.instrumentations 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$1.ReactNode;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
/**
* [`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.useTransitions} props.useTransitions 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,
useTransitions,
window
}: BrowserRouterProps): React$1.JSX.Element;
/**
* @category Types
*/
interface HashRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* {@link Route | `<Route>`} components describing your route configuration
*/
children?: React$1.ReactNode;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
/**
* [`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.useTransitions} props.useTransitions 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,
useTransitions,
window
}: HashRouterProps): React$1.JSX.Element;
/**
* @category Types
*/
interface HistoryRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* {@link Route | `<Route>`} components describing your route configuration
*/
children?: React$1.ReactNode;
/**
* A {@link History} implementation for use by the router
*/
history: History;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* 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
* @param {HistoryRouterProps.useTransitions} props.useTransitions n/a
* @returns A declarative {@link Router | `<Router>`} using the provided history
* implementation for client-side routing.
*/
declare function HistoryRouter({
basename,
children,
history,
useTransitions
}: HistoryRouterProps): React$1.JSX.Element;
declare namespace HistoryRouter {
var displayName: string;
}
/**
* @category Types
*/
interface LinkProps extends Omit<React$1.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
/**
* Defines the link [lazy route discovery](../../explanation/lazy-route-discovery) behavior.
*
* - **render** — default, discover the route when the link renders
* - **none** — don't eagerly discover, only discover if the link is clicked
*
* ```tsx
* <Link /> // default ("render")
* <Link discover="render" />
* <Link discover="none" />
* ```
*/
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;
/**
* Specify the default revalidation behavior for the navigation.
*
* ```tsx
* <Link to="/some/path" defaultShouldRevalidate={false} />
* ```
*
* If no `shouldRevalidate` functions are present on the active routes, then this
* value will be used directly. Otherwise it will be passed into `shouldRevalidate`
* so the route can make the final determination on revalidation. This can be
* useful when updating search params and you don't want to trigger a revalidation.
*
* By default (when not specified), loaders will revalidate according to the routers
* standard revalidation behavior.
*/
defaultShouldRevalidate?: boolean;
/**
* Masked path for this navigation, when you want to navigate the router to
* one location but display a separate location in the URL bar.
*
* This is useful for contextual navigations such as opening an image in a modal
* on top of a gallery while keeping the underlying gallery active. If a user
* shares the masked URL, or opens the link in a new tab, they will only load
* the masked location without the underlying contextual location.
*
* This feature relies on `history.state` and is thus only intended for SPA uses
* and SSR renders will not respect the masking.
*
* ```tsx
* // routes/gallery.tsx
* export function clientLoader({ request }: Route.LoaderArgs) {
* let sp = new URL(request.url).searchParams;
* return {
* images: getImages(),
* modalImage: sp.has("image") ? getImage(sp.get("image")!) : null,
* };
* }
*
* export default function Gallery({ loaderData }: Route.ComponentProps) {
* return (
* <>
* <GalleryGrid>
* {loaderData.images.map((image) => (
* <Link
* key={image.id}
* to={`/gallery?image=${image.id}`}
* mask={`/images/${image.id}`}
* >
* <img src={image.url} alt={image.alt} />
* </Link>
* ))}
* </GalleryGrid>
*
* {data.modalImage ? (
* <dialog open>
* <img src={data.modalImage.url} alt={data.modalImage.alt} />
* </dialog>
* ) : null}
* </>
* );
* }
* ```
*/
mask?: To;
}
/**
* 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.relative} props.relative n/a
* @param {LinkProps.reloadDocument} props.reloadDocument n/a
* @param {LinkProps.replace} props.replace n/a
* @param {LinkProps.state} props.state n/a
* @param {LinkProps.to} props.to n/a
* @param {LinkProps.viewTransition} props.viewTransition [modes: framework, data] n/a
* @param {LinkProps.defaultShouldRevalidate} props.defaultShouldRevalidate n/a
* @param {LinkProps.mask} props.mask [modes: framework, data] n/a
*/
declare const Link: React$1.ForwardRefExoticComponent<LinkProps & React$1.RefAttributes<HTMLAnchorElement>>;
/**
* The object passed to {@link NavLink} `children`, `className`, and `style` prop
* callbacks to render and style the link based on its state.
*
* ```
* // className
* <NavLink
* to="/messages"
* className={({ isActive, isPending }) =>
* isPending ? "pending" : isActive ? "active" : ""
* }
* >
* Messages
* </NavLink>
*
* // style
* <NavLink
* to="/messages"
* style={({ isActive, isPending }) => {
* return {
* fontWeight: isActive ? "bold" : "",
* color: isPending ? "red" : "black",
* }
* )}
* />
*
* // children
* <NavLink to="/tasks">
* {({ isActive, isPending }) => (
* <span className={isActive ? "active" : ""}>Tasks</span>
* )}
* </NavLink>
* ```
*
*/
type NavLinkRenderProps = {
/**
* Indicates if the link's URL matches the current {@link Location}.
*/
isActive: boolean;
/**
* Indicates if the pending {@link Location} matches the link's URL. Only
* available in Framework/Data modes.
*/
isPending: boolean;
/**
* Indicates if a view transition to the link's URL is in progress.
* See {@link useViewTransitionState}
*/
isTransitioning: boolean;
};
/**
* @category Types
*/
interface NavLinkProps extends Omit<LinkProps, "className" | "style" | "children"> {
/**
* Can be regular React children or a function that receives an object with the
* `active` and `pending` states of the link.
*
* ```tsx
* <NavLink to="/tasks">
* {({ isActive }) => (
* <span className={isActive ? "active" : ""}>Tasks</span>
* )}
* </NavLink>
* ```
*/
children?: React$1.ReactNode | ((props: NavLinkRenderProps) => React$1.ReactNode);
/**
* Changes the matching logic to make it case-sensitive:
*
* | Link | URL | isActive |
* | -------------------------------------------- | ------------- | -------- |
* | `<NavLink to="/SpOnGe-bOB" />` | `/sponge-bob` | true |
* | `<NavLink to="/SpOnGe-bOB" caseSensitive />` | `/sponge-bob` | false |
*/
caseSensitive?: boolean;
/**
* Classes are automatically applied to `NavLink` that correspond to the state.
*
* ```css
* a.active {
* color: red;
* }
* a.pending {
* color: blue;
* }
* a.transitioning {
* view-transition-name: my-transition;
* }
* ```
*
* Or you can specify a function that receives {@link NavLinkRenderProps} and
* returns the `className`:
*
* ```tsx
* <NavLink className={({ isActive, isPending }) => (
* isActive ? "my-active-class" :
* isPending ? "my-pending-class" :
* ""
* )} />
* ```
*/
className?: string | ((props: NavLinkRenderProps) => string | undefined);
/**
* Changes the matching logic for the `active` and `pending` states to only match
* to the "end" of the {@link NavLinkProps.to}. If the URL is longer, it will no
* longer be considered active.
*
* | Link | URL | isActive |
* | ----------------------------- | ------------ | -------- |
* | `<NavLink to="/tasks" />` | `/tasks` | true |
* | `<NavLink to="/tasks" />` | `/tasks/123` | true |
* | `<NavLink to="/tasks" end />` | `/tasks` | true |
* | `<NavLink to="/tasks" end />` | `/tasks/123` | false |
*
* `<NavLink to="/">` is an exceptional case because _every_ URL matches `/`.
* To avoid this matching every single route by default, it effectively ignores
* the `end` prop and only matches when you're at the root route.
*/
end?: boolean;
/**
* Styles can also be applied dynamically via a function that receives
* {@link NavLinkRenderProps} and returns the styles:
*
* ```tsx
* <NavLink to="/tasks" style={{ color: "red" }} />
* <NavLink to="/tasks" style={({ isActive, isPending }) => ({
* color:
* isActive ? "red" :
* isPending ? "blue" : "black"
* })} />
* ```
*/
style?: React$1.CSSProperties | ((props: NavLinkRenderProps) => React$1.CSSProperties | undefined);
}
/**
* Wraps {@link Link | `<Link>`} with additional props for styling active and
* pending states.
*
* - Automatically applies classes to the link based on its `active` and `pending`
* states, see {@link NavLinkProps.className}
* - Note that `pending` is only available with Framework and Data modes.
* - Automatically applies `aria-current="page"` to the link when the link is active.
* See [`aria-current`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)
* on MDN.
* - States are additionally available through the className, style, and children
* render props. See {@link NavLinkRenderProps}.
*
* @example
* <NavLink to="/message">Messages</NavLink>
*
* // Using render props
* <NavLink
* to="/messages"
* className={({ isActive, isPending }) =>
* isPending ? "pending" : isActive ? "active" : ""
* }
* >
* Messages
* </NavLink>
*
* @public
* @category Components
* @param {NavLinkProps.caseSensitive} props.caseSensitive n/a
* @param {NavLinkProps.children} props.children n/a
* @param {NavLinkProps.className} props.className n/a
* @param {NavLinkProps.discover} props.discover [modes: framework] n/a
* @param {NavLinkProps.end} props.end n/a
* @param {NavLinkProps.prefetch} props.prefetch [modes: framework] n/a
* @param {NavLinkProps.preventScrollReset} props.preventScrollReset [modes: framework, data] n/a
* @param {NavLinkProps.relative} props.relative n/a
* @param {NavLinkProps.reloadDocument} props.reloadDocument n/a
* @param {NavLinkProps.replace} props.replace n/a
* @param {NavLinkProps.state} props.state n/a
* @param {NavLinkProps.style} props.style n/a
* @param {NavLinkProps.to} props.to n/a
* @param {NavLinkProps.viewTransition} props.viewTransition [modes: framework, data] n/a
*/
declare const NavLink: React$1.ForwardRefExoticComponent<NavLinkProps & React$1.RefAttributes<HTMLAnchorElement>>;
/**
* Form props shared by navigations and fetchers
*/
interface SharedFormProps extends React$1.FormHTMLAttributes<HTMLFormElement> {
/**
* The HTTP verb to use when the form is submitted. Supports `"delete"`,
* `"get"`, `"patch"`, `"post"`, and `"put"`.
*
* Native [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
* only supports `"get"` and `"post"`, avoid the other verbs if you'd like to
* support progressive enhancement
*/
method?: HTMLFormMethod;
/**
* The encoding type to use for the form submission.
*
* ```tsx
* <Form encType="application/x-www-form-urlencoded"/> // Default
* <Form encType="multipart/form-data"/>
* <Form encType="text/plain"/>
* ```
*/
encType?: "application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain";
/**
* The URL to submit the form data to. If `undefined`, this defaults to the
* closest route in context.
*/
action?: string;
/**
* 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 slash-delimited URL segments.
* See {@link RelativeRoutingType}.
*/
relative?: RelativeRoutingType;
/**
* Prevent the scroll position from resetting to the top of the viewport on
* completion of the navigation when using the
* {@link ScrollRestoration | `<ScrollRestoration>`} component
*/
preventScrollReset?: boolean;
/**
* Specify the default revalidation behavior after this submission
*
* If no `shouldRevalidate` functions are present on the active routes, then this
* value will be used directly. Otherwise it will be passed into `shouldRevalidate`
* so the route can make the final determination on revalidation. This can be
* useful when updating search params and you don't want to trigger a revalidation.
*
* By default (when not specified), loaders will revalidate according to the routers
* standard revalidation behavior.
*/
defaultShouldRevalidate?: boolean;
}
/**
* Form props available to fetchers
* @category Types
*/
interface FetcherFormProps extends SharedFormProps {}
/**
* Form props available to navigations
* @category Types
*/
interface FormProps extends SharedFormProps {
/**
* Defines the form [lazy route discovery](../../explanation/lazy-route-discovery) behavior.
*
* - **render** — default, discover the route when the form renders
* - **none** — don't eagerly discover, only discover if the form is submitted
*
* ```tsx
* <Form /> // default ("render")
* <Form discover="render" />
* <Form discover="none" />
* ```
*/
discover?: DiscoverBehavior;
/**
* Indicates a specific fetcherKey to use when using `navigate={false}` so you
* can pick up the fetcher's state in a different component in a {@link useFetcher}.
*/
fetcherKey?: string;
/**
* When `false`, skips the navigation and submits via a fetcher internally.
* This is essentially a shorthand for {@link useFetcher} + `<fetcher.Form>` where
* you don't care about the resulting data in this component.
*/
navigate?: boolean;
/**
* Forces a full document navigation instead of client side routing and data
* fetch.
*/
reloadDocument?: boolean;
/**
* Replaces the current entry in the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack when the form navigates. Use this if you don't want the user to be
* able to click "back" to the page with the form on it.
*/
replace?: boolean;
/**
* State object to add to the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack entry for this navigation
*/
state?: any;
/**
* Enables a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* for this navigation. To apply specific styles during the transition, see
* {@link useViewTransitionState}.
*/
viewTransition?: boolean;
}
/**
* A progressively enhanced HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
* that submits data to actions via [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch),
* activating pending states in {@link useNavigation} which enables advanced
* user interfaces beyond a basic HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form).
* After a form's `action` completes, all data on the page is automatically
* revalidated to keep the UI in sync with the data.
*
* Because it uses the HTML form API, server rendered pages are interactive at a
* basic level before JavaScript loads. Instead of React Router managing the
* submission, the browser manages the submission as well as the pending states
* (like the spinning favicon). After JavaScript loads, React Router takes over
* enabling web application user experiences.
*
* `Form` is most useful for submissions that should also change the URL or
* otherwise add an entry to the browser history stack. For forms that shouldn't
* manipulate the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack, use {@link FetcherWithComponents.Form | `<fetcher.Form>`}.
*
* @example
* import { Form } from "react-router";
*
* function NewEvent() {
* return (
* <Form action="/events" method="post">
* <input name="title" type="text" />
* <input name="description" type="text" />
* </Form>
* );
* }
*
* @public
* @category Components
* @mode framework
* @mode data
* @param {FormProps.action} action n/a
* @param {FormProps.discover} discover n/a
* @param {FormProps.encType} encType n/a
* @param {FormProps.fetcherKey} fetcherKey n/a
* @param {FormProps.method} method n/a
* @param {FormProps.navigate} navigate n/a
* @param {FormProps.preventScrollReset} preventScrollReset n/a
* @param {FormProps.relative} relative n/a
* @param {FormProps.reloadDocument} reloadDocument n/a
* @param {FormProps.replace} replace n/a
* @param {FormProps.state} state n/a
* @param {FormProps.viewTransition} viewTransition n/a
* @param {FormProps.defaultShouldRevalidate} defaultShouldRevalidate n/a
* @returns A progressively enhanced [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) component
*/
declare const Form: React$1.ForwardRefExoticComponent<FormProps & React$1.RefAttributes<HTMLFormElement>>;
type ScrollRestorationProps = ScriptsProps & {
/**
* A function that returns a key to use for scroll restoration. This is useful
* for custom scroll restoration logic, such as using only the pathname so
* that later navigations to prior paths will restore the scroll. Defaults to
* `location.key`. See {@link GetScrollRestorationKeyFunction}.
*
* ```tsx
* <ScrollRestoration
* getKey={(location, matches) => {
* // Restore based on a unique location key (default behavior)
* return location.key
*
* // Restore based on pathname
* return location.pathname
* }}
* />
* ```
*/
getKey?: GetScrollRestorationKeyFunction;
/**
* The key to use for storing scroll positions in [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage).
* Defaults to `"react-router-scroll-positions"`.
*/
storageKey?: string;
};
/**
* Emulates the browser's scroll restoration on location changes. Apps should only render one of these, right before the {@link Scripts} component.
*
* ```tsx
* import { ScrollRestoration } from "react-router";
*
* export default function Root() {
* return (
* <html>
* <body>
* <ScrollRestoration />
* <Scripts />
* </body>
* </html>
* );
* }
* ```
*
* This component renders an inline `<script>` to prevent scroll flashing. The
* `nonce` prop will be passed down to the script tag to allow CSP nonce usage.
* If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*
* ```tsx
* <ScrollRestoration nonce={cspNonce} />
* ```
*
* @public
* @category Components
* @mode framework
* @mode data
* @param props Props
* @param {ScrollRestorationProps.getKey} props.getKey n/a
* @param {ScriptsProps.nonce} props.nonce n/a
* @param {ScrollRestorationProps.storageKey} props.storageKey n/a
* @returns A [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tag that restores scroll positions on navigation.
*/
declare function ScrollRestoration({
getKey,
storageKey,
...props
}: ScrollRestorationProps): React$1.JSX.Element | null;
declare namespace