---
title: Routes
sidebar_position: 1
---

# Routes

Modern.js's routing is based on [React Router 6](https://reactrouter.com/en/main) and provides multiple types of routing modes. According to different [entry](/guides/concept/entries) types, routing is divided into three modes: **Conventional Routing**, **Self-controlled Routing**, and **Other**.

:::note
The routing mentioned in this section refers to client-side routing, i.e., SPA routing.
:::

## Conventional Routing

With `routes/` as the convention for entry points, Modern.js automatically generates the corresponding routing structure based on the file system.

Modern.js supports the popular conventional routing mode in the industry: **Nested Routing**. When using nested routing, the page's routing corresponds to the UI structure, and we will introduce this routing mode in detail.

```bash
/user/johnny/profile                  /user/johnny/posts
+------------------+                  +-----------------+
| User             |                  | User            |
| +--------------+ |                  | +-------------+ |
| | Profile      | |  +------------>  | | Posts       | |
| |              | |                  | |             | |
| +--------------+ |                  | +-------------+ |
+------------------+                  +-----------------+
```

### Routing File Convention

Under the `routes/` directory, the directory name is mapped to the route URL. Modern.js has two file conventions, `layout.[jt]sx` and `page.[jt]sx` (abbreviated as`.tsx`). These two files determine the layout structure of the application. `layout.tsx` is used as the layout component, and `page.tsx` acts as the content component, which is the leaf node of the entire route (a route has only one leaf node and must end with a leaf node).

For example, the following directory structure:

```bash
.
└── routes
    ├── page.tsx
    └── user
        └── page.tsx
```

This will generate the following two routes:

- `/`
- `/user`

When `layout.tsx` is added, assuming the following directory:

:::info
Here, `routes/layout.tsx` will be used as the layout component for all components under the `/` route, and `routes/user/layout.tsx` will be used as the layout component for all route components under the `/user` route.
:::

```bash
.
└── routes
    ├── layout.tsx
    ├── page.tsx
    └── user
        ├── layout.tsx
        └── page.tsx
```

When the route is `/`, the following UI layout will be displayed:

```tsx
<Layout>
  <Page />
</Layout>
```

Similarly, `routes/user/layout.tsx` will be used as the layout component for all components under the `/user` route. When the route is `/user`, the following UI layout will be displayed:

```tsx
<Layout>
  <UserLayout>
    <UserPage />
  </UserLayout>
</Layout>
```

#### Layout

The `<Layout>` component refers to all `layout.tsx` files under the `routes/` directory. They represent the layout of the corresponding route segment and use `<Outlet>` to represent child components.

```tsx title=routes/layout.tsx
import { Link, Outlet, useLoaderData } from '@modern-js/runtime/router';

export default () => {
  return (
    <>
      <Outlet></Outlet>
    </>
  );
};
```

