# Micro-frontends

Micro-frontends are a way to split your application into smaller, more manageable pieces. Each piece is a separate application that can be developed, tested, and deployed independently. This allows you to scale your development team and infrastructure more easily.

## Why micro-frontends?

Micro-frontends are a great way to scale your application development. They allow you to:

- **Scale your development team:** Each team can work on a separate micro-frontend, allowing them to develop, test, and deploy independently.
- **Scale your infrastructure:** Each micro-frontend can be deployed independently, allowing you to scale your infrastructure more easily.
- **Improve performance:** Micro-frontends can be loaded on demand, reducing the initial load time of your application.
- **Improve maintainability:** Each micro-frontend is a separate application, making it easier to maintain and update.

## How to implement micro-frontends

> **Warning:** this is a highly experimental feature! Use with caution.

You can use any apps built with `@lazarv/react-server` as micro-frontends. Any route in your app can be a micro-frontend. Still you need to implement your micro-frontend route to be able to work as an outlet. You can't use an `html` document. You just need to render an HTML fragment. The most simple way to do this is to render a paragraph for example:

```jsx
export default function MicroFrontend() {
  return (
    <p>
      This is a micro-frontend!
    </p>
  );
}
```

Just don't use `html`, `head` or `body` tags in your micro-frontend route as the hosting application can't render multiple of these tags.

> **Note:** React Server Components, client components and server functions are all supported in micro-frontends when using `@lazarv/react-server` with server-side rendering.

`@lazarv/react-server` provides a set of tools to help you implement micro-frontends in your application. You can use the `RemoteComponent` component to load a micro-frontend from a remote URL. This allows you to load a micro-frontend on demand and render it in your application using server-side rendering.

The `RemoteComponent` component takes a `src` prop that specifies the URL of the micro-frontend. By using this component, you can compose your application from multiple micro-frontends, each developed and deployed independently.

```jsx
import RemoteComponent from "@lazarv/react-server/remote";

export default function Home() {
  return (
    <div>
      <h1>Hosting application</h1>
      <RemoteComponent src="http://localhost:3001" />
    </div>
  );
}
```

### Static export

