The @remix-run/router
package is a framework-agnostic routing package (sometimes referred to as a browser-emulator) that serves as the heart of React Router and Remix and provides all the core functionality for routing coupled with data loading and data mutations. It comes with built-in handling of errors, race-conditions, interruptions, cancellations, lazy-loading data, and much, much more.
If you're using React Router, you should never import
anything directly from the @remix-run/router
- you should have everything you need in react-router-dom
(or react-router
/react-router-native
if you're not rendering in the browser). All of those packages should re-export everything you would otherwise need from @remix-run/router
.
[!WARNING]
This router is a low-level package intended to be consumed by UI layer routing libraries. You should very likely not be using this package directly unless you are authoring a routing library such as
react-router-dom
or one of it's other UI ports.
A Router instance can be created using createRouter
:
// Create and initialize a router. "initialize" contains all side effects
// including history listeners and kicking off the initial data fetch
let router = createRouter({
// Required properties
routes: [{
path: '/',
loader: ({ request, params }) => { /* ... */ },
children: [{
path: 'home',
loader: ({ request, params }) => { /* ... */ },
}]
},
history: createBrowserHistory(),
// Optional properties
basename, // Base path
mapRouteProperties, // Map framework-agnostic routes to framework-aware routes
future, // Future flags
hydrationData, // Hydration data if using server-side-rendering
}).initialize();
Internally, the Router represents the state in an object of the following format, which is available through router.state
. You can also register a subscriber of the signature (state: RouterState) => void
to execute when the state updates via router.subscribe()
;
interface RouterState {
// False during the initial data load, true once we have our initial data
initialized: boolean;
// The `history` action of the most recently completed navigation
historyAction: Action;
// The current location of the router. During a navigation this reflects
// the "old" location and is updated upon completion of the navigation
location: Location;
// The current set of route matches
matches: DataRouteMatch[];
// The state of the current navigation
navigation: Navigation;
// The state of any in-progress router.revalidate() calls
revalidation: RevalidationState;
// Data from the loaders for the current matches
loaderData: RouteData;
// Data from the action for the current matches
actionData: RouteData | null;
// Errors thrown from loaders/actions for the current matches
errors: RouteData | null;
// Map of all active fetchers
fetchers: Map<string, Fetcher>;
// Scroll position to restore to for the active Location, false if we
// should not restore, or null if we don't have a saved position
// Note: must be enabled via router.enableScrollRestoration()
restoreScrollPosition: number | false | null;
// Proxied `preventScrollReset` value passed to router.navigate()
preventScrollReset: boolean;
}
All navigations are done through the router.navigate
API which is overloaded to support different types of navigations:
// Link navigation (pushes onto the history stack by default)
router.navigate("/page");
// Link navigation (replacing the history stack)
router.navigate("/page", { replace: true });
// Pop navigation (moving backward/forward in the history stack)
router.navigate(-1);
// Form submission navigation
let formData = new FormData();
formData.append(key, value);
router.navigate("/page", {
formMethod: "post",
formData,
});
// Relative routing from a source routeId
router.navigate("../../somewhere", {
fromRouteId: "active-route-id",
});
Fetchers are a mechanism to call loaders/actions without triggering a navigation, and are done through the router.fetch()
API. All fetch calls require a unique key to identify the fetcher.
// Execute the loader for /page
router.fetch("key", "/page");
// Submit to the action for /page
let formData = new FormData();
formData.append(key, value);
router.fetch("key", "/page", {
formMethod: "post",
formData,
});
By default, active loaders will revalidate after any navigation or fetcher mutation. If you need to kick off a revalidation for other use-cases, you can use router.revalidate()
to re-execute all active loaders.
We use Future Flags in the router to help us introduce breaking changes in an opt-in fashion ahead of major releases. Please check out the blog post and React Router Docs for more information on this process. The currently available future flags in @remix-run/router
are:
Flag | Description |
---|---|
v7_normalizeFormMethod |
Normalize useNavigation().formMethod to be an uppercase HTTP Method |
v7_prependBasename |
Prepend the basename to incoming router.navigate /router.fetch paths |