logo
  • Guide
  • Config
  • Plugin
  • API
  • Examples
  • Community
  • Modern.js 2.x Docs
  • English
    • 简体中文
    • English
    • Start
      Introduction
      Quick Start
      Upgrading
      Glossary
      Tech Stack
      Core Concept
      Page Entry
      Build Engine
      Web Server
      Basic Features
      Routes
      Routing
      Config Routes
      Data Solution
      Data Fetching
      Data Writing
      Data Caching
      Rendering
      Server-Side Rendering
      Streaming SSR
      Rendering Cache
      Static Site Generation
      Render Preprocessing
      Styling
      Styling
      Use CSS Modules
      Using CSS-in-JS
      Using Tailwind CSS
      HTML Template
      Import Static Assets
      Import JSON Files
      Import SVG Assets
      Import Wasm Assets
      Debug
      Data Mocking
      Network Proxy
      Using Rsdoctor
      Using Storybook
      Testing
      Playwright
      Vitest
      Jest
      Cypress
      Path Alias
      Environment Variables
      Output Files
      Deploy Application
      Advanced Features
      Using Rspack
      Using BFF
      Basic Usage
      Runtime Framework
      Extend BFF Server
      Extend Request SDK
      File Upload
      Cross-Project Invocation
      Optimize Page Performance
      Code Splitting
      Inline Static Assets
      Bundle Size Optimization
      React Compiler
      Improve Build Performance
      Browser Compatibility
      Low-Level Tools
      Source Code Build Mode
      Server Monitor
      Monitors
      Logs Events
      Metrics Events
      Internationalization
      Basic Concepts
      Quick Start
      Configuration
      Locale Detection
      Resource Loading
      Routing Integration
      API Reference
      Advanced Usage
      Best Practices
      Custom Web Server
      Topic Detail
      Module Federation
      Introduction
      Getting Started
      Application-Level Modules
      Server-Side Rendering
      Deployment
      Integrating Internationalization
      FAQ
      Dependencies FAQ
      CLI FAQ
      Build FAQ
      HMR FAQ
      Deprecated
      📝 Edit this page
      Previous pageRoutingNext pageData Fetching

      #Config Routes

      By default, Modern.js recommends using Convention Routes as the way to define routes. At the same time, Modern.js also provides a config-based routing capability that can be used together with convention routes or used separately.

      #When to Use Config Routes

      Config routes are particularly useful in the following scenarios:

      • Need flexible route control: When the file structure cannot directly map to the desired URL structure
      • Multiple routes pointing to the same component: Need to point different URL paths to the same page component
      • Conditional routes: Dynamically configure routes based on different conditions
      • Legacy project migration: Maintain the original routing structure when migrating from other routing systems
      • Complex route naming: Need to customize route paths without being limited by file directory structure

      #Basic Usage

      In the src directory or each entry directory, you can define a modern.routes.[tj]s file to configure routes:

      import { defineRoutes } from '@modern-js/runtime/config-routes'
      
      export default defineRoutes(({ route, layout, page, $ }, fileRoutes) => {
        return [
          route("home.tsx", "/"),
        ]
      })

      #Function Signature Description

      defineRoutes accepts a callback function with two parameters:

      1. routeFunctions: An object containing route, layout, page, $ functions
      2. fileRoutes: An array of route configurations generated by convention routes

      Basic signature of route functions:

      • First parameter: File path relative to modern.routes.[tj]s
      • Second parameter: Route path (optional, must be a string)
      • Third parameter: Array of child routes (optional)

      #Route Functions

      Modern.js provides four main route configuration functions:

      Path Description

      The first parameter (path) of all route functions is a relative path, which will be concatenated with the parent path to generate the final route path:

      • Root path: "/" or "" represents the root path of the current level
      • Relative path: Child route paths will be concatenated relative to the parent path
      • Dynamic path: Use :param syntax to represent dynamic parameters
      • Wildcard path: Use "*" syntax to match all paths
      Info
      • The first parameter (component file path) of functions like route, layout, page, $ must point to real files in the current project. Files from node_modules and other repositories in Monorepo are not currently supported.
      • Path aliases are not supported (such as @/pages/..., ~/pages/..., etc.); please use relative paths relative to modern.routes.[tj]s.

      #route Function

      The route function is a general-purpose route configuration function that automatically determines whether to generate a page route or layout route based on whether there are child routes. It can replace layout, page, $ and other functions.

      export default defineRoutes(({ route }, fileRoutes) => {
        return [
          // When no child routes, automatically generates page route
          route("home.tsx", "/"),
          route("about.tsx", "about"),
      
          // When has child routes, automatically generates layout route
          // dashboard/layout.tsx needs to contain <Outlet> to render child routes
          route("dashboard/layout.tsx", "dashboard", [
            route("dashboard/overview.tsx", "overview"),  // Generated path: /dashboard/overview
            route("dashboard/settings.tsx", "settings"),  // Generated path: /dashboard/settings
          ]),
      
          // Dynamic routes
          route("products/detail.tsx", "products/:id"),
      
          // Multiple paths pointing to the same component
          route("user/profile.tsx", "user"),
          route("user/profile.tsx", "profile"),
        ]
      })

      Use cases:

      • Quick route configuration without explicitly specifying page or layout
      • Simplify the complexity of route configuration

      #layout Function

      The layout function is specifically used to configure layout routes, always generates layout components, and must contain child routes:

      export default defineRoutes(({ layout, page }, fileRoutes) => {
        return [
          // Generate layout route with path "/auth", must contain child routes
          layout("auth/layout.tsx", "auth", [
            page("auth/login/page.tsx", "login"),      // Generated path: /auth/login
            page("auth/register/page.tsx", "register"), // Generated path: /auth/register
          ]),
        ]
      })

      Use cases:

      • Need to explicitly specify a component as a layout component
      • Provide common layout structure for multiple pages
      • Need to share navigation, sidebar and other UI components across multiple routes

      #page Function

      The page function is specifically used to configure page routes, always generates page components:

      export default defineRoutes(({ layout, page }, fileRoutes) => {
        return [
          layout("dashboard/layout.tsx", "dashboard", [
            page("dashboard/overview.tsx", "overview"),  // Generated path: /dashboard/overview
            page("dashboard/settings.tsx", "settings"),  // Generated path: /dashboard/settings
          ]),
        ]
      })

      Use cases:

      • Need to explicitly specify a component as a page component
      • Ensure the component does not contain <Outlet> child component rendering
      • Provide clearer semantic expression

      #$ Function

      The $ function is specifically used to configure wildcard routes for handling unmatched routes:

      export default defineRoutes(({ layout, page, $ }, fileRoutes) => {
        return [
          layout("blog/layout.tsx", "blog", [
            page("blog/page.tsx", ""),                    // Generated path: /blog
            page("blog/[id]/page.tsx", ":id"),            // Generated path: /blog/:id
            $("blog/$.tsx", "*"),                         // Wildcard route, matches all unmatched paths under /blog
          ]),
        ]
      })

      Use cases:

      • Custom 404 pages
      • Handle all unmatched requests under specific paths
      Tip

      The $ function has the same functionality as the $.tsx file in convention routes, used to catch unmatched route requests.

      #Configuring Routes

      #Basic Example

      export default defineRoutes(({ page }, fileRoutes) => {
        return [
          // Use page function to explicitly specify page route
          page("home.tsx", "/"),
          page("about.tsx", "about"),
          page("contact.tsx", "contact"),
        ]
      })

      #Routes Without Path

      When no path is specified, routes inherit the parent path:

      export default defineRoutes(({ layout, page }, fileRoutes) => {
        return [
          // Use layout function to explicitly specify layout route
          // auth/layout.tsx needs to contain <Outlet> to render child routes
          layout("auth/layout.tsx", [
            page("login/page.tsx", "login"),
            page("register/page.tsx", "register"),
          ]),
        ]
      })

      The above configuration will generate:

      • /login → auth/layout.tsx + login/page.tsx
      • /register → auth/layout.tsx + register/page.tsx

      #Multiple Paths Pointing to the Same Component

      export default defineRoutes(({ page }, fileRoutes) => {
        return [
          page("user.tsx", "user"),
          page("user.tsx", "profile"),
          page("user.tsx", "account"),
        ]
      })

      #Dynamic Routes

      Config routes support dynamic route parameters:

      export default defineRoutes(({ page }, fileRoutes) => {
        return [
          // Required parameter
          page("blog/detail.tsx", "blog/:id"),
      
          // Optional parameter
          page("blog/index.tsx", "blog/:slug?"),
        ]
      })

      #Automatic Route-Related File Discovery

      Config routes automatically discover component-related files without manual configuration. For any component file specified in modern.routes.[tj]s, Modern.js will automatically find the following related files:

      // modern.routes.[tj]s
      export default defineRoutes(({ route }, fileRoutes) => {
        return [
          route("pages/profile.tsx", "profile"),
          route("pages/user/detail.tsx", "user/:id"),
        ]
      })

      Modern.js will automatically find and load:

      • pages/profile.data.ts → Data loader
      • pages/profile.config.ts → Route configuration
      • pages/profile.error.tsx → Error boundary
      • pages/profile.loading.tsx → Loading component

      #Discovery Rules

      • File location: Related files must be in the same directory as the component file
      • File naming: Related file names are the same as the component file name (excluding extension)
      • Auto-discovery: Modern.js automatically discovers and loads these files
      Tip

      For more detailed information about data fetching, please refer to the Data Fetching documentation. For Loading component usage, please refer to Convention Routes - Loading.

      #Route Merging

      Config routes can be used together with convention routes. You can merge routes by modifying the fileRoutes parameter:

      1. Override routes: You can actively remove convention routes and replace them with config routes
      2. Supplement routes: You can add new config routes based on convention routes
      3. Mixed usage: You can add config child routes under convention layout routes
      Info

      Current route structure can be viewed through the modern routes command

      #Merging Examples

      The following examples demonstrate how to merge config routes with convention routes:

      // modern.routes.ts
      import { defineRoutes } from '@modern-js/runtime/config-routes';
      
      export default defineRoutes(({ page }, fileRoutes) => {
        // Scenario 1: Override convention routes
        // Remove the original shop route and replace with custom component
        const shopPageIndex = fileRoutes[0].children?.findIndex(
          route => route.id === 'three_shop/page',
        );
        fileRoutes[0].children?.splice(shopPageIndex!, 1);
        fileRoutes[0].children?.push(page('routes/CustomShop.tsx', 'shop'));
      
        // Scenario 2: Supplement convention routes
        // Add config routes without corresponding convention routes
        fileRoutes[0].children?.push(page('routes/Settings.tsx', 'settings'));
      
        // Scenario 3: Mixed nested routes
        // Add config child routes under convention layout routes
        const userRoute = fileRoutes[0].children?.find(
          (route: any) => route.path === 'user',
        );
        if (userRoute?.children) {
          userRoute.children.push(page('routes/user/CustomTab.tsx', 'custom-tab'));
        }
      
        // Scenario 4: Automatic discovery of related files
        // Product.tsx will automatically discover Product.data.ts and Product.error.tsx
        fileRoutes[0].children?.push(page('routes/Product.tsx', 'product/:id'));
      
        return fileRoutes;
      });

      #Debugging Routes

      Since the final structure after route merging may not be intuitive, Modern.js provides debugging commands to help you view complete route information:

      # Generate route structure analysis report
      npx modern routes

      This command will generate the final route structure in the dist/routes-inspect.json file, helping you understand the complete route information after merging.

      #Report File Examples

      #Single Entry Scenario

      dist/routes-inspect.json
      {
        "routes": [
          {
            "path": "/",
            "component": "@_modern_js_src/routes/page",
            "data": "@_modern_js_src/routes/page.data",
            "clientData": "@_modern_js_src/routes/page.data.client",
            "error": "@_modern_js_src/routes/page.error",
            "loading": "@_modern_js_src/routes/page.loading"
          },
          {
            "path": "/dashboard",
            "component": "pages/admin",
            "config": "pages/admin.config",
            "error": "pages/admin.error"
          },
          {
            "path": "/user",
            "component": "layouts/user",
            "children": [
              {
                "path": "/user/profile",
                "component": "@_modern_js_src/routes/user/profile",
                "data": "@_modern_js_src/routes/user/profile.data"
              }
            ]
          },
          {
            "path": "@_modern_js_src/routes/blog/:id",
            "component": "blog/detail",
            "params": ["id"],
            "data": "blog/detail.data",
            "loading": "blog/detail.loading"
          },
          {
            "path": "/files/*",
            "component": "@_modern_js_src/routes/files/list"
          }
        ]
      }

      #Multi-entry Scenario

      In multi-entry projects, the report file will be grouped by entry name, where the key is entryName:

      dist/routes-inspect.json
      {
        "main": {
          "routes": [
            {
              "path": "/",
              "component": "@_modern_js_src/routes/page",
              "data": "@_modern_js_src/routes/page.data",
              "error": "@_modern_js_src/routes/page.error",
              "loading": "@_modern_js_src/routes/page.loading"
            },
            {
              "path": "/dashboard",
              "component": "@_modern_js_src/routes/dashboard",
              "config": "@_modern_js_src/routes/dashboard.config"
            }
          ]
        },
        "admin": {
          "routes": [
            {
              "path": "/",
              "component": "@_modern_js_src/routes/dashboard",
              "data": "@_modern_js_src/routes/dashboard.data",
              "clientData": "@_modern_js_src/routes/dashboard.data.client",
              "config": "@_modern_js_src/routes/dashboard.config"
            },
            {
              "path": "/users",
              "component": "@_modern_js_src/routes/users",
              "data": "@_modern_js_src/routes/users.data",
              "error": "@_modern_js_src/routes/users.error",
              "loading": "@_modern_js_src/routes/users.loading"
            }
          ]
        }
      }

      #Route-Related File Field Descriptions

      In addition to basic route information, the report also displays related files found for each route:

      • data: Server-side data loading file (.data.ts), used to fetch data on the server
      • clientData: Client-side data loading file (.data.client.ts), used to refetch data on the client
      • error: Error boundary file (.error.tsx), used to handle route rendering errors
      • loading: Loading state component file (.loading.tsx), used to display data loading state
      • config: Route configuration file (.config.ts), used to configure route metadata

      These fields are optional and will only be displayed in the report when corresponding files are found. By viewing these fields, you can quickly understand the complete file structure for each route.