What is @remix-run/router?
The @remix-run/router package is a powerful routing library designed for the Remix framework, which is used for building modern web applications. It provides robust features for handling URL routing, navigation, and data loading in a React environment. The router is designed to work seamlessly with both client-side and server-side rendering, offering a flexible and efficient way to manage routes in a web application.
What are @remix-run/router's main functionalities?
Route Matching
This feature allows developers to define a set of routes and corresponding components. The router matches the current URL to the defined routes and renders the appropriate component.
import { createBrowserRouter, RouterProvider, Route } from '@remix-run/router';
const router = createBrowserRouter([
{
path: '/',
element: <Home />
},
{
path: '/about',
element: <About />
}
]);
function App() {
return <RouterProvider router={router} />;
}
Nested Routes
Nested routes allow for the organization of components into a hierarchy, matching nested paths to nested components, which is useful for creating complex layouts with sub-routes.
import { createBrowserRouter, RouterProvider, Route } from '@remix-run/router';
const router = createBrowserRouter([
{
path: 'dashboard',
element: <DashboardLayout />,
children: [
{ path: 'stats', element: <Stats /> },
{ path: 'reports', element: <Reports /> }
]
}
]);
function App() {
return <RouterProvider router={router} />;
}
Data Loading
This feature supports defining data loading functions directly within the route configuration. These functions fetch necessary data before rendering the route's component, ensuring the component has all data it needs on initial render.
import { createBrowserRouter, RouterProvider, Route } from '@remix-run/router';
const router = createBrowserRouter([
{
path: '/profile',
element: <Profile />,
loader: async () => {
const data = await fetchUserProfile();
return data;
}
}
]);
function App() {
return <RouterProvider router={router} />;
}
Other packages similar to @remix-run/router
react-router
React Router is one of the most popular routing solutions for React applications. It offers similar functionalities to @remix-run/router, such as dynamic route matching, nested routes, and server-side rendering support. However, React Router operates more broadly within the React ecosystem, whereas @remix-run/router is specifically optimized for the Remix framework.
Router
The @remix-run/router
package is the heart of React Router and provides all the core functionality for routing, data loading, data mutations, and navigation.
If you're using React Router, you should never import
anything directly from
the @remix-run/router
or react-router
packages, but you should have everything
you need in either react-router-dom
or react-router-native
. Both of those
packages re-export everything from @remix-run/router
and react-router
.
API
A Router instance can be created using createRouter
:
let router = createRouter({
routes,
history,
onChange: (state) => { ... },
hydrationData?: HydrationState;
}
Internally, the Router represents the state in an object of the following format, which is available through router.state
or via the onChange
callback function:
interface RouterState {
action: Action;
location: Location;
matches: RouteMatch[];
navigation: Navigation;
loaderData: RouteData;
actionData: RouteData | null;
errors: RouteData | null;
}
Navigations
All navigations are done through the router.navigate
API which is overloaded to support different types of navigations:
router.navigate('/page');
router.navigate('/page', { replace: true });
router.navigate(-1);
router.navigate('/page', {
formMethod: 'GET',
formData: new FormData(...),
});
Navigation Flows
Each navigation (link click or form submission) in the Router is reflected via an internal state.navigation
that indicates the state of the navigation. This concept of a navigation
is a complex and heavily async bit of logic that is foundational to the Router's ability to manage data loading, submission, error handling, redirects, interruptions, and so on. Due to the user-driven nature of interruptions we don't quite believe it can be modeled as a finite state machine, however we have modeled some of the happy path flows below for clarity.
Note: This does not depict error or interruption flows.
graph LR
%% Link click
idle -->|link clicked| loading/normalLoad
subgraph "<Link> click"
loading/normalLoad -->|loader redirected| loading/normalRedirect
loading/normalRedirect --> loading/normalRedirect
end
loading/normalLoad -->|loaders completed| idle
loading/normalRedirect -->|loaders completed| idle
%% Form method=get
idle -->|form method=get| submitting/loaderSubmission
subgraph "<Form method=get>"
submitting/loaderSubmission -->|loader redirected| R1[loading/submissionRedirect]
R1[loading/submissionRedirect] --> R1[loading/submissionRedirect]
end
submitting/loaderSubmission -->|loaders completed| idle
R1[loading/submissionRedirect] -->|loaders completed| idle
%% Form method=post
idle -->|form method=post| submitting/actionSubmission
subgraph "<Form method=post>"
submitting/actionSubmission -->|action returned| loading/actionReload
submitting/actionSubmission -->|action redirected| R2[loading/submissionRedirect]
loading/actionReload -->|loader redirected| R2[loading/submissionRedirect]
R2[loading/submissionRedirect] --> R2[loading/submissionRedirect]
end
loading/actionReload -->|loaders completed| idle
R2[loading/submissionRedirect] -->|loaders completed| idle
idle -->|fetcher action redirect| R3[loading/submissionRedirect]
subgraph "Fetcher action redirect"
R3[loading/submissionRedirect] --> R3[loading/submissionRedirect]
end
R3[loading/submissionRedirect] -->|loaders completed| idle
Fetcher Flows
Fetcher submissions and loads in the Router can each have their own internal states, indicated on fetcher.state
and fetcher.type
. As with navigations, these states are a complex and heavily async bit of logic that is foundational to the Router's ability to manage data loading, submission, error handling, redirects, interruptions, and so on. Due to the user-driven nature of interruptions we don't quite believe it can be modeled as a finite state machine, however we have modeled some of the happy path flows below for clarity.
Note: This does not depict error or interruption flows, nor the ability to re-use fetchers once they've reached idle/done
.
graph LR
idle/init -->|"load"| loading/normalLoad
subgraph "Normal Fetch"
loading/normalLoad -.->|loader redirected| T1{{navigation}}
end
loading/normalLoad -->|loader completed| idle/done
T1{{navigation}} -.-> idle/done
idle/init -->|"submit (get)"| submitting/loaderSubmission
subgraph "Loader Submission"
submitting/loaderSubmission -.->|"loader redirected"| T2{{navigation}}
end
submitting/loaderSubmission -->|loader completed| idle/done
T2{{navigation}} -.-> idle/done
idle/init -->|"submit (post)"| submitting/actionSubmission
subgraph "Action Submission"
submitting/actionSubmission -->|action completed| loading/actionReload
submitting/actionSubmission -->|action redirected| loading/submissionRedirect
loading/submissionRedirect -.-> T3{{navigation}}
loading/actionReload -.-> |loaders redirected| T3{{navigation}}
end
T3{{navigation}} -.-> idle/done
loading/actionReload --> |loaders completed| idle/done
idle/done -->|"revalidate"| loading/revalidate
subgraph "Fetcher Revalidation"
loading/revalidate -.->|loader redirected| T4{{navigation}}
end
loading/revalidate -->|loader completed| idle/done
T4{{navigation}} -.-> idle/done
classDef navigation fill:lightgreen;
class T1,T2,T3,T4 navigation;