Modern.js provides out-of-the-box data fetching capabilities. Developers can use these APIs to fetch data in their projects. It's important to note that these APIs do not help the application make requests but assist developers in managing data better and improving project performance.
In Modern.js v1 projects, data was fetched using useLoader
. This is no longer the recommended approach; we suggest migrating to Data Loader.
Modern.js recommends managing routes using conventional routing. Each route component (layout.ts
, page.ts
, or $.tsx
) can have a same-named .data
file. These files can export a loader
function, known as Data Loader, which executes before the corresponding route component renders to provide data for the component. Here is an example:
In the routes/user/page.data.ts
file, you can export a Data Loader function:
.loader
file. In the current version, we recommend defining it in a .data
file, while maintaining compatibility with .loader
files..loader
files, the Data Loader can be exported as default. In .data
files, it should be named loader
export.In the route component, you can use the useLoaderData
function to fetch data:
Route components and .data
files share types. Use import type
to avoid unexpected side effects.
In a CSR environment, the loader
function executes on the browser side and can use browser APIs (though it's usually unnecessary and not recommended).
In an SSR environment, the loader
function only executes on the server side for initial page loads and when navigating. Here it can call any Node.js APIs, and any dependencies or code used won't be included in the client-side bundle.
In future versions, Modern.js may support running loader
functions on the server side even in CSR environments to improve performance and security. Therefore, it is advisable to keep the loader
function pure, handling only data fetching scenarios.
When navigating on the client side, based on conventional routing, Modern.js supports parallel execution (requests) of all loader
functions. For example, when visiting /user/profile
, the loader
functions under /user
and /user/profile
will execute in parallel, solving the request-render waterfall issue and significantly improving page performance.
loader
FunctionThe loader
function has two parameters used for getting route parameters and request information.
params
is the dynamic route segments when the route is a dynamic route, which passed as parameters to the loader
function:
request
is an instance of Fetch Request. A common use case is to get query parameters from request
:
The return value of the loader
function must be one of two data structures: a serializable data object or an instance of Fetch Response.
By default, the loader
response's Content-type
is application/json
, and its status
is 200. You can customize the Response
to change these:
The loader
function may run on the server or client. When it runs on the server, it's called a Server Loader; when it runs on the client, it's called a Client Loader.
In CSR applications, the loader
function runs on the client, hence it is a Client Loader by default.
In SSR applications, the loader
function runs only on the server, hence it is a Server Loader by default. During SSR rendering, Modern.js will directly call the loader
function on the server side. When navigating on the client side, Modern.js sends an HTTP request to the SSR service, also triggering the loader
function on the server side.
Having the loader
function run only on the server in SSR applications brings several benefits:
We recommend using the fetch
API in loader
functions to make requests. Modern.js provides a default polyfill for the fetch
API, allowing it to be used on the server. This means you can fetch data in a consistent manner whether in CSR or SSR:
In a loader
function, you can handle errors by using throw error
or throw response
. When an error is thrown in the loader
function, Modern.js will stop executing the remaining code in the current loader
and switch the front-end UI to the defined ErrorBoundary
component:
In SSR projects, you can control the page status code by throwing a response in the loader
function and display the corresponding UI.
In the following example, the page's status code will match this response
, and the page will display the ErrorBoundary
UI:
In many scenarios, child components need to access data from the upper component's loader
. You can use the useRouteLoaderData
function to easily get data from the upper component:
useRouteLoaderData
accepts a parameter routeId
. In conventional routing, Modern.js automatically generates the routeId
, which is the path of the corresponding component relative to src/routes
. In the example above, the routeId
for fetching data from routes/user/layout.tsx
's loader is user/layout
.
In a multi-entry scenario, the routeId
value needs to include the corresponding entry name, which is typically the directory name if not explicitly specified. For example, with the following directory structure:
To get data returned by the loader in entry1/routes/layout.tsx
, the routeId
value would be entry1_layout
.
Create user/layout.data.ts
and add the following code:
Add the following code in user/layout.tsx
:
During route navigation, Modern.js will only load data for the parts of the route that change. For example, if the current route is a/b
, and the Data Loader for the a
path has already executed, then when transitioning from /a/b
to /a/c
, the Data Loader for the a
path will not re-execute, but the Data Loader for the c
path will execute and fetch the data.
This default optimization strategy avoids redundant data requests. However, you might wonder how to update the data for the a
path's Data Loader?
In Modern.js, the Data Loader for a specific path will reload in the following scenarios:
shouldRevalidate
function that returns true
If you define a shouldRevalidate
function for a route, this function will be checked first to determine whether data reloads.
shouldRevalidate
Currently, shouldRevalidate
only takes effect in CSR and Streaming SSR.
In route components (layout.tsx
, page.tsx
, $.tsx
), you can export a shouldRevalidate
function. This function is triggered on each route change in the project and can control which route data to reload. If this function returns true
, Modern.js will reload the corresponding route data.
For more details on the shouldRevalidate
function, refer to the react-router documentation.
loader
can only return serializable data. In an SSR environment, the return value of the loader
function will be serialized as a JSON string and then deserialized as an object on the client side. Therefore, the loader
function should not return non-serializable data such as functions.This limitation currently does not exist in CSR, but we strongly recommend adhering to it, as future versions may enforce this restriction in CSR as well.
loader
function for you, and you should not call it yourself:loader
files from route components, and do not import variables from route components into loader
files. If you need to share types, use import type
.loader
functions are packaged into a single bundle. Therefore, we do not recommend using __filename
and __dirname
in server code.loader
and BFF functions?In CSR projects, the loader
executes on the client side and can directly call BFF functions to make API requests.
In SSR projects, each loader
is also a server-side API. We recommend using loader
instead of BFF functions with an HTTP method of get
to avoid an extra layer of forwarding and execution.