> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Entry Changes

This chapter introduces changes related to page entries when upgrading from Modern.js 2.0 to 3.0.

## Overview

Modern.js 3.0 has optimized and simplified the entry mechanism. The main changes include:

- **Entry File Naming Change**: Custom entry files changed from `index.tsx` to `entry.tsx`
- **Bootstrap Function Replacement**: Use the new `createRoot` and `render` APIs
- **Runtime Configuration Migration**: `App.config` and `config` exports from `routes/layout` need to be migrated
- **Initialization Logic Migration**: `App.init` and `init` exports from `routes/layout` need to be changed to runtime plugins

## Entry Type Identification

Before starting migration, first identify the entry type used in your project.

### Entry Identification Conditions

Modern.js scans directories and identifies entries that meet **any** of the following conditions:

1. **Has a `routes/` directory** → Convention-based routing entry
2. **Has an `App.tsx?` file** → Self-controlled routing entry
3. **Has an `index.tsx?` file (2.0) or `entry.tsx?` file (3.0)** → Custom entry

### Single Entry vs Multiple Entries

**Single Entry Application**: Scans the `src/` directory by default

```bash
src/
├── routes/        # or
├── App.tsx        # or
└── index.tsx      # 2.0 version
```

**Multiple Entry Application**: Scans first-level subdirectories under `src/`

```bash
src/
├── entry1/
│   └── routes/    # Each subdirectory is an entry
└── entry2/
    └── App.tsx
```

:::tip
You can modify the entry scanning directory through the [source.entriesDir](/configure/app/source/entries-dir.md) configuration.
:::

## Migration Steps

The migration operations in this section only need to be performed when the corresponding usage actually exists in the project, such as `bootstrap` function, `App.config/App.init`, `config/init` functions in `routes/layout.tsx`, etc.

### 1. Custom Entry File Rename

If your project uses a custom entry file (`index.tsx`), you need to rename it to `entry.tsx`.

**2.0 Version:**

```bash
src/
└── index.tsx
```

**3.0 Version:**

```bash
src/
└── entry.tsx
```

### 2. Bootstrap Function Migration

If your entry file exports a function that receives `App` and `bootstrap` parameters, you need to use the new API instead.

**2.0 Version:**

```tsx title="src/index.tsx"
export default (App: React.ComponentType, bootstrap: () => void) => {
  // Perform initialization operations
  initSomething().then(() => {
    bootstrap();
  });
};
```

**3.0 Version:**

```tsx title="src/entry.tsx"
import { createRoot } from '@modern-js/runtime/react';
import { render } from '@modern-js/runtime/browser';

// Create root component
const ModernRoot = createRoot();

// Perform initialization operations
async function beforeRender() {
  await initSomething();
}

// Render application
beforeRender().then(() => {
  render(<ModernRoot />);
});
```

:::info Note

- The component returned by `createRoot()` corresponds to the component generated by the `routes/` directory or exported by `App.tsx`
- The `render()` function is used to handle rendering and mounting components

:::

### 3. App.config Migration

If you defined `App.config` in `App.tsx`, you need to migrate it to the runtime configuration file.

**2.0 Version:**

```tsx title="src/App.tsx"
const App = () => {
  return <div>Hello</div>;
};

App.config = {
  router: {
    supportHtml5History: true,
  },
};

export default App;
```

**3.0 Version:**

Create `modern.runtime.ts` in the same directory as the entry:

```ts title="src/modern.runtime.ts"
import { defineRuntimeConfig } from '@modern-js/runtime';

export default defineRuntimeConfig({
  router: {
    supportHtml5History: true,
  },
});
```

:::warning Note
Modern.js 3.0 no longer supports configuring runtime in `modern.config.ts`, you must use the `modern.runtime.ts` file.
:::

### 4. App.init Migration

If you defined `App.init` in `App.tsx`, you need to change it to a runtime plugin.

**2.0 Version:**

```tsx title="src/App.tsx"
const App = () => {
  return <div>Hello</div>;
};

App.init = context => {
  context.store = createStore();
  context.request = (url: string) => fetch(url);
};

export default App;
```

**3.0 Version:**

```ts title="src/modern.runtime.ts"
import type { RuntimePlugin } from '@modern-js/runtime';
import { defineRuntimeConfig } from '@modern-js/runtime';

const initPlugin = (): RuntimePlugin => ({
  name: 'init-plugin',
  setup: api => {
    return {
      init({ context }) {
        context.store = createStore();
        context.request = (url: string) => fetch(url);
      },
    };
  },
});

export default defineRuntimeConfig({
  plugins: [initPlugin()],
});
```

### 5. config Export Migration from routes/layout.tsx

If you exported a `config` function in `routes/layout.tsx`, you need to migrate it to the runtime configuration file.

**2.0 Version:**

```tsx title="src/routes/layout.tsx"
export const config = () => {
  return {
    router: {
      supportHtml5History: true,
    },
  };
};

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

**3.0 Version:**

```tsx title="src/routes/layout.tsx"
export default function Layout() {
  return <Outlet />;
}
```

```ts title="src/modern.runtime.ts"
import { defineRuntimeConfig } from '@modern-js/runtime';

