React DOM bindings for Remix web framework providing components, hooks, and utilities for full-stack React applications
—
Comprehensive type definitions for route modules, component props, and framework integration in Remix applications.
/**
* Function that loads data for a route on the client side
* Runs in the browser and can call server loader via serverLoader
*/
type ClientLoaderFunction = ((
args: ClientLoaderFunctionArgs
) => ReturnType<LoaderFunction>) & {
/** Whether to hydrate this loader on initial page load */
hydrate?: boolean;
};
/**
* Arguments passed to a route clientLoader function
*/
interface ClientLoaderFunctionArgs extends LoaderFunctionArgs {
/** Function to call the server loader */
serverLoader: <T = AppData>() => Promise<SerializeFrom<T>>;
}
/**
* Function that handles data mutations for a route on the client side
* Runs in the browser and can call server action via serverAction
*/
type ClientActionFunction = (
args: ClientActionFunctionArgs
) => ReturnType<ActionFunction>;
/**
* Arguments passed to a route clientAction function
*/
interface ClientActionFunctionArgs extends ActionFunctionArgs {
/** Function to call the server action */
serverAction: <T = AppData>() => Promise<SerializeFrom<T>>;
}Usage Examples:
import type { ClientLoaderFunction, ClientActionFunction } from "@remix-run/react";
// Client loader with server fallback
export const clientLoader: ClientLoaderFunction = async ({
params,
serverLoader
}) => {
// Try cache first
const cached = getCachedData(params.id);
if (cached) {
return cached;
}
// Fall back to server loader
const serverData = await serverLoader();
setCachedData(params.id, serverData);
return serverData;
};
// Client action with optimistic updates
export const clientAction: ClientActionFunction = async ({
request,
serverAction
}) => {
const formData = await request.formData();
// Optimistic update
updateUIOptimistically(formData);
try {
return await serverAction();
} catch (error) {
// Revert optimistic update
revertOptimisticUpdate();
throw error;
}
};/**
* Function that returns meta descriptors for a route
* Used to generate meta tags in the document head
*/
type MetaFunction<
Loader = unknown,
ParentsLoaders = {}
> = (args: MetaArgs<Loader, ParentsLoaders>) => MetaDescriptor[];
/**
* Arguments passed to meta functions
*/
interface MetaArgs<Loader = unknown, ParentsLoaders = {}> {
/** Data from the route's loader */
data: SerializeFrom<Loader>;
/** Route parameters */
params: Params;
/** Current request object */
request: Request;
/** Current location */
location: Location;
/** Matched routes with their meta data */
matches: MetaMatches<ParentsLoaders>;
/** Error if the route threw */
error?: Error;
}
/**
* Individual meta descriptor types
*/
type MetaDescriptor =
| { title: string }
| { name: string; content: string }
| { property: string; content: string }
| { httpEquiv: string; content: string }
| { charset: string }
| { "script:ld+json": LdJsonObject }
| { tagName: "meta"; [key: string]: string }
| { tagName: string; [key: string]: unknown };
/**
* Meta matches from parent routes
*/
type MetaMatches<ParentsLoaders = {}> = Array<
MetaMatch<string, ParentsLoaders[keyof ParentsLoaders]>
>;
interface MetaMatch<
RouteId extends string = string,
Loader extends unknown = unknown
> {
id: RouteId;
pathname: string;
data: SerializeFrom<Loader>;
params: Params<string>;
meta: MetaDescriptor[];
}Usage Examples:
import type { MetaFunction } from "@remix-run/react";
export const meta: MetaFunction<typeof loader> = ({
data,
params,
location,
matches
}) => {
if (!data) {
return [{ title: "Not Found" }];
}
const parentMeta = matches.find(match => match.id === "root")?.meta || [];
return [
{ title: `${data.post.title} | My Blog` },
{ name: "description", content: data.post.excerpt },
{ property: "og:title", content: data.post.title },
{ property: "og:description", content: data.post.excerpt },
{ property: "og:image", content: data.post.imageUrl },
{ property: "og:url", content: `https://myblog.com${location.pathname}` },
...parentMeta.filter(meta => "name" in meta && meta.name === "author"),
];
};/**
* Main route component type
*/
type RouteComponent = ComponentType;
/**
* Error boundary component for route-level errors
*/
type ErrorBoundaryComponent = ComponentType;
/**
* Hydration fallback component for client loaders
*/
type HydrateFallbackComponent = ComponentType;
/**
* Layout component for root route
*/
type LayoutComponent = ComponentType<{
children: ReactElement<
unknown,
ErrorBoundaryComponent | HydrateFallbackComponent | RouteComponent
>;
}>;
/**
* Links function for route-level stylesheets and resources
*/
interface LinksFunction {
(): HtmlLinkDescriptor[];
}
/**
* Route handle for custom metadata
*/
interface RouteHandle {
[key: string]: any;
}/**
* Props for RemixBrowser component
*/
interface RemixBrowserProps {
/** Base path for all routes */
basename?: string;
}
/**
* Props for RemixServer component
*/
interface RemixServerProps {
/** Entry context from server */
context: EntryContext;
/** Request URL */
url: string | URL;
/** Abort delay for deferred requests */
abortDelay?: number;
/** Nonce for CSP */
nonce?: string;
}/**
* Props for enhanced Form component
*/
interface RemixFormProps extends Omit<FormHTMLAttributes<HTMLFormElement>, "onSubmit"> {
method?: "get" | "post" | "put" | "patch" | "delete";
action?: string;
discover?: "render" | "none";
encType?: "application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain";
replace?: boolean;
preventScrollReset?: boolean;
reloadDocument?: boolean;
navigate?: boolean;
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void;
}
/**
* Props for enhanced Link component
*/
interface RemixLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
to: string | Partial<Path>;
discover?: "render" | "none";
prefetch?: "none" | "intent" | "render" | "viewport";
replace?: boolean;
state?: any;
preventScrollReset?: boolean;
relative?: "route" | "path";
reloadDocument?: boolean;
unstable_viewTransition?: boolean;
}
/**
* Props for enhanced NavLink component
*/
interface RemixNavLinkProps extends RemixLinkProps {
className?: string | ((props: { isActive: boolean; isPending: boolean }) => string);
style?: CSSProperties | ((props: { isActive: boolean; isPending: boolean }) => CSSProperties);
children?: ReactNode | ((props: { isActive: boolean; isPending: boolean }) => ReactNode);
end?: boolean;
caseSensitive?: boolean;
}
/**
* Props for Await component
*/
interface AwaitProps<T> {
/** Promise to resolve */
resolve: Promise<T>;
/** Error boundary fallback */
errorElement?: ReactNode;
/** Children function that receives resolved value */
children: ReactNode | ((value: T) => ReactNode);
}/**
* Props for Scripts component
*/
interface ScriptProps {
/** Nonce for CSP */
nonce?: string;
/** Suppress hydration warnings */
suppressHydrationWarning?: boolean;
}
/**
* Props for ScrollRestoration component
*/
interface ScrollRestorationProps extends ScriptProps {
/** Function to generate storage key */
getKey?: (location: Location, matches: UIMatch[]) => string;
}
/**
* Props for LiveReload component (development only)
*/
interface LiveReloadProps {
/** Port for live reload server */
port?: number;
/** Reconnection timeout */
timeoutMs?: number;
/** Custom origin */
origin?: string;
/** Nonce for CSP */
nonce?: string;
}/**
* HTML link descriptor for stylesheets and resources
*/
interface HtmlLinkDescriptor {
/** Link relationship */
rel: string;
/** Resource URL */
href?: string;
/** Media query */
media?: string;
/** MIME type */
type?: string;
/** Integrity hash */
integrity?: string;
/** Cross-origin policy */
crossOrigin?: "anonymous" | "use-credentials";
/** Referrer policy */
referrerPolicy?:
| ""
| "no-referrer"
| "no-referrer-when-downgrade"
| "same-origin"
| "origin"
| "strict-origin"
| "origin-when-cross-origin"
| "strict-origin-when-cross-origin"
| "unsafe-url";
/** Icon sizes */
sizes?: string;
/** Preload destination */
as?:
| "audio"
| "document"
| "embed"
| "fetch"
| "font"
| "image"
| "object"
| "script"
| "style"
| "track"
| "video"
| "worker";
/** Resource language */
hrefLang?: string;
}
/**
* Prefetch page descriptor
*/
interface PrefetchPageDescriptor {
/** Page path to prefetch */
page: string;
}These types are exported with UNSAFE_ prefix for advanced usage but may change in future versions.
/**
* Future configuration flags
*/
interface FutureConfig {
unstable_singleFetch?: boolean;
unstable_fogOfWar?: boolean;
unstable_optimizeDeps?: boolean;
}
/**
* Assets manifest
*/
interface AssetsManifest {
version: string;
url: string;
entry: {
module: string;
imports: string[];
};
routes: Record<string, {
id: string;
parentId?: string;
path?: string;
index?: boolean;
caseSensitive?: boolean;
module: string;
imports?: string[];
hasAction: boolean;
hasLoader: boolean;
hasClientAction: boolean;
hasClientLoader: boolean;
hasErrorBoundary: boolean;
}>;
}
/**
* Remix context object
*/
interface RemixContextObject {
manifest: AssetsManifest;
routeModules: RouteModules;
serverHandoffString?: string;
abortDelay?: number;
serializeError: (error: Error) => SerializedError;
future: FutureConfig;
isSpaMode: boolean;
}
/**
* Entry route definition
*/
interface EntryRoute {
id: string;
parentId?: string;
path?: string;
index?: boolean;
caseSensitive?: boolean;
module: string;
imports?: string[];
hasAction: boolean;
hasLoader: boolean;
hasClientAction: boolean;
hasClientLoader: boolean;
hasErrorBoundary: boolean;
}
/**
* Route manifest
*/
type RouteManifest = Record<string, EntryRoute>;/**
* Serialized form of data from loaders/actions
*/
type SerializeFrom<T> = T extends (...args: any[]) => infer R
? SerializeFrom<R>
: T extends Date
? string
: T extends object
? { [K in keyof T]: SerializeFrom<T[K]> }
: T;
/**
* UI match information
*/
interface UIMatch<T = unknown, Handle = RouteHandle> {
id: string;
pathname: string;
params: Params;
data: T;
handle: Handle | undefined;
}
/**
* Generic app data type
*/
type AppData = unknown;
/**
* Route discovery behavior options
* Controls when route modules should be discovered and loaded
*/
type DiscoverBehavior = "render" | "none";
/**
* Prefetch behavior options
* Controls when and how route data and assets should be prefetched
*/
type PrefetchBehavior = "intent" | "render" | "none" | "viewport";
/**
* Route parameters
*/
type Params<Key extends string = string> = {
readonly [key in Key]: string | undefined;
};Install with Tessl CLI
npx tessl i tessl/npm-remix-run--react