UNPKG

react-router

Version:
1,508 lines (1,499 loc) • 54.8 kB
import * as React from 'react'; import { ComponentType, ReactElement } from 'react'; /** * Actions represent the type of change to a location value. */ declare enum Action { /** * A POP indicates a change to an arbitrary index in the history stack, such * as a back or forward navigation. It does not describe the direction of the * navigation, only that the current index changed. * * Note: This is the default action for newly created history objects. */ Pop = "POP", /** * A PUSH indicates a new entry being added to the history stack, such as when * a link is clicked and a new page loads. When this happens, all subsequent * entries in the stack are lost. */ Push = "PUSH", /** * A REPLACE indicates the entry at the current index in the history stack * being replaced by a new one. */ Replace = "REPLACE" } /** * The pathname, search, and hash values of a URL. */ interface Path { /** * A URL pathname, beginning with a /. */ pathname: string; /** * A URL search string, beginning with a ?. */ search: string; /** * A URL fragment identifier, beginning with a #. */ hash: string; } /** * An entry in a history stack. A location contains information about the * URL path, as well as possibly some arbitrary state and a key. */ interface Location<State = any> extends Path { /** * A value of arbitrary data associated with this location. */ state: State; /** * A unique string associated with this location. May be used to safely store * and retrieve data in some other storage API, like `localStorage`. * * Note: This value is always "default" on the initial location. */ key: string; } /** * A change to the current location. */ interface Update { /** * The action that triggered the change. */ action: Action; /** * The new location. */ location: Location; /** * The delta between this location and the former location in the history stack */ delta: number | null; } /** * A function that receives notifications about location changes. */ interface Listener { (update: Update): void; } /** * Describes a location that is the destination of some navigation used in * {@link Link}, {@link useNavigate}, etc. */ type To = string | Partial<Path>; /** * A history is an interface to the navigation stack. The history serves as the * source of truth for the current location, as well as provides a set of * methods that may be used to change it. * * It is similar to the DOM's `window.history` object, but with a smaller, more * focused API. */ interface History { /** * The last action that modified the current location. This will always be * Action.Pop when a history instance is first created. This value is mutable. */ readonly action: Action; /** * The current location. This value is mutable. */ readonly location: Location; /** * Returns a valid href for the given `to` value that may be used as * the value of an <a href> attribute. * * @param to - The destination URL */ createHref(to: To): string; /** * Returns a URL for the given `to` value * * @param to - The destination URL */ createURL(to: To): URL; /** * Encode a location the same way window.history would do (no-op for memory * history) so we ensure our PUSH/REPLACE navigations for data routers * behave the same as POP * * @param to Unencoded path */ encodeLocation(to: To): Path; /** * Pushes a new location onto the history stack, increasing its length by one. * If there were any entries in the stack after the current one, they are * lost. * * @param to - The new URL * @param state - Data to associate with the new location */ push(to: To, state?: any): void; /** * Replaces the current location in the history stack with a new one. The * location that was replaced will no longer be available. * * @param to - The new URL * @param state - Data to associate with the new location */ replace(to: To, state?: any): void; /** * Navigates `n` entries backward/forward in the history stack relative to the * current index. For example, a "back" navigation would use go(-1). * * @param delta - The delta in the stack index */ go(delta: number): void; /** * Sets up a listener that will be called whenever the current location * changes. * * @param listener - A function that will be called when the location changes * @returns unlisten - A function that may be used to stop listening */ listen(listener: Listener): () => void; } /** * A user-supplied object that describes a location. Used when providing * entries to `createMemoryHistory` via its `initialEntries` option. */ type InitialEntry = string | Partial<Location>; /** * A browser history stores the current location in regular URLs in a web * browser environment. This is the standard for most web apps and provides the * cleanest URLs the browser's address bar. * * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#browserhistory */ interface BrowserHistory extends UrlHistory { } type BrowserHistoryOptions = UrlHistoryOptions; /** * Browser history stores the location in regular URLs. This is the standard for * most web apps, but it requires some configuration on the server to ensure you * serve the same app at multiple URLs. * * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory */ declare function createBrowserHistory(options?: BrowserHistoryOptions): BrowserHistory; /** * @private */ declare function invariant(value: boolean, message?: string): asserts value; declare function invariant<T>(value: T | null | undefined, message?: string): asserts value is T; /** * Creates a string URL path from the given pathname, search, and hash components. * * @category Utils */ declare function createPath({ pathname, search, hash, }: Partial<Path>): string; /** * Parses a string URL path into its separate pathname, search, and hash components. * * @category Utils */ declare function parsePath(path: string): Partial<Path>; interface UrlHistory extends History { } type UrlHistoryOptions = { window?: Window; v5Compat?: boolean; }; /** * Map of routeId -> data returned from a loader/action/error */ interface RouteData { [routeId: string]: any; } type LowerCaseFormMethod = "get" | "post" | "put" | "patch" | "delete"; type UpperCaseFormMethod = Uppercase<LowerCaseFormMethod>; /** * Users can specify either lowercase or uppercase form methods on `<Form>`, * useSubmit(), `<fetcher.Form>`, etc. */ type HTMLFormMethod = LowerCaseFormMethod | UpperCaseFormMethod; /** * Active navigation/fetcher form methods are exposed in uppercase on the * RouterState. This is to align with the normalization done via fetch(). */ type FormMethod = UpperCaseFormMethod; type FormEncType = "application/x-www-form-urlencoded" | "multipart/form-data" | "application/json" | "text/plain"; 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; /** * @private * Internal interface to pass around for action submissions, not intended for * external consumption */ type Submission = { formMethod: FormMethod; formAction: string; formEncType: FormEncType; formData: FormData; json: undefined; text: undefined; } | { formMethod: FormMethod; formAction: string; formEncType: FormEncType; formData: undefined; json: JsonValue; text: undefined; } | { formMethod: FormMethod; formAction: string; formEncType: FormEncType; formData: undefined; json: undefined; text: string; }; /** * @private * Arguments passed to route loader/action functions. Same for now but we keep * this as a private implementation detail in case they diverge in the future. */ interface DataFunctionArgs<Context> { request: Request; params: Params; context?: Context; } /** * Arguments passed to loader functions */ interface LoaderFunctionArgs<Context = any> extends DataFunctionArgs<Context> { } /** * Arguments passed to action functions */ interface ActionFunctionArgs<Context = any> extends DataFunctionArgs<Context> { } /** * Loaders and actions can return anything except `undefined` (`null` is a * valid return value if there is no data to return). Responses are preferred * and will ease any future migration to Remix */ type DataFunctionValue = Response | NonNullable<unknown> | null; type DataFunctionReturnValue = Promise<DataFunctionValue> | DataFunctionValue; /** * Route loader function signature */ type LoaderFunction<Context = any> = { (args: LoaderFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue; } & { hydrate?: boolean; }; /** * Route action function signature */ interface ActionFunction<Context = any> { (args: ActionFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue; } /** * Arguments passed to shouldRevalidate function */ interface ShouldRevalidateFunctionArgs { currentUrl: URL; currentParams: AgnosticDataRouteMatch["params"]; nextUrl: URL; nextParams: AgnosticDataRouteMatch["params"]; formMethod?: Submission["formMethod"]; formAction?: Submission["formAction"]; formEncType?: Submission["formEncType"]; text?: Submission["text"]; formData?: Submission["formData"]; json?: Submission["json"]; actionStatus?: number; actionResult?: any; defaultShouldRevalidate: boolean; } /** * Route shouldRevalidate function signature. This runs after any submission * (navigation or fetcher), so we flatten the navigation/fetcher submission * onto the arguments. It shouldn't matter whether it came from a navigation * or a fetcher, what really matters is the URLs and the formData since loaders * have to re-run based on the data models that were potentially mutated. */ interface ShouldRevalidateFunction { (args: ShouldRevalidateFunctionArgs): boolean; } interface DataStrategyMatch extends AgnosticRouteMatch<string, AgnosticDataRouteObject> { shouldLoad: boolean; resolve: (handlerOverride?: (handler: (ctx?: unknown) => DataFunctionReturnValue) => DataFunctionReturnValue) => Promise<DataStrategyResult>; } interface DataStrategyFunctionArgs<Context = any> extends DataFunctionArgs<Context> { matches: DataStrategyMatch[]; fetcherKey: string | null; } /** * Result from a loader or action called via dataStrategy */ interface DataStrategyResult { type: "data" | "error"; result: unknown; } interface DataStrategyFunction { (args: DataStrategyFunctionArgs): Promise<Record<string, DataStrategyResult>>; } type AgnosticPatchRoutesOnNavigationFunctionArgs<O extends AgnosticRouteObject = AgnosticRouteObject, M extends AgnosticRouteMatch = AgnosticRouteMatch> = { path: string; matches: M[]; patch: (routeId: string | null, children: O[]) => void; }; type AgnosticPatchRoutesOnNavigationFunction<O extends AgnosticRouteObject = AgnosticRouteObject, M extends AgnosticRouteMatch = AgnosticRouteMatch> = (opts: AgnosticPatchRoutesOnNavigationFunctionArgs<O, M>) => void | Promise<void>; /** * Function provided by the framework-aware layers to set any framework-specific * properties from framework-agnostic properties */ interface MapRoutePropertiesFunction { (route: AgnosticRouteObject): { hasErrorBoundary: boolean; } & Record<string, any>; } /** * Keys we cannot change from within a lazy() function. We spread all other keys * onto the route. Either they're meaningful to the router, or they'll get * ignored. */ type ImmutableRouteKey = "lazy" | "caseSensitive" | "path" | "id" | "index" | "children"; type RequireOne<T, Key = keyof T> = Exclude<{ [K in keyof T]: K extends Key ? Omit<T, K> & Required<Pick<T, K>> : never; }[keyof T], undefined>; /** * lazy() function to load a route definition, which can add non-matching * related properties to a route */ interface LazyRouteFunction<R extends AgnosticRouteObject> { (): Promise<RequireOne<Omit<R, ImmutableRouteKey>>>; } /** * Base RouteObject with common props shared by all types of routes */ type AgnosticBaseRouteObject = { caseSensitive?: boolean; path?: string; id?: string; loader?: LoaderFunction | boolean; action?: ActionFunction | boolean; hasErrorBoundary?: boolean; shouldRevalidate?: ShouldRevalidateFunction; handle?: any; lazy?: LazyRouteFunction<AgnosticBaseRouteObject>; }; /** * Index routes must not have children */ type AgnosticIndexRouteObject = AgnosticBaseRouteObject & { children?: undefined; index: true; }; /** * Non-index routes may have children, but cannot have index */ type AgnosticNonIndexRouteObject = AgnosticBaseRouteObject & { children?: AgnosticRouteObject[]; index?: false; }; /** * A route object represents a logical route, with (optionally) its child * routes organized in a tree-like structure. */ type AgnosticRouteObject = AgnosticIndexRouteObject | AgnosticNonIndexRouteObject; type AgnosticDataIndexRouteObject = AgnosticIndexRouteObject & { id: string; }; type AgnosticDataNonIndexRouteObject = AgnosticNonIndexRouteObject & { children?: AgnosticDataRouteObject[]; id: string; }; /** * A data route object, which is just a RouteObject with a required unique ID */ type AgnosticDataRouteObject = AgnosticDataIndexRouteObject | AgnosticDataNonIndexRouteObject; type RouteManifest<R = AgnosticDataRouteObject> = Record<string, R | undefined>; type Regex_az = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"; type Regez_AZ = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"; type Regex_09 = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; type Regex_w = Regex_az | Regez_AZ | Regex_09 | "_"; type ParamChar = Regex_w | "-"; type RegexMatchPlus<CharPattern extends string, T extends string> = T extends `${infer First}${infer Rest}` ? First extends CharPattern ? RegexMatchPlus<CharPattern, Rest> extends never ? First : `${First}${RegexMatchPlus<CharPattern, Rest>}` : never : never; type _PathParam<Path extends string> = Path extends `${infer L}/${infer R}` ? _PathParam<L> | _PathParam<R> : Path extends `:${infer Param}` ? Param extends `${infer Optional}?${string}` ? RegexMatchPlus<ParamChar, Optional> : RegexMatchPlus<ParamChar, Param> : never; type PathParam<Path extends string> = Path extends "*" | "/*" ? "*" : Path extends `${infer Rest}/*` ? "*" | _PathParam<Rest> : _PathParam<Path>; type ParamParseKey<Segment extends string> = [ PathParam<Segment> ] extends [never] ? string : PathParam<Segment>; /** * The parameters that were parsed from the URL path. */ type Params<Key extends string = string> = { readonly [key in Key]: string | undefined; }; /** * A RouteMatch contains info about how a route matched a URL. */ interface AgnosticRouteMatch<ParamKey extends string = string, RouteObjectType extends AgnosticRouteObject = AgnosticRouteObject> { /** * The names and values of dynamic parameters in the URL. */ params: Params<ParamKey>; /** * The portion of the URL pathname that was matched. */ pathname: string; /** * The portion of the URL pathname that was matched before child routes. */ pathnameBase: string; /** * The route object that was used to match. */ route: RouteObjectType; } interface AgnosticDataRouteMatch extends AgnosticRouteMatch<string, AgnosticDataRouteObject> { } /** * Matches the given routes to a location and returns the match data. * * @category Utils */ declare function matchRoutes<RouteObjectType extends AgnosticRouteObject = AgnosticRouteObject>(routes: RouteObjectType[], locationArg: Partial<Location> | string, basename?: string): AgnosticRouteMatch<string, RouteObjectType>[] | null; interface UIMatch<Data = unknown, Handle = unknown> { id: string; pathname: string; params: AgnosticRouteMatch["params"]; data: Data; handle: Handle; } /** * Returns a path with params interpolated. * * @category Utils */ declare function generatePath<Path extends string>(originalPath: Path, params?: { [key in PathParam<Path>]: string | null; }): string; /** * A PathPattern is used to match on some portion of a URL pathname. */ interface PathPattern<Path extends string = string> { /** * A string to match against a URL pathname. May contain `:id`-style segments * to indicate placeholders for dynamic parameters. May also end with `/*` to * indicate matching the rest of the URL pathname. */ path: Path; /** * Should be `true` if the static portions of the `path` should be matched in * the same case. */ caseSensitive?: boolean; /** * Should be `true` if this pattern should match the entire URL pathname. */ end?: boolean; } /** * A PathMatch contains info about how a PathPattern matched on a URL pathname. */ interface PathMatch<ParamKey extends string = string> { /** * The names and values of dynamic parameters in the URL. */ params: Params<ParamKey>; /** * The portion of the URL pathname that was matched. */ pathname: string; /** * The portion of the URL pathname that was matched before child routes. */ pathnameBase: string; /** * The pattern that was used to match. */ pattern: PathPattern; } /** * Performs pattern matching on a URL pathname and returns information about * the match. * * @category Utils */ declare function matchPath<ParamKey extends ParamParseKey<Path>, Path extends string>(pattern: PathPattern<Path> | Path, pathname: string): PathMatch<ParamKey> | null; /** * Returns a resolved path object relative to the given pathname. * * @category Utils */ declare function resolvePath(to: To, fromPathname?: string): Path; declare class DataWithResponseInit<D> { type: string; data: D; init: ResponseInit | null; constructor(data: D, init?: ResponseInit); } /** * Create "responses" that contain `status`/`headers` without forcing * serialization into an actual `Response` - used by Remix single fetch * * @category Utils */ declare function data<D>(data: D, init?: number | ResponseInit): DataWithResponseInit<D>; type RedirectFunction = (url: string, init?: number | ResponseInit) => Response; /** * A redirect response. Sets the status code and the `Location` header. * Defaults to "302 Found". * * @category Utils */ declare const redirect: RedirectFunction; /** * A redirect response that will force a document reload to the new location. * Sets the status code and the `Location` header. * Defaults to "302 Found". * * @category Utils */ declare const redirectDocument: RedirectFunction; /** * A redirect response that will perform a `history.replaceState` instead of a * `history.pushState` for client-side navigation redirects. * Sets the status code and the `Location` header. * Defaults to "302 Found". * * @category Utils */ declare const replace: RedirectFunction; type ErrorResponse = { status: number; statusText: string; data: any; }; /** * @private * Utility class we use to hold auto-unwrapped 4xx/5xx Response bodies * * We don't export the class for public use since it's an implementation * detail, but we export the interface above so folks can build their own * abstractions around instances via isRouteErrorResponse() */ declare class ErrorResponseImpl implements ErrorResponse { status: number; statusText: string; data: any; private error?; private internal; constructor(status: number, statusText: string | undefined, data: any, internal?: boolean); } /** * Check if the given error is an ErrorResponse generated from a 4xx/5xx * Response thrown from an action/loader * * @category Utils */ declare function isRouteErrorResponse(error: any): error is ErrorResponse; /** * A Router instance manages all navigation and data loading/mutations */ interface Router { /** * @private * PRIVATE - DO NOT USE * * Return the basename for the router */ get basename(): RouterInit["basename"]; /** * @private * PRIVATE - DO NOT USE * * Return the future config for the router */ get future(): FutureConfig; /** * @private * PRIVATE - DO NOT USE * * Return the current state of the router */ get state(): RouterState; /** * @private * PRIVATE - DO NOT USE * * Return the routes for this router instance */ get routes(): AgnosticDataRouteObject[]; /** * @private * PRIVATE - DO NOT USE * * Return the window associated with the router */ get window(): RouterInit["window"]; /** * @private * PRIVATE - DO NOT USE * * Initialize the router, including adding history listeners and kicking off * initial data fetches. Returns a function to cleanup listeners and abort * any in-progress loads */ initialize(): Router; /** * @private * PRIVATE - DO NOT USE * * Subscribe to router.state updates * * @param fn function to call with the new state */ subscribe(fn: RouterSubscriber): () => void; /** * @private * PRIVATE - DO NOT USE * * Enable scroll restoration behavior in the router * * @param savedScrollPositions Object that will manage positions, in case * it's being restored from sessionStorage * @param getScrollPosition Function to get the active Y scroll position * @param getKey Function to get the key to use for restoration */ enableScrollRestoration(savedScrollPositions: Record<string, number>, getScrollPosition: GetScrollPositionFunction, getKey?: GetScrollRestorationKeyFunction): () => void; /** * @private * PRIVATE - DO NOT USE * * Navigate forward/backward in the history stack * @param to Delta to move in the history stack */ navigate(to: number): Promise<void>; /** * Navigate to the given path * @param to Path to navigate to * @param opts Navigation options (method, submission, etc.) */ navigate(to: To | null, opts?: RouterNavigateOptions): Promise<void>; /** * @private * PRIVATE - DO NOT USE * * Trigger a fetcher load/submission * * @param key Fetcher key * @param routeId Route that owns the fetcher * @param href href to fetch * @param opts Fetcher options, (method, submission, etc.) */ fetch(key: string, routeId: string, href: string | null, opts?: RouterFetchOptions): Promise<void>; /** * @private * PRIVATE - DO NOT USE * * Trigger a revalidation of all current route loaders and fetcher loads */ revalidate(): Promise<void>; /** * @private * PRIVATE - DO NOT USE * * Utility function to create an href for the given location * @param location */ createHref(location: Location | URL): string; /** * @private * PRIVATE - DO NOT USE * * Utility function to URL encode a destination path according to the internal * history implementation * @param to */ encodeLocation(to: To): Path; /** * @private * PRIVATE - DO NOT USE * * Get/create a fetcher for the given key * @param key */ getFetcher<TData = any>(key: string): Fetcher<TData>; /** * @private * PRIVATE - DO NOT USE * * Delete the fetcher for a given key * @param key */ deleteFetcher(key: string): void; /** * @private * PRIVATE - DO NOT USE * * Cleanup listeners and abort any in-progress loads */ dispose(): void; /** * @private * PRIVATE - DO NOT USE * * Get a navigation blocker * @param key The identifier for the blocker * @param fn The blocker function implementation */ getBlocker(key: string, fn: BlockerFunction): Blocker; /** * @private * PRIVATE - DO NOT USE * * Delete a navigation blocker * @param key The identifier for the blocker */ deleteBlocker(key: string): void; /** * @private * PRIVATE DO NOT USE * * Patch additional children routes into an existing parent route * @param routeId The parent route id or a callback function accepting `patch` * to perform batch patching * @param children The additional children routes */ patchRoutes(routeId: string | null, children: AgnosticRouteObject[]): void; /** * @private * PRIVATE - DO NOT USE * * HMR needs to pass in-flight route updates to React Router * TODO: Replace this with granular route update APIs (addRoute, updateRoute, deleteRoute) */ _internalSetRoutes(routes: AgnosticRouteObject[]): void; /** * @private * PRIVATE - DO NOT USE * * Internal fetch AbortControllers accessed by unit tests */ _internalFetchControllers: Map<string, AbortController>; } /** * State maintained internally by the router. During a navigation, all states * reflect the the "old" location unless otherwise noted. */ interface RouterState { /** * The action of the most recent navigation */ historyAction: Action; /** * The current location reflected by the router */ location: Location; /** * The current set of route matches */ matches: AgnosticDataRouteMatch[]; /** * Tracks whether we've completed our initial data load */ initialized: boolean; /** * Current scroll position we should start at for a new view * - number -> scroll position to restore to * - false -> do not restore scroll at all (used during submissions) * - null -> don't have a saved position, scroll to hash or top of page */ restoreScrollPosition: number | false | null; /** * Indicate whether this navigation should skip resetting the scroll position * if we are unable to restore the scroll position */ preventScrollReset: boolean; /** * Tracks the state of the current navigation */ navigation: Navigation; /** * Tracks any in-progress revalidations */ revalidation: RevalidationState; /** * Data from the loaders for the current matches */ loaderData: RouteData; /** * Data from the action for the current matches */ actionData: RouteData | null; /** * Errors caught from loaders for the current matches */ errors: RouteData | null; /** * Map of current fetchers */ fetchers: Map<string, Fetcher>; /** * Map of current blockers */ blockers: Map<string, Blocker>; } /** * Data that can be passed into hydrate a Router from SSR */ type HydrationState = Partial<Pick<RouterState, "loaderData" | "actionData" | "errors">>; /** * Future flags to toggle new feature behavior */ interface FutureConfig { } /** * Initialization options for createRouter */ interface RouterInit { routes: AgnosticRouteObject[]; history: History; basename?: string; mapRouteProperties?: MapRoutePropertiesFunction; future?: Partial<FutureConfig>; hydrationData?: HydrationState; window?: Window; dataStrategy?: DataStrategyFunction; patchRoutesOnNavigation?: AgnosticPatchRoutesOnNavigationFunction; } /** * State returned from a server-side query() call */ interface StaticHandlerContext { basename: Router["basename"]; location: RouterState["location"]; matches: RouterState["matches"]; loaderData: RouterState["loaderData"]; actionData: RouterState["actionData"]; errors: RouterState["errors"]; statusCode: number; loaderHeaders: Record<string, Headers>; actionHeaders: Record<string, Headers>; _deepestRenderedBoundaryId?: string | null; } /** * A StaticHandler instance manages a singular SSR navigation/fetch event */ interface StaticHandler { dataRoutes: AgnosticDataRouteObject[]; query(request: Request, opts?: { requestContext?: unknown; skipLoaderErrorBubbling?: boolean; dataStrategy?: DataStrategyFunction; }): Promise<StaticHandlerContext | Response>; queryRoute(request: Request, opts?: { routeId?: string; requestContext?: unknown; dataStrategy?: DataStrategyFunction; }): Promise<any>; } type ViewTransitionOpts = { currentLocation: Location; nextLocation: Location; }; /** * Subscriber function signature for changes to router state */ interface RouterSubscriber { (state: RouterState, opts: { deletedFetchers: string[]; viewTransitionOpts?: ViewTransitionOpts; flushSync: boolean; }): void; } /** * Function signature for determining the key to be used in scroll restoration * for a given location */ interface GetScrollRestorationKeyFunction { (location: Location, matches: UIMatch[]): string | null; } /** * Function signature for determining the current scroll position */ interface GetScrollPositionFunction { (): number; } /** - "route": relative to the route hierarchy so `..` means remove all segments of the current route even if it has many. For example, a `route("posts/:id")` would have both `:id` and `posts` removed from the url. - "path": relative to the pathname so `..` means remove one segment of the pathname. For example, a `route("posts/:id")` would have only `:id` removed from the url. */ type RelativeRoutingType = "route" | "path"; type BaseNavigateOrFetchOptions = { preventScrollReset?: boolean; relative?: RelativeRoutingType; flushSync?: boolean; }; type BaseNavigateOptions = BaseNavigateOrFetchOptions & { replace?: boolean; state?: any; fromRouteId?: string; viewTransition?: boolean; }; type BaseSubmissionOptions = { formMethod?: HTMLFormMethod; formEncType?: FormEncType; } & ({ formData: FormData; body?: undefined; } | { formData?: undefined; body: any; }); /** * Options for a navigate() call for a normal (non-submission) navigation */ type LinkNavigateOptions = BaseNavigateOptions; /** * Options for a navigate() call for a submission navigation */ type SubmissionNavigateOptions = BaseNavigateOptions & BaseSubmissionOptions; /** * Options to pass to navigate() for a navigation */ type RouterNavigateOptions = LinkNavigateOptions | SubmissionNavigateOptions; /** * Options for a fetch() load */ type LoadFetchOptions = BaseNavigateOrFetchOptions; /** * Options for a fetch() submission */ type SubmitFetchOptions = BaseNavigateOrFetchOptions & BaseSubmissionOptions; /** * Options to pass to fetch() */ type RouterFetchOptions = LoadFetchOptions | SubmitFetchOptions; /** * Potential states for state.navigation */ type NavigationStates = { Idle: { state: "idle"; location: undefined; formMethod: undefined; formAction: undefined; formEncType: undefined; formData: undefined; json: undefined; text: undefined; }; Loading: { state: "loading"; location: Location; formMethod: Submission["formMethod"] | undefined; formAction: Submission["formAction"] | undefined; formEncType: Submission["formEncType"] | undefined; formData: Submission["formData"] | undefined; json: Submission["json"] | undefined; text: Submission["text"] | undefined; }; Submitting: { state: "submitting"; location: Location; formMethod: Submission["formMethod"]; formAction: Submission["formAction"]; formEncType: Submission["formEncType"]; formData: Submission["formData"]; json: Submission["json"]; text: Submission["text"]; }; }; type Navigation = NavigationStates[keyof NavigationStates]; type RevalidationState = "idle" | "loading"; /** * Potential states for fetchers */ type FetcherStates<TData = any> = { /** * The fetcher is not calling a loader or action * * ```tsx * fetcher.state === "idle" * ``` */ Idle: { state: "idle"; formMethod: undefined; formAction: undefined; formEncType: undefined; text: undefined; formData: undefined; json: undefined; /** * If the fetcher has never been called, this will be undefined. */ data: TData | undefined; }; /** * The fetcher is loading data from a {@link LoaderFunction | loader} from a * call to {@link FetcherWithComponents.load | `fetcher.load`}. * * ```tsx * // somewhere * <button onClick={() => fetcher.load("/some/route") }>Load</button> * * // the state will update * fetcher.state === "loading" * ``` */ Loading: { state: "loading"; formMethod: Submission["formMethod"] | undefined; formAction: Submission["formAction"] | undefined; formEncType: Submission["formEncType"] | undefined; text: Submission["text"] | undefined; formData: Submission["formData"] | undefined; json: Submission["json"] | undefined; data: TData | undefined; }; /** The fetcher is submitting to a {@link LoaderFunction} (GET) or {@link ActionFunction} (POST) from a {@link FetcherWithComponents.Form | `fetcher.Form`} or {@link FetcherWithComponents.submit | `fetcher.submit`}. ```tsx // somewhere <input onChange={e => { fetcher.submit(event.currentTarget.form, { method: "post" }); }} /> // the state will update fetcher.state === "submitting" // and formData will be available fetcher.formData ``` */ Submitting: { state: "submitting"; formMethod: Submission["formMethod"]; formAction: Submission["formAction"]; formEncType: Submission["formEncType"]; text: Submission["text"]; formData: Submission["formData"]; json: Submission["json"]; data: TData | undefined; }; }; type Fetcher<TData = any> = FetcherStates<TData>[keyof FetcherStates<TData>]; interface BlockerBlocked { state: "blocked"; reset(): void; proceed(): void; location: Location; } interface BlockerUnblocked { state: "unblocked"; reset: undefined; proceed: undefined; location: undefined; } interface BlockerProceeding { state: "proceeding"; reset: undefined; proceed: undefined; location: Location; } type Blocker = BlockerUnblocked | BlockerBlocked | BlockerProceeding; type BlockerFunction = (args: { currentLocation: Location; nextLocation: Location; historyAction: Action; }) => boolean; declare const IDLE_NAVIGATION: NavigationStates["Idle"]; declare const IDLE_FETCHER: FetcherStates["Idle"]; declare const IDLE_BLOCKER: BlockerUnblocked; /** * Create a router and listen to history POP navigations */ declare function createRouter(init: RouterInit): Router; interface CreateStaticHandlerOptions { basename?: string; mapRouteProperties?: MapRoutePropertiesFunction; future?: {}; } interface IndexRouteObject { caseSensitive?: AgnosticIndexRouteObject["caseSensitive"]; path?: AgnosticIndexRouteObject["path"]; id?: AgnosticIndexRouteObject["id"]; loader?: AgnosticIndexRouteObject["loader"]; action?: AgnosticIndexRouteObject["action"]; hasErrorBoundary?: AgnosticIndexRouteObject["hasErrorBoundary"]; shouldRevalidate?: AgnosticIndexRouteObject["shouldRevalidate"]; handle?: AgnosticIndexRouteObject["handle"]; index: true; children?: undefined; element?: React.ReactNode | null; hydrateFallbackElement?: React.ReactNode | null; errorElement?: React.ReactNode | null; Component?: React.ComponentType | null; HydrateFallback?: React.ComponentType | null; ErrorBoundary?: React.ComponentType | null; lazy?: LazyRouteFunction<RouteObject>; } interface NonIndexRouteObject { caseSensitive?: AgnosticNonIndexRouteObject["caseSensitive"]; path?: AgnosticNonIndexRouteObject["path"]; id?: AgnosticNonIndexRouteObject["id"]; loader?: AgnosticNonIndexRouteObject["loader"]; action?: AgnosticNonIndexRouteObject["action"]; hasErrorBoundary?: AgnosticNonIndexRouteObject["hasErrorBoundary"]; shouldRevalidate?: AgnosticNonIndexRouteObject["shouldRevalidate"]; handle?: AgnosticNonIndexRouteObject["handle"]; index?: false; children?: RouteObject[]; element?: React.ReactNode | null; hydrateFallbackElement?: React.ReactNode | null; errorElement?: React.ReactNode | null; Component?: React.ComponentType | null; HydrateFallback?: React.ComponentType | null; ErrorBoundary?: React.ComponentType | null; lazy?: LazyRouteFunction<RouteObject>; } type RouteObject = IndexRouteObject | NonIndexRouteObject; type DataRouteObject = RouteObject & { children?: DataRouteObject[]; id: string; }; interface RouteMatch<ParamKey extends string = string, RouteObjectType extends RouteObject = RouteObject> extends AgnosticRouteMatch<ParamKey, RouteObjectType> { } interface DataRouteMatch extends RouteMatch<string, DataRouteObject> { } type PatchRoutesOnNavigationFunctionArgs = AgnosticPatchRoutesOnNavigationFunctionArgs<RouteObject, RouteMatch>; type PatchRoutesOnNavigationFunction = AgnosticPatchRoutesOnNavigationFunction<RouteObject, RouteMatch>; interface DataRouterContextObject extends Omit<NavigationContextObject, "future"> { router: Router; staticContext?: StaticHandlerContext; } declare const DataRouterContext: React.Context<DataRouterContextObject | null>; declare const DataRouterStateContext: React.Context<RouterState | null>; type ViewTransitionContextObject = { isTransitioning: false; } | { isTransitioning: true; flushSync: boolean; currentLocation: Location; nextLocation: Location; }; declare const ViewTransitionContext: React.Context<ViewTransitionContextObject>; type FetchersContextObject = Map<string, any>; declare const FetchersContext: React.Context<FetchersContextObject>; interface NavigateOptions { replace?: boolean; state?: any; preventScrollReset?: boolean; relative?: RelativeRoutingType; flushSync?: boolean; viewTransition?: boolean; } /** * A Navigator is a "location changer"; it's how you get to different locations. * * Every history instance conforms to the Navigator interface, but the * distinction is useful primarily when it comes to the low-level `<Router>` API * where both the location and a navigator must be provided separately in order * to avoid "tearing" that may occur in a suspense-enabled app if the action * and/or location were to be read directly from the history instance. */ interface Navigator { createHref: History["createHref"]; encodeLocation?: History["encodeLocation"]; go: History["go"]; push(to: To, state?: any, opts?: NavigateOptions): void; replace(to: To, state?: any, opts?: NavigateOptions): void; } interface NavigationContextObject { basename: string; navigator: Navigator; static: boolean; future: {}; } declare const NavigationContext: React.Context<NavigationContextObject>; interface LocationContextObject { location: Location; navigationType: Action; } declare const LocationContext: React.Context<LocationContextObject>; interface RouteContextObject { outlet: React.ReactElement | null; matches: RouteMatch[]; isDataRoute: boolean; } declare const RouteContext: React.Context<RouteContextObject>; type Primitive = null | undefined | string | number | boolean | symbol | bigint; type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>); interface HtmlLinkProps { /** * Address of the hyperlink */ href?: string; /** * How the element handles crossorigin requests */ crossOrigin?: "anonymous" | "use-credentials"; /** * Relationship between the document containing the hyperlink and the destination resource */ rel: LiteralUnion<"alternate" | "dns-prefetch" | "icon" | "manifest" | "modulepreload" | "next" | "pingback" | "preconnect" | "prefetch" | "preload" | "prerender" | "search" | "stylesheet", string>; /** * Applicable media: "screen", "print", "(max-width: 764px)" */ media?: string; /** * Integrity metadata used in Subresource Integrity checks */ integrity?: string; /** * Language of the linked resource */ hrefLang?: string; /** * Hint for the type of the referenced resource */ type?: string; /** * Referrer policy for fetches initiated by the element */ referrerPolicy?: "" | "no-referrer" | "no-referrer-when-downgrade" | "same-origin" | "origin" | "strict-origin" | "origin-when-cross-origin" | "strict-origin-when-cross-origin" | "unsafe-url"; /** * Sizes of the icons (for rel="icon") */ sizes?: string; /** * Potential destination for a preload request (for rel="preload" and rel="modulepreload") */ as?: LiteralUnion<"audio" | "audioworklet" | "document" | "embed" | "fetch" | "font" | "frame" | "iframe" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt", string>; /** * Color to use when customizing a site's icon (for rel="mask-icon") */ color?: string; /** * Whether the link is disabled */ disabled?: boolean; /** * The title attribute has special semantics on this element: Title of the link; CSS style sheet set name. */ title?: string; /** * Images to use in different situations, e.g., high-resolution displays, * small monitors, etc. (for rel="preload") */ imageSrcSet?: string; /** * Image sizes for different page layouts (for rel="preload") */ imageSizes?: string; } interface HtmlLinkPreloadImage extends HtmlLinkProps { /** * Relationship between the document containing the hyperlink and the destination resource */ rel: "preload"; /** * Potential destination for a preload request (for rel="preload" and rel="modulepreload") */ as: "image"; /** * Address of the hyperlink */ href?: string; /** * Images to use in different situations, e.g., high-resolution displays, * small monitors, etc. (for rel="preload") */ imageSrcSet: string; /** * Image sizes for different page layouts (for rel="preload") */ imageSizes?: string; } /** * Represents a `<link>` element. * * WHATWG Specification: https://html.spec.whatwg.org/multipage/semantics.html#the-link-element */ type HtmlLinkDescriptor = (HtmlLinkProps & Pick<Required<HtmlLinkProps>, "href">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "imageSizes">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "href"> & { imageSizes?: never; }); interface PageLinkDescriptor extends Omit<HtmlLinkDescriptor, "href" | "rel" | "type" | "sizes" | "imageSrcSet" | "imageSizes" | "as" | "color" | "title"> { /** * The absolute path of the page to prefetch. */ page: string; } type LinkDescriptor = HtmlLinkDescriptor | PageLinkDescriptor; interface RouteModules { [routeId: string]: RouteModule | undefined; } interface RouteModule { clientAction?: ClientActionFunction; clientLoader?: ClientLoaderFunction; ErrorBoundary?: ErrorBoundaryComponent; HydrateFallback?: HydrateFallbackComponent; Layout?: LayoutComponent; default: RouteComponent; handle?: RouteHandle; links?: LinksFunction; meta?: MetaFunction; shouldRevalidate?: ShouldRevalidateFunction; } /** * A function that handles data mutations for a route on the client */ type ClientActionFunction = (args: ClientActionFunctionArgs) => ReturnType<ActionFunction>; /** * Arguments passed to a route `clientAction` function */ type ClientActionFunctionArgs = ActionFunctionArgs<undefined> & { serverAction: <T = unknown>() => Promise<SerializeFrom<T>>; }; /** * A function that loads data for a route on the client */ type ClientLoaderFunction = ((args: ClientLoaderFunctionArgs) => ReturnType<LoaderFunction>) & { hydrate?: boolean; }; /** * Arguments passed to a route `clientLoader` function */ type ClientLoaderFunctionArgs = LoaderFunctionArgs<undefined> & { serverLoader: <T = unknown>() => Promise<SerializeFrom<T>>; }; /** * ErrorBoundary to display for this route */ type ErrorBoundaryComponent = ComponentType; /** * `<Route HydrateFallback>` component to render on initial loads * when client loaders are present */ type HydrateFallbackComponent = ComponentType; /** * Optional, root-only `<Route Layout>` component to wrap the root content in. * Useful for defining the <html>/<head>/<body> document shell shared by the * Component, HydrateFallback, and ErrorBoundary */ type LayoutComponent = ComponentType<{ children: ReactElement<unknown, ErrorBoundaryComponent | HydrateFallbackComponent | RouteComponent>; }>; /** * A function that defines `<link>` tags to be inserted into the `<head>` of * the document on route transitions. * * @see https://remix.run/route/meta */ interface LinksFunction { (): LinkDescriptor[]; } interface MetaMatch<RouteId extends string = string, Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown> { id: RouteId; pathname: DataRouteMatch["pathname"]; data: Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown; handle?: RouteHandle; params: DataRouteMatch["params"]; meta: MetaDescriptor[]; error?: unknown; } type MetaMatches<MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> = Array<{ [K in keyof MatchLoaders]: MetaMatch<Exclude<K, number | symbol>, MatchLoaders[K]>; }[keyof MatchLoaders]>; interface MetaArgs<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> { data: (Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown) | undefined; params: Params; location: Location; matches: MetaMatches<MatchLoaders>; error?: unknown; } /** * A function that returns an array of data objects to use for rendering * metadata HTML tags in a route. These tags are not rendered on descendant * routes in the route hierarchy. In other words, they will only be rendered on * the route in which they are exported. * * @param Loader - The type of the current route's loader function * @param MatchLoaders - Mapping from a parent route's filepath to its loader * function type * * Note that parent route filepaths are relative to the `app/` directory. * * For example, if this meta function is for `/sales/customers/$customerId`: * * ```ts * // app/root.tsx * const loader = () => ({ hello: "world" }) * export type Loader = typeof loader * * // app/routes/sales.tsx * const loader = () => ({ salesCount: 1074 }) * export type Loader = typeof loader * * // app/routes/sales/customers.tsx * const loader = () => ({ customerCount: 74 }) * export type Loader = typeof loader * * // app/routes/sales/customers/$customersId.tsx * import type { Loader as RootLoader } from "../../../root" * import type { Loader as SalesLoader } from "../../sales" * import type { Loader as CustomersLoader } from "../../sales/customers" * * const loader = () => ({ name: "Customer name" }) * * const meta: MetaFunction<typeof loader, { * "root": RootLoader, * "routes/sales": SalesLoader, * "routes/sales/customers": CustomersLoader, * }> = ({ data, matches }) => { * const { name } = data * // ^? string * const { customerCount } = matches.find((match) => match.id === "routes/sales/customers").data * // ^? number * const { salesCount } = matches.find((match) => match.id === "routes/sales").data * // ^? number * const { hello } = matches.find((match) => match.id === "root").data * // ^? "world" * } * ``` */ interface MetaFunction<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> { (args: MetaArgs<Loader, MatchLoaders>): MetaDescriptor[] | undefined; } type MetaDescriptor = { charSet: "utf-8"; } | { title: string; } | { name: string; content: string; } | { property: string; content: string; } | { httpEquiv: string; content: string; } | { "script:ld+json": LdJsonObject; } | { tagName: "meta" | "link"; [name: string]: string; } | { [name: string]: unknown; }; type LdJsonObject = { [Key in string]: LdJsonValue; } & { [Key in string]?: LdJsonVal