export default defineRuntimeConfig({
  router: {
    supportHtml5History: true,
  },
});
```

### 6. init Export Migration from routes/layout.tsx

If you exported an `init` function in `routes/layout.tsx`, you need to change it to a runtime plugin.

**2.0 Version:**

```tsx title="src/routes/layout.tsx"
export const init = context => {
  context.request = (url: string) => fetch(url);
};

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

**3.0 Version:**

```tsx title="src/routes/layout.tsx"
export default function Layout() {
  return <Outlet />;
}
```

```ts title="src/modern.runtime.ts"
import type { RuntimePlugin } from '@modern-js/runtime';
import { defineRuntimeConfig } from '@modern-js/runtime';

const initPlugin = (): RuntimePlugin => ({
  name: 'init-plugin',
  setup: api => {
    return {
      init({ context }) {
        context.request = (url: string) => fetch(url);
      },
    };
  },
});

export default defineRuntimeConfig({
  plugins: [initPlugin()],
});
```

## Multi-Entry Application Migration Notes

For multi-entry applications, you need to use function-form configuration in `src/modern.runtime.ts`, returning different runtime configurations based on entry names.

### Configuration Method

**Directory Structure:**

```bash
src/
├── modern.runtime.ts  # Unified runtime configuration file
├── entry1/
│   └── routes/
└── entry2/
    └── App.tsx
```

**Configuration Example:**

```ts title="src/modern.runtime.ts"
import { defineRuntimeConfig } from '@modern-js/runtime';

export default defineRuntimeConfig(entryName => {
  // Common configuration
  const commonConfig = {
    plugins: [commonPlugin()],
  };

  // Return specific configuration based on entry name
  if (entryName === 'entry1') {
    return {
      ...commonConfig,
      router: {
        supportHtml5History: true,
      },
      plugins: [...commonConfig.plugins, entry1Plugin()],
    };
  }

  if (entryName === 'entry2') {
    return {
      ...commonConfig,
      router: {
        supportHtml5History: false,
      },
      plugins: [...commonConfig.plugins, entry2Plugin()],
    };
  }

  // Default configuration
  return commonConfig;
});
```

:::info Note

- The `entryName` parameter corresponds to the entry directory name
- Main entry (same name as `name` in `package.json`): the directory name is passed in
- Other entries: the entry directory name is passed in

:::

### Migration Notes

1. **Merge configurations for the same entry**: If both `App.config/App.init` and `config/init` from `routes/layout.tsx` exist for the same entry, you need to merge them into the corresponding entry configuration in the `src/modern.runtime.ts` file

2. **Multiple plugins in parallel**: Multiple runtime plugins can be configured in parallel in the `plugins` array

3. **Clean up old code**: After migration is complete, remember to delete from the original files:
   - `App.config` property
   - `App.init` method
   - `config` export from `routes/layout.tsx`
   - `init` export from `routes/layout.tsx`

### Migration Example

Assume you have a 2.0 version multi-entry application:

**2.0 Version Directory Structure:**

```bash
src/
├── main/
│   ├── routes/
│   │   └── layout.tsx  # Contains config and init
│   └── App.tsx         # Contains App.config and App.init
└── admin/
    └── routes/
        └── layout.tsx  # Contains config and init
```

**2.0 Version Configuration:**

```tsx title="src/main/App.tsx"
const App = () => <div>Main App</div>;

App.config = {
  router: { supportHtml5History: true },
};

App.init = context => {
  context.mainData = 'main';
};
```

```tsx title="src/admin/routes/layout.tsx"
export const config = () => ({
  router: { supportHtml5History: false },
});

export const init = context => {
  context.adminData = 'admin';
};
```

**3.0 Version After Migration:**

```bash
src/
├── modern.runtime.ts  # New unified configuration file
├── main/
│   ├── routes/
│   │   └── layout.tsx  # Removed config and init
│   └── App.tsx         # Removed App.config and App.init
└── admin/
    └── routes/
        └── layout.tsx  # Removed config and init
```

```ts title="src/modern.runtime.ts"
import { defineRuntimeConfig } from '@modern-js/runtime';
import type { RuntimePlugin } from '@modern-js/runtime';

// Main entry initialization plugin
const mainInitPlugin = (): RuntimePlugin => ({
  name: 'main-init-plugin',
  setup: api => {
    return {
      init({ context }) {
        context.mainData = 'main';
      },
    };
  },
});

// Admin entry initialization plugin
const adminInitPlugin = (): RuntimePlugin => ({
  name: 'admin-init-plugin',
  setup: api => {
    return {
      init({ context }) {
        context.adminData = 'admin';
      },
    };
  },
});

export default defineRuntimeConfig(entryName => {
  if (entryName === 'main') {
    return {
      router: {
        supportHtml5History: true,
      },
      plugins: [mainInitPlugin()],
    };
  }

  if (entryName === 'admin') {
    return {
      router: {
        supportHtml5History: false,
      },
      plugins: [adminInitPlugin()],
    };
  }

  return {};
});
```

## Related Links

- [Page Entries](/guides/concept/entries.md)
- [Runtime Configuration](/configure/app/runtime/0-intro.md)
- [Runtime Plugins](/plugin/introduction.md#runtime-插件)
- [source.entries Configuration](/configure/app/source/entries.md)
- [source.entriesDir Configuration](/configure/app/source/entries-dir.md)
