External Bundle

3.5
Info

Loading CSS files from an External Bundle requires Lynx 3.7 or later.

External Bundle lets you build a Lynx bundle once and load it from multiple Lynx apps at runtime. Use it when you want to share ReactLynx components or common business bundles across apps.

Compared with Chunk Splitting, External Bundle is for cross-application reuse, while Chunk Splitting is for splitting code inside one app.

This workflow has two parts:

  1. Use @lynx-js/lynx-bundle-rslib-config to build the external bundle.
  2. Use @lynx-js/external-bundle-rsbuild-plugin to load it in the host app.

Install

npm
yarn
pnpm
bun
deno
npm install @lynx-js/lynx-bundle-rslib-config @lynx-js/external-bundle-rsbuild-plugin @lynx-js/react-umd @rslib/core -D

Build An External Bundle

For a ReactLynx component bundle, the recommended setup is:

rslib-comp-lib.config.ts
import { defineExternalBundleRslibConfig } from '@lynx-js/lynx-bundle-rslib-config';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';

export default defineExternalBundleRslibConfig({
  id: 'comp-lib',
  source: {
    entry: {
      './App.js': './external-bundle/CompLib.tsx',
    },
  },
  plugins: [pluginReactLynx()],
  output: {
    externalsPresets: {
      reactlynx: true,
    },
  },
});

externalsPresets.reactlynx maps the standard ReactLynx requests for you, so a ReactLynx component bundle can depend on the shared runtime directly.

Run the build with:

npm
yarn
pnpm
bun
deno
npm exec rslib build --config rslib-comp-lib.config.ts

Load It In The Host App

Use the same request key in the app:

lynx.config.ts
import { pluginExternalBundle } from '@lynx-js/external-bundle-rsbuild-plugin';
import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
import { defineConfig } from '@lynx-js/rspeedy';

export default defineConfig({
  plugins: [
    pluginReactLynx(),
    pluginQRCode(),
    pluginExternalBundle({
      externalsPresets: {
        reactlynx: true,
      },
      externals: {
        './App.js': 'comp-lib.lynx.bundle',
      },
    }),
  ],
});

This shorthand is equivalent to the following expanded config:

externals: {
  './App.js': {
    bundlePath: 'comp-lib.lynx.bundle',
    libraryName: './App.js',
    background: { sectionPath: './App.js' },
    mainThread: { sectionPath: './App.js__main-thread' },
    async: true,
  },
}

If your section names already match the request key, this is the smallest recommended setup.

When To Set globalObject

In most projects, you can omit globalObject.

Set globalObject: 'globalThis' only when your app enables a shared globalThis across background-thread runtimes and you want later bundles to reuse externals that have already been loaded there.

Use it on both sides when needed:

rslib-comp-lib.config.ts
output: {
  externalsPresets: {
    reactlynx: true,
  },
  globalObject: 'globalThis',
}
lynx.config.ts
pluginExternalBundle({
  externalsPresets: {
    reactlynx: true,
  },
  externals: {
    './App.js': 'comp-lib.lynx.bundle',
  },
  globalObject: 'globalThis',
});

Why @lynx-js/react-umd Is Needed

@lynx-js/react-umd ships the prebuilt ReactLynx runtime bundles used by the built-in reactlynx preset.

When you enable externalsPresets.reactlynx in pluginExternalBundle(), the plugin will automatically resolve @lynx-js/react-umd/dev or @lynx-js/react-umd/prod based on NODE_ENV, emit react.lynx.bundle, and load it through the current runtime public path.

For most projects, this means:

  • install @lynx-js/react-umd
  • enable reactlynx
  • let the preset provide the ReactLynx runtime mapping

Extend Presets

Use the same preset name on both sides so the bundle build and the host app stay aligned.

From The Library Side

On the external bundle build side, presets live in output.externalsPresets and output.externalsPresetDefinitions:

rslib-comp-lib.config.ts
import { defineExternalBundleRslibConfig } from '@lynx-js/lynx-bundle-rslib-config';

export default defineExternalBundleRslibConfig({
  output: {
    externalsPresets: {
      reactlynxPlus: true,
    },
    externalsPresetDefinitions: {
      reactlynxPlus: {
        extends: 'reactlynx',
        externals: {
          '@lynx-js/lynx-ui': ['LynxUI', 'UI'],
        },
      },
    },
  },
});

This side decides how imports are mapped to external globals when the bundle is built.

From The Lynx Page Side

On the host app side, presets live in pluginExternalBundle({ externalsPresets, externalsPresetDefinitions }):

lynx.config.ts
import { pluginExternalBundle } from '@lynx-js/external-bundle-rsbuild-plugin';

pluginExternalBundle({
  externalsPresets: {
    reactlynxPlus: true,
  },
  externalsPresetDefinitions: {
    reactlynxPlus: {
      extends: 'reactlynx',
      resolveExternals() {
        return {
          '@lynx-js/lynx-ui': {
            libraryName: ['LynxUI', 'UI'],
            bundlePath: 'lynx-ui.lynx.bundle',
            background: { sectionPath: 'LynxUI' },
            mainThread: { sectionPath: 'LynxUI__main-thread' },
            async: false,
          },
        };
      },
    },
  },
});

This side decides where the bundle is loaded from and which section names the runtime should request.

Explicit externals still override mappings provided by presets. Use that when a single app needs a different bundle path or section name.

When To Use The Object Form

Use the full object form when you need custom section names, custom exported library names, timeouts, or a dedicated local bundle directory.

The external-bundle example in lynx-examples uses that style:

lynx.config.mjs
pluginExternalBundle({
  externalBundleRoot: 'dist-external-bundle',
  externalsPresets: {
    reactlynx: true,
  },
  externals: {
    'lodash-es': {
      bundlePath: 'lodash-es.lynx.bundle',
      background: { sectionPath: 'lodash-es' },
      mainThread: { sectionPath: 'lodash-es__main-thread' },
      async: false,
      timeout: 10000,
    },
    './components': {
      bundlePath: 'comp.lynx.bundle',
      background: { sectionPath: 'component' },
      mainThread: { sectionPath: 'component__main-thread' },
      async: true,
      timeout: 5000,
    },
  },
  timeout: 10000,
});

Use this form when:

  • one bundle contains multiple business modules
  • section names do not match the request key
  • bundles are built into dist-external-bundle
  • you want per-bundle timeout or loading behavior

Example

The lynx-examples external-bundle example is available directly in the docs:

API Reference

Except as otherwise noted, this work is licensed under a Creative Commons Attribution 4.0 International License, and code samples are licensed under the Apache License 2.0.