---
sidebar_position: 3
title: Develop Main App
---

# Develop Main App

In the previous [Experience micro frontend](/guides/topic-detail/micro-frontend/c02-development), an example was used to demonstrate how to create and configure micro frontend sub-applications. Through this chapter, you can further understand how to develop the main application, and its common configuration.

After creating an Modern.js project through the `@modern-js/create` command, you can execute `pnpm run new` in the project (the `modern new` command is actually executed). After selecting the 「micro frontend」 mode, the micro frontend will be installed. Dependencies, just register the plugin manually.

import EnableMicroFrontend from '@site-docs-en/components/enable-micro-frontend';

<EnableMicroFrontend />

## Register Sub-app

When the information is provided on the `masterApp` configuration, the application will be considered the main application. At present, there are two configuration methods for sub-app information, and these two methods are applied to different scenarios.

### Register sub-app info directly

You can register the sub-application information directly through the configuration:

:::tip
It can be configured at runtime via the API [defineConfig](/apis/app/runtime/app/define-config).

When the parameter is a function, it cannot be serialized to the output code, so please configure it through defineConfig when it comes to configuration such as functions
:::

import MicroRuntimeConfig from '@site-docs-en/components/micro-runtime-config';

<MicroRuntimeConfig />

### Custom remote app list

This function allows you to pull a list of remote child applications and register them with the runtime framework:

```ts title="App.tsx"
defineConfig(App, {
  masterApp: {
    manifest: {
      getAppList: async () => {
        // get from remote api
        return [
          {
            name: 'Table',
            entry: 'http://localhost:8081',
            // activeWhen: '/table'
          },
          {
            name: 'Dashboard',
            entry: 'http://localhost:8082',
            // activeWhen: '/dashboard'
          },
        ];
      },
    },
  },
});
```

## Renderer sub-app

There are two ways to load sub-app in micro frontend:

1. **Sub-app component ** Get the components of each sub-app, and then you can render the sub-app of micro frontend just like using ordinary'React 'components.
2. **Centralized routing** Through centralized routing configuration, the corresponding sub-app of rendering is automatically activated according to the current page `pathname`.

### Sub-app component

Developers can use the `useModuleApps` method to obtain the components of each child application.

Through the combined use of the `router` component, developers can autonomously render different sub-applications according to different routes.

Suppose our subapp list is configured as follows:

<EnableMicroFrontend />

`App.tsx` as follow:

```js title="App.tsx"
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';

import {
  RouterProvider,
  Route,
  createBrowserRouter,
  createRoutesFromElements,
  BrowserRouter,
  Link,
  Outlet,
} from '@modern-js/runtime/router';

const AppLayout = () => (
  <>
    <div>
      <Link to={'/table'}>load file-based sub-app</Link>
    </div>
    <div>
      <Link to={'/dashboard'}>load self-controlled sub-app</Link>
    </div>
    <div>
      <Link to={'/'}>unmount sub-app</Link>
    </div>
    <Outlet />
  </>
);

export default () => {
  const { apps, MApp } = useModuleApps();

  // Instead of using the MApp component, you need to use createBrowserRouter to create the route
  const router = createBrowserRouter(
    createRoutesFromElements(
      <Route path="/" element={<AppLayout />}>
        {apps.map(app => {
          const { Component } = app;
          // Fuzzy match, path needs to be written in a pattern similar to abc/*
          return (
            <Route
              key={app.name}
              path={`${app.name.toLowerCase()}/*`}
              element={
                <Component
                  loadable={{
                    loading: ({ pastDelay, error }: any) => {
                      if (error) {
                        return <div>error: {error?.message}</div>;
                      } else if (pastDelay) {
                        return <div>loading</div>;
                      } else {
                        return null;
                      }
                    },
                  }}
                />
              }
            />
          );
        })}
      </Route>,
    ),
  );

  return (
    // Use MApp to automatically load sub-applications according to the configured activeWhen parameters (this project is configured in modern.config.ts)
    // <BrowserRouter>
    //   <MApp />
    // </BrowserRouter>

    // Manually write the Route component to load the sub-application, which is convenient for scenarios that require pre-operation such as authentication
    <>
      <RouterProvider router={router} />
    </>
  );
};
```

Here, the activation route of **Table** is customized as **/table** through the `Route` component, and the activation route of **Dashboard** is **/dashboard**.