When you exported the source of the `RemoteComponent` as static at build time using the `remote` flag the component still works as expected. The `RemoteComponent` will be replaced with the static content of the micro-frontend. Read more about how to export static micro-frontend content in the [Static generation](/router/static#remote) section of the documentation.

You can also export your hosting application while using the `RemoteComponent` as static at build time. The `RemoteComponent` will be replaced with the static content of the micro-frontend. So both your micro-frontend application and your hosting application can be statically generated at build time to achieve the best performance when using static content in a micro-frontend architecture.

### Deferred

For streaming response to work from a micro-frontend, you need to pass `defer` as a prop to the `RemoteComponent`. The initial content will be rendered during server-side rendering, and the rest of the content will be streamed from the micro-frontend after the initial content is rendered and the `RemoteComponent` gets hydrated.

```jsx
import RemoteComponent from "@lazarv/react-server/remote";

export default function Home() {
  return (
    <div>
      <h1>Hosting application</h1>
      <RemoteComponent src="http://localhost:3001" defer />
    </div>
  );
}
```

### Passing props

Remote components support passing props to the remote micro-frontend application. You can pass props to the `RemoteComponent` just like you would with any other React component. The props will be passed to the micro-frontend and can be used in the micro-frontend's components. Passing props includes passing child components as well, so you can compose your micro-frontend with other components in your hosting application.

```jsx
import RemoteComponent from "@lazarv/react-server/remote";

export default function Home() {
  return (
    <div>
      <h1>Hosting application</h1>
      <RemoteComponent src="http://localhost:3001" message="Hello from the hosting application!">
        <p>This is a child component passed to the micro-frontend.</p>
      </RemoteComponent>
    </div>
  );
}
```

> **Note:** only serializable props can be passed to the micro-frontend application. This means that you can pass all props which could also be passed to React client components.

### Isolation

You can isolate the micro-frontend from the hosting application by using the `isolate` prop. This will create a shadow DOM for the micro-frontend, preventing styles and scripts from leaking into or out of the micro-frontend. This is useful when you want to ensure that the micro-frontend is completely isolated from the hosting application.

```jsx
import RemoteComponent from "@lazarv/react-server/remote";

export default function Home() {
  return (
    <div>
      <h1>Hosting application</h1>
      <RemoteComponent src="http://localhost:3001" isolate />
    </div>
  );
}
```

### React Server Components

It's also possible to use the `ReactServerComponent` component with the `url` and `defer` props to load a micro-frontend from a remote URL. This component is similar to the `RemoteComponent` component, but it only renders the micro-frontend on the client side. This is very similar to how Astro server islands work with the `server:defer` attribute. Learn how [outlets](/router/client-navigation#outlet) work in the router section of the documentation about client-side navigation.

```jsx
import { ReactServerComponent } from "@lazarv/react-server/navigation";

export default function Home() {
  return (
    <div>
      <h1>Hosting application</h1>
      <ReactServerComponent url="http://localhost:3001" defer />
    </div>
  );
}
```

## Sharing dependencies

When you use micro-frontends, you need to share dependencies between the hosting application and the micro-frontends. You can use import maps to specify the shared dependencies between the hosting application and the micro-frontends.

An import map is a JSON file that specifies the mapping between the import specifier and the URL of the module. You can use the `importMap` option in the `react-server.config.json` file to specify the import map for your application.

```json
{
  "importMap": {
    "imports": {
      // ...
    }
  }
}
```

When you specify the import map in the `react-server.config.json` file in each app, the hosting application and the micro-frontends will share the specified dependencies.

To externalize the shared dependencies, you can use the `resolve.shared` option in the `react-server.config.json` file. This option allows you to specify the shared dependencies that should be loaded from a remote URL.

```json
{
  "resolve": {
    "shared": [
      "my-shared-dependency",
      /another-shared-dependency/
    ]
  }
}
```

When you specify the shared dependencies in the `react-server.config.json` file, the hosting application and the micro-frontends will load the shared dependencies from the specified remote URL.

For `@lazarv/react-server` to work properly with import maps, you need to specify the source for `react`, `react-jsx/runtime` (or `react/jsx-dev-runtime`), `react-dom`, and `react-dom/client` in the import map.

If you want to use these dependencies from the CDN even during development, you can specify the source for these dependencies in the import map, but keep in mind that you need the development version of these dependencies.

> **Warning:** You need to use the exact same versions of `react`, `react/jsx-dev-runtime`, `react/jsx-runtime`, `react-dom`, and `react-dom/client` in the hosting application and the micro-frontends, both on the client and the server side. Otherwise, you may encounter compatibility issues.

To use an import map to work with both development and production environments, you need to create a configuration file for each environments, specifying the import maps. The configuration files below show how to use an import map that uses React from [esm.sh](https://esm.sh).

In development, we can use the hosting application to serve the client components:

```mjs filename="react-server.development.config.json"
export default {
  importMap: {
    imports: {
      react: "https://esm.sh/react@0.0.0-experimental-43bcbf80-20260603?dev",
      "react/jsx-dev-runtime":
        "https://esm.sh/react@0.0.0-experimental-43bcbf80-20260603/jsx-dev-runtime?dev",
      "react/jsx-runtime":
        "https://esm.sh/react@0.0.0-experimental-43bcbf80-20260603/jsx-runtime?dev",
      "react-dom":
        "https://esm.sh/react-dom@0.0.0-experimental-43bcbf80-20260603?dev",
      "react-dom/client":
        "https://esm.sh/react-dom@0.0.0-experimental-43bcbf80-20260603/client?dev"
    },
  },
};
```

In production, we only map `@lazarv/react-server` modules to share these with each micro-frontend:

```mjs filename="react-server.production.config.json"
export default {
  importMap: {
    imports: {
      react: "https://esm.sh/react@0.0.0-experimental-43bcbf80-20260603",
      "react/jsx-runtime":
        "https://esm.sh/react@0.0.0-experimental-43bcbf80-20260603/jsx-runtime",
      "react-dom":
        "https://esm.sh/react-dom@0.0.0-experimental-43bcbf80-20260603",
      "react-dom/client":
        "https://esm.sh/react-dom@0.0.0-experimental-43bcbf80-20260603/client"
    },
  },
};
```

Beside the configuration files above, we still specify static exports in the `react-server.config.json` file:

```mjs filename="react-server.config.json"
export default {
  export() {
    return [
      {
        path: "/",
        remote: true,
      },
    ];
  },
};
```

## Example

For a fully working example of micro-frontends, check out the [micro-frontends example](https://github.com/lazarv/react-server/tree/main/examples/remote) in the `@lazarv/react-server` repository.

To run the example, clone the `@lazarv/react-server` repository and run the following commands in the root directory:

```sh
pnpm install
pnpm --filter ./examples/remote dev
```

## Disabling Remote Components rendering

Applications that don't host Remote Components can force-disable the entire rendering path by setting `remoteComponents: false` in the runtime config. The runtime then ignores the `@__react_server_remote__` URL marker, never enters the Remote Components code path, and never parses request bodies as Remote Components payloads.

```js
// react-server.config.mjs
export default {
  remoteComponents: false,
};
```

Two effects worth understanding:

The remote-rendering routing is removed. URLs carrying the `@__react_server_remote__` marker are no longer treated as Remote Components fetches; they fall through to normal page rendering as if the marker weren't there.

Temporary references — the runtime mechanism that lets non-serializable client values (callbacks, DOM refs, opaque thenables) round-trip through `$T` tags on the wire — are gated by the same flag. Their only legitimate use is round-tripping values back to the same client during a Remote Components render, so when `remoteComponents: false` is set, the temporary-reference set is not created and any `$T` tag in an incoming request body is rejected as malformed. This eliminates a small but real attack surface: a fabricated body otherwise allocates proxy objects and runs opaque lookups against the in-memory reference map.

This pairs with `serverFunctions: false` (see [Disabling Server Functions entirely](../guide/server-functions#disabling-server-functions)) for apps that want to ship a strictly read-only surface — neither Server Functions nor Remote Components remain reachable, and any `POST`/`PUT`/`PATCH`/`DELETE` traffic flows through to normal rendering without parsing the request body.