Code Splitting

Rspack supports code splitting, which allows splitting the code into other chunks. You have the full control about size and number of generated assets, which allow you to gain performance improvements in loading time.

Rspack - Code Splitting

Lazy-loading components

Usually, you import components with the static import declaration:

import LazyComponent from './LazyComponent.jsx';

export function App() {
  return (
    <view>
      <LazyComponent />
    </view>
  );
}

To defer loading this component’s code until it’s rendered for the first time, replace this import with:

- import LazyComponent from './LazyComponent.jsx'
+ import { lazy } from '@lynx-js/react'
+ const LazyComponent = lazy(() => import('./LazyComponent.jsx'))

This code relies on dynamic import(), which is supported by Rspack. Using this pattern requires that the lazy component you are importing was exported as the default export.

Now that your component’s code loads on demand, you also need to specify what should be displayed while it is loading. You can do this by wrapping the lazy component or any of its parents into a <Suspense> boundary:

INFO

The split components will only start downloading when they are rendered.

src/App.tsx
import { Suspense, lazy } from '@lynx-js/react';

const LazyComponent = lazy(() => import('./LazyComponent.jsx'));

export function App() {
  return (
    <view>
      <Suspense fallback={<text>Loading...</text>}>
        <LazyComponent />
      </Suspense>
    </view>
  );
}

Load lazy component when needed

In this example, the code for LazyComponent won’t be loaded until you attempt to render it. If LazyComponent hasn’t loaded yet, a "Loading..." will be shown in its place. For example:

src/App.tsx
import { Suspense, lazy, useState } from '@lynx-js/react';

const LazyComponent = lazy(() => import('./LazyComponent.jsx'));

export function App() {
  const [shouldDisplay, setShouldDisplay] = useState(false);
  const handleClick = () => {
    setShouldDisplay(true);
  };
  return (
    <view>
      <view bindtap={handleClick}>Load Component</view>
      {shouldShow && (
        <Suspense fallback={<text>Loading...</text>}>
          <LazyComponent />
        </Suspense>
      )}
    </view>
  );
}

Error handling

Use ErrorBoundary

If loading is completed, lazy-loaded components are essentially also a React component, so the error handling practices in React are still applicable.

Checkout React - Catching rendering errors with an error boundary for details.

Lazy-loading standalone project

You may also lazy-load modules that being built in a standalone Rspeedy project.

Glossary of Terms

  • Producer (Remote): An application that exposes modules to be consumed by other Lynx applications.
  • Consumer (Host): An application that consumes modules from other Producers.

Create a standalone Producer project

Create a standalone project using create-rspeedy:

pnpm create rspeedy@latest

Then add experimental_isLazyBundle to the options of pluginReactLynx in the lynx.config.js:

import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
import { defineConfig } from '@lynx-js/rspeedy';

export default defineConfig({
  source: {
    entry: './src/index.tsx',
  },
  plugins: [
    pluginReactLynx({
      experimental_isLazyBundle: true,
    }),
  ],
});

Finally, change the index.tsx to export the App.

src/index.tsx
import { App } from './App.jsx';

export default App;

Modify the Consumer project

To load the Producer project, add an import to @lynx-js/react/experimental/lazy/import at the beginning of the entry.

src/index.tsx
import '@lynx-js/react/experimental/lazy/import';
import { root } from '@lynx-js/react';

import { App } from './App.jsx';

root.render(<App />);

This would provide essential APIs that the Producer needs.

Then, the Producer could be loaded using dynamic import().

src/App.tsx
import { Suspense, lazy } from '@lynx-js/react';

const LazyComponent = lazy(
  () =>
    import('https://<host>:<port>/path/to/lynx.bundle', {
      with: { type: 'component' },
    }),
);

export function App() {
  return (
    <view>
      <Suspense fallback={<text>Loading...</text>}>
        <LazyComponent />
      </Suspense>
    </view>
  );
}

Developing Producer project

It is recommended to create a separated Consumer in the Producer project.

src/Consumer.tsx
import { Suspense, lazy, root } from '@lynx-js/react';

// You may use static import if you want
const App = lazy(() => import('./App.jsx'));

root.render(
  <Suspense>
    <App />
  </Suspense>,
);

Then, create a separated lynx.config.consumer.js:

lynx.config.consumer.js
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
import { defineConfig } from '@lynx-js/rspeedy';

export default defineConfig({
  source: {
    entry: './src/Consumer.tsx',
  },
  plugins: [pluginReactLynx()],
});

Use npx rspeedy dev --config lynx.config.consumer.js to start developing the producer project.

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.