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

# ReactLynx

In this document, you will learn how to build a [ReactLynx](https://lynxjs.org/react/introduction) component library for Lynx applications with Rslib. You can check out ReactLynx related example projects in [Examples](https://github.com/rstackjs/rstack-examples/tree/main/rslib/reactlynx-basic).

## Create a ReactLynx project

You can use [`@lynx-js/create-lynx`](https://www.npmjs.com/package/@lynx-js/create-lynx) to create a ReactLynx component library with Rslib:


```sh [npm]
npm create @lynx-js/lynx@latest
```

```sh [yarn]
yarn create @lynx-js/lynx
```

```sh [pnpm]
pnpm create @lynx-js/lynx@latest
```

```sh [bun]
bun create @lynx-js/lynx@latest
```

```sh [deno]
deno init --npm @lynx-js/lynx@latest
```

Then select `Rslib` when prompted to "Select build tool", followed by TypeScript or JavaScript. You can also specify the Rslib template directly:


```sh [npm]
npm create @lynx-js/lynx@latest my-lib -- --template rslib-react-ts
```

```sh [yarn]
yarn create @lynx-js/lynx my-lib --template rslib-react-ts
```

```sh [pnpm]
pnpm create @lynx-js/lynx@latest my-lib --template rslib-react-ts
```

```sh [bun]
bun create @lynx-js/lynx@latest my-lib --template rslib-react-ts
```

```sh [deno]
deno init --npm @lynx-js/lynx@latest my-lib --template rslib-react-ts
```

## Use Rslib in an existing project

To develop a ReactLynx library, you need to set the [target](/config/rsbuild/output.md#outputtarget) to `"web"` in `rslib.config.ts`. This is crucial because Rslib sets the `target` to `"node"` by default, which differs from the default target of Rsbuild.

Additionally, ReactLynx component libraries typically need to preserve JSX syntax in the build output so that the application's ReactLynx compiler can process it according to the target environment and build configuration. You can register the Rsbuild [React Plugin](https://rsbuild.rs/plugins/list/plugin-react), set `runtime` to `'preserve'` through [swcReactOptions](https://rsbuild.rs/plugins/list/plugin-react#swcreactoptionsruntime), and set [bundle](/config/lib/bundle.md) to `false` to enable bundleless builds. Also, set `js` to `'[name].jsx'` in [output.filename](/config/rsbuild/output.md#outputfilename) to emit `.jsx` files.

For example, configure `rslib.config.ts` as follows:

```ts title="rslib.config.ts" twoslash
import { defineConfig } from '@rslib/core';
import { pluginReact } from '@rsbuild/plugin-react'; // [!code highlight]

export default defineConfig({
  bundle: false, // [!code highlight]
  // [!code highlight:6]
  output: {
    target: 'web',
    filename: {
      js: '[name].jsx',
    },
  },
  plugins: [
    pluginReact({
      // [!code highlight:3]
      swcReactOptions: {
        runtime: 'preserve',
      },
    }),
  ],
});
```

## TypeScript

For ReactLynx projects using TypeScript, set `"jsx": "preserve"` and `"jsxImportSource": "@lynx-js/react"` in your `tsconfig.json`, and add `@lynx-js/types` to `types`:

```json title="tsconfig.json"
{
  "compilerOptions": {
    // [!code highlight:3]
    "jsx": "preserve",
    "jsxImportSource": "@lynx-js/react",
    "types": ["@lynx-js/types", "@rslib/core/types"]
  }
}
```

Set [dts](/config/lib/dts.md) to `true` in `rslib.config.ts` to generate the library's type declarations.

## Output

Configure the `.jsx` entry and type declaration entry in `package.json`, and declare ReactLynx and its type dependencies as peer dependencies:

```json title="package.json"
{
  "name": "reactlynx-scroll-list",
  "type": "module",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts", // [!code ++]
      "default": "./dist/index.jsx" // [!code ++]
    }
  },
  "types": "./dist/index.d.ts", // [!code ++]
  "files": ["dist"],
  "peerDependencies": {
    "@lynx-js/react": ">=0.100.0", // [!code ++]
    "@lynx-js/types": ">=4", // [!code ++]
    "@types/react": ">=19" // [!code ++]
  }
}
```

## Testing

You can use Rstest to test ReactLynx components. First, install the dependencies needed for testing:


```sh [npm]
npm add @rstest/core @rstest/adapter-rslib @lynx-js/react-rsbuild-plugin @testing-library/dom @testing-library/jest-dom happy-dom -D
```

```sh [yarn]
yarn add @rstest/core @rstest/adapter-rslib @lynx-js/react-rsbuild-plugin @testing-library/dom @testing-library/jest-dom happy-dom -D
```

```sh [pnpm]
pnpm add @rstest/core @rstest/adapter-rslib @lynx-js/react-rsbuild-plugin @testing-library/dom @testing-library/jest-dom happy-dom -D
```

```sh [bun]
bun add @rstest/core @rstest/adapter-rslib @lynx-js/react-rsbuild-plugin @testing-library/dom @testing-library/jest-dom happy-dom -D
```

```sh [deno]
deno add npm:@rstest/core npm:@rstest/adapter-rslib npm:@lynx-js/react-rsbuild-plugin npm:@testing-library/dom npm:@testing-library/jest-dom npm:happy-dom -D
```

Use the `withRslibConfig` function from `@rstest/adapter-rslib` to reuse your Rslib configuration. See [Use Rstest](/guide/advanced/rstest.md) for details.

Also, use the `withDefaultConfig` function provided by `@lynx-js/react` to load the [ReactLynx test preset](https://lynxjs.org/api/reactlynx-testing-library/#rstest), and register the `pluginReactLynx` plugin from `@lynx-js/react-rsbuild-plugin` to compile JSX:

```ts title="rstest.config.ts"
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'; // [!code ++]
import { withDefaultConfig } from '@lynx-js/react/testing-library/rstest-config'; // [!code ++]
import { withRslibConfig } from '@rstest/adapter-rslib';
import { defineConfig } from '@rstest/core';

export default defineConfig({
  extends: [withDefaultConfig(), withRslibConfig()], // [!code ++]
  plugins: [pluginReactLynx()], // [!code ++]
});
```

Once configured, import APIs such as `render`, `screen`, and `fireEvent` from `@lynx-js/react/testing-library` to test component rendering and interactions.

See the [ReactLynx testing guide](https://lynxjs.org/react/reactlynx-testing-library.html) for usage details. You can find a complete component testing project in [Examples](https://github.com/rstackjs/rstack-examples/tree/main/rslib/reactlynx-rstest).

## Use the component library

### Use in an application

In a Lynx application, you can use a ReactLynx component library by importing it as a package or loading it as an External Bundle.

#### Import from a package

The component library built and published with the configuration above preserves JSX. After installing it in a Lynx application, you can import and use its components directly, with their JSX processed by the application's ReactLynx compiler. For example, use the `ScrollList` exported by the library:

```tsx title="src/App.tsx"
import { ScrollList } from 'reactlynx-scroll-list';

export function App() {
  return <ScrollList />;
}
```

#### Load an external bundle

Lynx applications can also load an External Bundle on demand at runtime. Its JSX has already been compiled during the bundle build. When creating the library, you can select the optional `External Bundle` tool or enable it with `--tools external-bundle` in the initialization command:


```sh [npm]
npm create @lynx-js/lynx@latest my-lib -- --template rslib-react-ts --tools external-bundle
```

```sh [yarn]
yarn create @lynx-js/lynx my-lib --template rslib-react-ts --tools external-bundle
```

```sh [pnpm]
pnpm create @lynx-js/lynx@latest my-lib --template rslib-react-ts --tools external-bundle
```

```sh [bun]
bun create @lynx-js/lynx@latest my-lib --template rslib-react-ts --tools external-bundle
```

```sh [deno]
deno init --npm @lynx-js/lynx@latest my-lib --template rslib-react-ts --tools external-bundle
```

The generated project includes `rslib.external-bundle.config.*` and a `build:external-bundle` script. Run the following command to compile the library into `dist-external-bundle/<id>.lynx.bundle`:


```sh [npm]
npm run build:external-bundle
```

```sh [yarn]
yarn run build:external-bundle
```

```sh [pnpm]
pnpm run build:external-bundle
```

```sh [bun]
bun run build:external-bundle
```

```sh [deno]
deno run build:external-bundle
```

See the [Lynx External Bundle guide](https://lynxjs.org/rspeedy/external-bundle.html) for loading and configuration details.

### Use in a component library

You can use existing shared components in your library and preserve JSX for the application to compile. Distributing the package that provides those components as a dependency is recommended. If you need to publish its code with your library, copy output that is ready to use, or rebuild it when its code, styles, or internal imports need processing.

::: tip Type declarations

When publishing dependency code with your library by building or copying it, consumers still need to install the dependency if your `.d.ts` files reference its types. Use [dts.bundle.bundledPackages](/config/lib/dts.md#dtsbundlebundledpackages) to bundle those type declarations as well, for example by setting it to `['reactlynx-scroll-list']`.

:::

#### Distribute as a dependency (recommended)

Declare the library that provides the shared components in `dependencies` or `peerDependencies`. Rslib marks these dependencies as [external](/guide/advanced/third-party-deps.md#default-handling-of-third-party-dependencies) by default, preserving their package imports.

When consumers install your library, their package manager installs or reuses packages according to the declared dependencies. The application build then loads these libraries through their package imports and compiles their JSX together.

#### Rebuild dependency output

If you need to compile the dependency's code or styles, or change its internal imports, use Rslib to build your library and the dependency separately, and use [output.externals](/config/rsbuild/output.md#outputexternals) to rewrite imports.

Consider a component library that includes a scroll list adapted from `reactlynx-scroll-list` and other components you write. The configuration has three builds, each identified by an [id](/config/lib/id.md):

- `components`: builds the other components you write in bundleless mode, handling component code that needs to preserve JSX.
- `bundled-components`: bundles the scroll list entry and the local TS/JS modules it imports, which can adjust exports or include other TS/JS logic. It uses `output.externals` to reference the dependency output.
- `vendor`: rebuilds the output of `reactlynx-scroll-list`, preserving JSX and emitting files to `dist/vendor/reactlynx-scroll-list`.

In this example, only `src/scroll-list/index.ts` imports `reactlynx-scroll-list`. Other components reference this entry through local imports:

```ts title="src/scroll-list/index.ts"
export { ScrollList } from 'reactlynx-scroll-list';
```

Adjust [outBase](/config/lib/out-base.md) and the entry to match the dependency's actual output, including the code, styles, and static assets to process:

```ts title="rslib.config.ts"
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { pluginReact } from '@rsbuild/plugin-react';
import { defineConfig } from '@rslib/core';

const scrollListDir = dirname(
  fileURLToPath(import.meta.resolve('reactlynx-scroll-list')),
);
const reactPlugin = pluginReact({
  swcReactOptions: {
    runtime: 'preserve',
  },
});

export default defineConfig({
  lib: [
    {
      id: 'components',
      bundle: false, // [!code ++]
      dts: true,
      source: {
        entry: {
          index: [
            './src/**/*',
            '!./src/scroll-list/**', // [!code ++]
          ],
        },
      },
      plugins: [reactPlugin],
    },
    {
      id: 'bundled-components',
      source: {
        entry: {
          'scroll-list/index': './src/scroll-list/index.ts', // [!code ++]
        },
      },
      output: {
        externals: {
          'reactlynx-scroll-list': '../vendor/reactlynx-scroll-list/index.jsx', // [!code ++]
        },
      },
    },
    {
      id: 'vendor',
      bundle: false, // [!code ++]
      outBase: scrollListDir, // [!code ++]
      source: {
        entry: {
          index: `${scrollListDir}/**/*.{js,jsx,css,svg}`, // [!code ++]
        },
      },
      output: {
        distPath: './dist/vendor/reactlynx-scroll-list', // [!code ++]
      },
      plugins: [reactPlugin],
    },
  ],
  output: {
    target: 'web',
    filename: {
      js: '[name].jsx',
    },
  },
});
```

The top-level `output.filename` setting gives all builds a consistent `.jsx` extension for their code output. The generated `dist/scroll-list/index.jsx` imports `dist/vendor/reactlynx-scroll-list/index.jsx`, so include the entire `dist` directory when publishing your package.

If `reactlynx-scroll-list` imports other component libraries that also need to be distributed with your package, add builds for those packages. In the `vendor` build, use `output.externals` to rewrite their package imports to the paths of their output files.

#### Copy dependency output directly

If the dependency's output is ready to use and its internal imports need no changes, copy the complete output with [output.copy](/config/rsbuild/output.md#outputcopy).

The following configuration builds the library source in bundleless mode to preserve JSX and copies the dependency's output. As above, only `src/scroll-list/index.ts` imports the dependency, and the path in `output.externals` is relative to the generated `dist/scroll-list/index.jsx`:

```ts title="rslib.config.ts"
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { pluginReact } from '@rsbuild/plugin-react';
import { defineConfig } from '@rslib/core';

const scrollListDir = dirname(
  fileURLToPath(import.meta.resolve('reactlynx-scroll-list')),
);

export default defineConfig({
  bundle: false,
  dts: true,
  output: {
    target: 'web',
    filename: {
      js: '[name].jsx',
    },
    externals: {
      'reactlynx-scroll-list': '../vendor/reactlynx-scroll-list/index.jsx', // [!code ++]
    },
    copy: [
      {
        from: scrollListDir, // [!code ++]
        to: 'vendor/reactlynx-scroll-list', // [!code ++]
      },
    ],
  },
  plugins: [
    pluginReact({
      swcReactOptions: {
        runtime: 'preserve',
      },
    }),
  ],
});
```