:::note
`<Outlet>` is a new API in React Router 6. For more details, please refer to [Outlet](https://reactrouter.com/en/main/components/outlet#outlet).

:::

To simplify the introduction of the relationship between `<Layout>` and `<Outlet>`, the following file directory is used as an example:

```bash
.
└── routes
    ├── blog
    │   └── page.tsx
    ├── layout.tsx
    ├── page.tsx
    └── user
        ├── layout.tsx
        └── page.tsx
```

1. When the route is `/`, the `<Outlet>` in `routes/layout.tsx` represents the component exported in `routes/page.tsx`, generating the following UI structure:

```tsx
<Layout>
  <Page />
</Layout>
```

2. When the route is `/blog`, the `<Outlet>` in `routes/layout.tsx` represents the component exported in `routes/blog/page.tsx`, generating the following UI structure:

```tsx
<Layout>
  <BlogPage />
</Layout>
```

3. When the route is `/user`, the `<Outlet>` in `routes/layout.tsx` represents the component exported in `routes/user/layout.tsx`. The `<Outlet>` in `routes/user/layout.tsx` represents the component exported in `routes/user/page.tsx`, generating the following UI structure:

```tsx
<Layout>
  <UserLayout>
    <UserPage />
  </UserLayout>
</Layout>
```
In summary, if there is a `layout.tsx` file under the sub-route's file directory, the `<Outlet>` in the parent `layout.tsx` will represent the `layout.tsx` in the sub-route file directory. Otherwise, it will represent the `page.tsx` in the sub-route file directory.

#### Page

All routes should end with the `<Page>` component. If the developer introduces the `<Outlet>` component in the `page.tsx` file, there will be no effect.

### Dynamic Routing

Routes generated from file directories named with `[]` will be handled as dynamic routes. For example, the following file directory:

```bash
└── routes
    ├── [id]
    │   └── page.tsx
    ├── blog
    │   └── page.tsx
    └── page.tsx
```

The `routes/[id]/page.tsx` file will be converted to the `/:id` route. Except for the exact matching `/blog` route, all other `/xxx` routes will match this route.

In the component, you can use [useParams](/apis/app/runtime/router/router#useparams) to get the corresponding named parameter.

In the loader, params will be passed as the input parameter of the [loader function](/guides/basic-features/data-fetch#loader-function), and you can get the parameter value through `params.xxx`.

### Dynamic Optional Routing

Routes generated from file directories named with `[$]` will be treated as dynamic optional routes. For example, the following file directory:

```bash
└── routes
    ├── user
    │   └── [id$]
    │       └── page.tsx
    ├── blog
    │   └── page.tsx
    └── page.tsx
```

The `routes/user/[id$]/page.tsx` file will be converted to the `/user/:id?` route. All routes under `/user` will match this route, and the `id` parameter is optional. This route is usually used to distinguish between **creation** and **editing**.

In the component, you can use [useParams](/apis/app/runtime/router/router#useparams) to get the corresponding named parameter.

In the loader, params will be passed as the input parameter of the [loader function](/guides/basic-features/data-fetch#loader-function), and you can get the parameter value through `params.xxx`.

### Catch-all Routing

If you create a `$.tsx` file under the routes directory, it will be treated as the catch-all routing component. When there is no matching route, this component will be rendered.

:::note
`$.tsx` can be considered as a special `page` route component. When there is a `layout.tsx` file in the current directory, `$.tsx` will be rendered as a child component of `layout`.
:::

For example, the following directory structure:

```bash
└── routes
    ├── $.tsx
    ├── blog
    │   └── page.tsx
    └── page.tsx
```

When accessing any path that does not match, the `routes/$.tsx` component will be rendered. Similarly, you can use [useParams](/apis/app/runtime/router/router#useparams) in `$.tsx` to capture the remaining parts of the URL.

```ts title="$.tsx"
import { useParams } from '@modern-js/runtime/router';
// When the path is `/aaa/bbb`
const params = useParams();
params['*']; // => 'aaa/bbb'
```

### No-path Layout

When the directory name starts with `__`, the directory name will not be converted to an actual route path. For example, the following file directory:

```bash
.
└── routes
    ├── __auth
    │   ├── layout.tsx
    │   ├── login
    │   │   └── page.tsx
    │   └── signup
    │       └── page.tsx
    ├── layout.tsx
    └── page.tsx
```

Modern.js will generate two routes, `/login` and `/signup`. The `__auth/layout.tsx` component will serve as the layout component for `login/page.tsx` and `signup/page.tsx`, but `__auth` will not be a route path segment.

This feature is very useful when you need to create independent layouts for certain types of routes or want to classify routes.

### No Layout

In some cases, the project requires complex routing, but these routes do not have independent UI layouts. If you create routes like ordinary file directories, it will result in deep directory levels.

Therefore, Modern.js supports using `.` to separate route segments instead of file directories. For example, when you need `/user/profile/2022/edit`, you can directly create the following files:

```bash
└── routes
    ├── user.profile.[id].edit
    │      └── page.tsx
    ├── layout.tsx
    └── page.tsx
```

When accessing the route, you will get the following UI layout:

```tsx
<RootLayout>
  <UserProfileEdit /> // routes/user.profile.[id].edit/page.tsx
</RootLayout>
```

### (WIP)Loading

In each directory under `routes/`, developers can create a `loading.tsx` file that exports a `<Loading>` component by default.

When this component and the `layout` component exist in the route directory, the `<Loading>` component will be used as the fallback UI when switching routes in this sub-route. For example, the following file directory:

```bash
.
└── routes
    ├── blog
    │   ├── [id]
    │   │   └── page.tsx
    │   └── page.tsx
    ├── layout.tsx
    ├── loading.tsx
    └── page.tsx
```

When defining `loading.tsx`, it is equivalent to the following layout:

When the route is `/`:

```tsx
<Layout>
  <Suspense fallback={<Loading />}>
    <Page />
  </Suspense>
</Layout>
```

When the route is `/blog`:

```tsx
<Layout>
  <Suspense fallback={<Loading />}>
    <BlogPage />
  </Suspense>
</Layout>
```

When the route is `/blog/123`:

```tsx
<Layout>
  <Suspense fallback={<Loading />}>
    <BlogIdPage />
  </Suspense>
</Layout>
```

:::info
When the Layout component does not exist in the directory, the Loading component in that directory will not take effect. Modern.js recommends having a root Layout and root Loading.

:::

When the route jumps from `/` to `/blog`, if the JS Chunk of the `blog/page` component has not been loaded yet, the UI of the component exported in `loading.tsx` will be displayed first.

Similarly, when the route jumps from `/` or `/blog` to `/blog/123`, if the JS Chunk of the `blog/[id]/page` component has not been loaded yet, the UI of the component exported in `loading.tsx` will be displayed first.

### Redirect

You can use a [Data Loader](/guides/basic-features/data-fetch) file to redirect a route. For example, if you have a `routes/user/page.tsx` file and want to redirect the corresponding route, you can create a `routes/user/page.loader.ts` file:

```ts title="routes/user/page.loader.ts"
import { redirect } from '@modern-js/runtime/router';

export default () => {
  const user = await getUser();
  if (!user) {
    return redirect('/login');
  }
  return null;
};
```

### ErrorBoundary

In each directory under `routes/`, developers can also define an `error.tsx` file that exports an `<ErrorBoundary>` component by default.

When this component exists in the routes directory, any rendering errors will be caught by the `ErrorBoundary` component. When the `layout.tsx` file is not defined in the directory, the `<ErrorBoundary>` component will not take effect.

`<ErrorBoundary>` can return the UI view when an error occurs. When the `<ErrorBoundary>` component is not declared in the current level, the error will bubble up to a higher-level component until it is caught or thrown. At the same time, when a component has an error, it will only affect the route component and its child components that catch the error. The status and view of other components are not affected and can continue to interact.

In the `<ErrorBoundary>` component, you can use [useRouteError](/apis/app/runtime/router/router#userouteerror) to get specific information about the error:

```tsx
import { useRouteError } from '@modern-js/runtime/router';
export const ErrorBoundary = () => {
  const error = useRouteError();
  return (
    <div>
      <h1>{error.status}</h1>
      <h2>{error.message}</h2>
    </div>
  );
};
```

### Runtime Configuration

In each root `Layout` component (`routes/layout.ts`), you can dynamically define runtime configuration:


```tsx title="src/routes/layout.tsx"
// Define runtime config
import type { AppConfig } from '@modern-js/runtime';

export const config = (): AppConfig => {
  return {
    router: {
      createRoutes() {
        return [
          {
            path: 'modern',
            element: <div>modern</div>,
          },
        ];
      },
    },
  };
};
```

### Hooks Before Rendering

In some scenarios, you may need to perform some operations before rendering the application. You can define an `init` hook in `routes/layout.tsx`. The `init` hook will be executed on both the client and server side. A basic example of usage is as follows:

```ts title="src/routes/layout.tsx"
import type { RuntimeContext } from '@modern-js/runtime';

export const init = (context: RuntimeContext) => {
  // do something
};
```

By using the `init` hook, you can mount some global data, and the `runtimeContext` variable can be accessed in other parts of the application:

:::note
This feature is very useful when the application needs pre-rendered data, custom data injection, or framework migration (such as Next.js).
:::

```ts title="src/routes/layout.tsx"
import { RuntimeContext } from '@modern-js/runtime';

export const init = (context: RuntimeContext) => {
  return {
    message: 'Hello World',
  };
};
```

```tsx title="src/routes/page.tsx"
import { useRuntimeContext } from '@modern-js/runtime';

export default () => {
  const { context } = useRuntimeContext();
  const { message } = context.getInitData();

  return <div>{message}</div>;
};
```

When used with the SSR feature, the data returned by the `init` hook during SSR can be obtained on the client side. Developers can decide whether to re-fetch data on the client side to overwrite the SSR data. For example:

```tsx title="src/routes/layout.tsx"
import { RuntimeContext } from '@modern-js/runtime';

export const init = (context: RuntimeContext) => {
  if (process.env.MODERN_TARGET === 'node') {
    return {
      message: 'Hello World By Server',
    };
  } else {
    const { context } = runtimeContext;
    const data = context.getInitData();
    // If do not get the expected data
    if (!data.message) {
      return {
        message: 'Hello World By Client',
      };
    }
  }
};
```

### Preloading

In conventional routing, Modern.js automatically splits routes into chunks based on the route. When a user visits a specific route, the corresponding chunk will be loaded automatically, effectively reducing the loading time of the initial screen.

However, this also brings a problem: if the chunk corresponding to the route has not finished loading when the user visits the route, a white screen will appear.

In this case, you can define a `Loading` component to display a custom `Loading` component before the static resources are loaded.

To further improve the user experience and reduce loading time, Modern.js supports defining the `prefetch` attribute on the Link component to preload static resources and data.

```tsx
<Link prefetch="intent" to="page">
```

:::info
- This feature is currently only supported in Webpack projects and not yet supported in Rspack projects.
- Preloading data currently only preloads the data returned by the [Data Loader](/guides/basic-features/data-fetch) in SSR projects.

:::

The `prefetch` attribute has three optional values:

- `none`: default value, no prefetching, no additional behavior.
- `intent`: This is the value we recommend for most scenarios. When you hover over the Link, the corresponding chunk and data defined in the data loader will be loaded automatically. When you move the mouse away, the loading will be cancelled automatically. In our tests, even fast clicks can reduce loading time by about 200ms.
- `render`: The corresponding chunk and data defined in the Data Loader will be loaded when the Link component is rendered.

#### FAQ

1. What is the difference between using `render` and not splitting static resources based on the route?

- By using `render`, you can specify which routes to load during the initial screen, and you can control the rendering so that only when the `Link` component enters the visible area, the `Link` component will be rendered.
- By using `render`, static resources will only be loaded when the system is idle, and will not compete with the static resources of the initial screen for network resources.
- In the SSR scenario, data will also be pre-fetched.

import Motivation from '@site-docs-en/components/convention-routing-motivation'

<Motivation/>

import Practice from '@site-docs-en/components/routes-practice'

<Practice/>

## Self-controlled Routing

With `src/App.tsx` as the convention for entry points, Modern.js will not perform any additional routing operations. Developers can use the API of [React Router 6](https://reactrouter.com/en/main) for development, for example:

```ts title="src/App.tsx"
import { BrowserRouter, Route, Routes } from '@modern-js/runtime/router';

export default () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route index element={<div>index</div>} />
        <Route path="about" element={<div>about</div>} />
      </Routes>
    </BrowserRouter>
  );
};
```

:::note
Modern.js provides a series of optimizations for resource loading and rendering for conventional routing by default, and provides out-of-the-box SSR capabilities. When using self-controlled routing, developers need to encapsulate these capabilities themselves. It is recommended to use conventional routing.
:::

## Other

By default, Modern.js will enable the built-in routing scheme, which is React Router.

```js
export default defineConfig({
  runtime: {
    router: true,
  },
});
```

As mentioned above, when the [`runtime.router`](/configure/app/runtime/router) configuration is enabled, Modern.js will export the API of React Router from the `@modern-js/runtime/router` namespace for developers to use, ensuring that developers and Modern.js are using the same code, and automatically wrapping the `Provider` component according to the router configuration. In addition, in this case, the code of React Router will be packed into the JS output.

If the project already has its own routing plan or does not need to use client-side routing, this feature can be disabled.

```js
export default defineConfig({
  runtime: {
    router: false,
  },
});
```

As mentioned above, if the [`runtime.router`](/configure/app/runtime/router) configuration is disabled and `react-router-dom` is used directly for project routing management, the `Provider` needs to be wrapped according to the React Router documentation.