### Centralized routing

**Centralized Routing** is a way to centrally configure active routes for subapps. We enable **Centralized Routing** by adding an `activeWhen` field to the subapp list information.

<MicroRuntimeConfig />

Use `useModuleApp` api in Main App, get `MApp` component and then render it

```tsx title=Main App: App.tsx
import { useModuleApp } from '@modern-js/plugin-garfish/runtime';

function App() {
  const { MApp } = useModuleApps();

  return (
    <div>
      <MApp />
    </div>
  );
}
```

After starting the application in this way, accessing the '/table' route will render the'Table 'sub-application, and accessing the'/dashboard 'route will render the'Dashboard' sub-application.

### Mix Mode

Of course, **sub-application components** and **centralized routing** can be mixed.

- One molecular application is activated as a **sub-application component**, and the other part is activated as a **centralized routing**.
- A molecular application can be activated either as a **centralized routing** or as a **sub-application component**.

### Loading

By configuring the `loadable` configuration, loading components can be added for 「centralized routing」 and 「sub-applicati」, and errors, timeouts, flashes, etc. can be considered, so as to provide users with a better user experience. The design and implementation of this function refer to [react-loadable](https://github.com/jamiebuilds/react-loadable), and the basic functions are similar.

```tsx
function Loading() {
  return <div>Loading...</div>;
}

function App(){
  return <>
    <Table
      loadable={{
        loading: Loading,
      }}
    />
  <>
}
```

#### Add Error Status

When the micro-frontend sub-application fails to load or render, the `loading component` will receive the `error` parameter (if there is no error, the error is null).

```tsx
function Loading({ error }) {
  if (error) {
    return <div>Error msg {error?.message}</div>;
  } else {
    return <div>Loading...</div>;
  }
}
```

#### Avoid Loading Flash Back

Sometimes the display time of the loading component may be less than 200ms, and the loading component will flash back at this time. Many user studies have proved that the loading flash back situation will cause the user to perceive that the loading content takes longer than the actual time.

When loading is less than 200ms, if the content is not displayed, the user will think it is faster.

Therefore, the loading component also provides the `pastDelay` parameter, which will only be true when it exceeds the set delay display. You can set the delay duration through the `delay` parameter.

```tsx
function Loading({ error, pastDelay }) {
  if (error) {
    return <div>Error! {error?.message}</div>;
  } else if (pastDelay) {
    return <div>Loading...</div>;
  } else {
    return null;
  }
}
```

The default value of `delay` is `200ms`, you can set the delay display time through `delay` in `loadable`.

```tsx

function App(){
  return <>
    <Table
      loadable={{
        loading: Loading,
        delay: 300 // 0.3 seconds
      }}
    />
  <>
}
```

#### Add Timeout State

Sometimes because of the network, the micro-front-end sub-application fails to load, resulting in the loading state being displayed all the time, which is very bad for users, because they don't know the right response to get a specific response, whether they need to refresh the page, by Increasing the timeout state can solve this problem well.

The loading component will get the `timeOut` parameter when timeout, when the micro frontend application loads timeout, it will get the `timeOut` property value of true.

```tsx
function Loading({ error, timeOut, pastDelay }) {
  if (error) {
    return <div>Error! {error?.message}</div>;
  } else if (timeOut) {
    return <div>Loading timed out, please refresh the page... </div>;
  } else if (pastDelay) {
    return <div>Loading...</div>;
  } else {
    return null;
  }
}
```

The timeout state is off and can be enabled by setting the `timeout` parameter in `loadable`:

```tsx

function App(){
  return <>
    <Table
      loadable={{
        loading: Loading,
        timeOut: 10000 // 10s
      }}
    />
  <>
}
```
