react-router
Version:
Declarative routing for React
1,508 lines (1,499 loc) • 54.8 kB
TypeScript
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