Page Entry

In this chapter, you will learn about the entry convention in Modern.js and how to customize it.

What is Entry?

Entry refers to the starting module of a page.

In a Modern.js project, each entry corresponds to an independent page and a server-side route. By default, Modern.js automatically determines the entry of a page based on directory conventions, but also supports customizing the entry through configuration options.

Many configuration options provided by Modern.js are divided by entry, such as page title, HTML template, page meta information, whether to enable SSR/SSG, server-side routing rules, etc.

Single Entry and Multiple Entries

The project initialized by Modern.js is a single entry (SPA) project, with the following structure:

. ├── src │ └── routes │ ├── index.css │ ├── layout.tsx │ └── page.tsx ├── package.json ├── modern.config.ts └── tsconfig.json

In a Modern.js project, you can easily switch from single entry to multiple entries by running pnpm run new in the project directory and creating an entry:

? Please select the operation you want: Create Element
? Please select the type of element to create: New "entry"
? Please fill in the entry name: new-entry

After running the command, Modern.js will automatically generate a new entry directory. At this point, you can see that the src/ directory has the following structure:

.
├── myapp     # Original entry
│   └── routes
│       ├── index.css
│       ├── layout.tsx
│       └── page.tsx
└── new-entry  # New entry
    └── routes
        ├── index.css
        ├── layout.tsx
        └── page.tsx

The original entry code has been moved to a directory with the same name as the name field in package.json, and a new-entry entry directory has been created.

You can run pnpm run dev to start the development server. At this point, you will see a new route named /new-entry added, and the existing page routes remain unchanged.

TIP

Modern.js will use the entry with the same name as the name field in package.json as the main entry. The route of the main entry is /, and the route of other entries is /{entryName}.

For example, when the name field in package.json is myapp, src/myapp will be the main entry of the project.

Entry Types

Different entry types have different compilation and runtime behaviors.

By default, Modern.js scans the files under src/ before starting the project, identifies the entry, and generates the corresponding server-side route.

TIP
  • You can custom the recognition directory for page entries by using source.entriesDir.
  • If you need to customize the entry points, please refer to Custom Entries.

Not all top-level directories under src/ become project entries. The directory where the entry is located must meet one of the following four conditions:

  1. Has a routes/ directory.
  2. Has an App.[jt]sx? file.
  3. Has an index.[jt]sx? file.
  4. Has a pages/ directory (compatible with Modern.js 1.0).

When the src/ directory meets the entry requirements, Modern.js considers the current project as a single entry application.

TIP

In a single entry application, the default entry name is main.

When the project is not a single entry application, Modern.js will further look at the top-level directories under src/.

Framework Mode Entry

The framework mode refers to the need to use the framework capabilities of Modern.js, such as nested routing, SSR, and integrated BFF, etc. Under this kind of entry convention, the entry defined by the developer is not the actual compilation entry. When Modern.js is launched, it generates a wrapped entry, and the real entry can be found at node_modules/.modern/[entryName]/index.js.

Conventional Routing

If there is a routes/ directory in the entry, Modern.js will scan the files under routes/ during startup, and automatically generate client-side routes (react-router) based on file conventions. For example:

.
├── src
│   └── routes
│       ├── layout.tsx
│       └── page.tsx

In the above directory, the component exported in layout.tsx will be the outermost component, and the component exported in page.tsx will be the component of the / route.

For more information, please refer to Conventional Routing.

Self-controlled Routing

If there is an App.[jt]sx? file in the entry, developers can set the client-side route in this file through code, or not set the client-side route.

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>
  );
};

For more information, please refer to Self-controlled Routing.

Custom Bootstrap

If there is an index.[jt]sx file in the entry, and the file exports a function by default, Modern.js will pass the default bootstrap function as a parameter and use the exported function to replace the default bootstrap. This way, developers can customize how components are mounted to DOM nodes or add custom behavior before mounting. For example:

export default (App: React.ComponentType, bootstrap: () => void) => {
  // do something before bootstrap...
  initSomething().then(() => {
    bootstrap();
  });
};

At this point, the generated file content of Modern.js is as follows:

import React from 'react';
import ReactDOM from 'react-dom/client';
import customBootstrap from '@_modern_js_src/index.tsx';
import App from '@_modern_js_src/App';
import { router, state } from '@modern-js/runtime/plugins';

const IS_BROWSER = typeof window !== 'undefined' && window.name !== 'nodejs';
const MOUNT_ID = 'root';

let AppWrapper = null;

function render() {
  AppWrapper = createApp({
    // plugin parameters for runtime...
  })(App);
  if (IS_BROWSER) {
    customBootstrap(AppWrapper);
  }
  return AppWrapper;
}

AppWrapper = render();

export default AppWrapper;

Build Mode Entry

Build mode refers to the mode where the entry point of the page is not automatically generated by Modern.js, but is fully defined by the developers themselves.

When there is an index.[jt]sx file in the entry directory and it is not exported as a function using export default, this type of file will be recognized as the entry module for webpack or Rspack.

In this case, Modern.js will not generate the entry code automatically. Therefore, you need to manually mount the component to the DOM node, for example:

src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

This approach is equivalent to enabling the source.entries.disableMount option in Modern.js. When you use this approach, you will not be able to use the runtime capabilities of the Modern.js framework, such as the runtime configuration in the modern.config.js file will no longer take effect.

Custom Entries

In some cases, you may need to customize the entry configuration instead of using the entry conventions provided by Modern.js.

For example, if you want to migrate a non-Modern.js project to Modern.js and it is not structured according to Modern.js directory structure, there might be some migration costs involved in changing it to the conventional structure. In such cases, you can custom the entries.

Modern.js provides the following configuration options that you can set in modern.config.ts:

  • source.entries: Used to set custom entry objects.
  • source.disableDefaultEntries: Used to disable Modern.js's default entry scanning behavior. When you use custom entries, parts of your project structure might coincidentally match the Modern.js conventional directory structure, but you may not want Modern.js to generate entry configurations for them. Enabling this option can help avoid this issue.

Example

Here is an example of a custom entry point. You can also refer to the documentation of the corresponding configuration options for more usage.

modern.config.ts
export default defineConfig({
  source: {
    entries: {
      // Specify an entry named 'my-entry'
      'my-entry': {
        // Path to the entry module
        entry: './src/my-page/index.tsx',
        // Disable automatic generation of entry code by Modern.js
        disableMount: true,
      },
    },
    // Disable entry scanning behavior
    disableDefaultEntries: true,
  },
});

Note that when you enable disableMount, you won't be able to use the runtime capabilities of the Modern.js framework, such as the runtime configuration in the modern.config.ts file.