Mobile Development 29 min read

Building a React Native Component Library with Lerna, Yarn Workspaces, and Automated Tooling

This comprehensive tutorial walks through creating a React Native component library using Lerna monorepo, Yarn workspaces, code standards, automated builds, unit testing, documentation with Dumi, and on-demand loading techniques, providing practical guidance for developers to manage, test, and publish reusable UI components.

ByteFE
ByteFE
ByteFE
Building a React Native Component Library with Lerna, Yarn Workspaces, and Automated Tooling

Introduction

The article presents a step‑by‑step guide for building a production‑ready React Native component library, covering repository management, component design, unit testing, CI/CD, and documentation.

Choosing the Base UI Library

After evaluating vant , antd-mobile and fishd-mobile , the author selects vant as the design reference and creates a new package named vant-react-native with appropriate name and description fields in package.json :

{
  "name": "vant-react-native",
  "description": "Lightweight React Native UI Components inspired on Vant"
}

Monorepo Architecture with Lerna

Lerna is used to manage a monorepo (single repository) that contains multiple packages. The advantages include component‑level decoupling, independent versioning, on‑demand imports, clear dependency control, and easier contribution.

.
└── packages
    ├── button   # @vant-react-native/button
    └── icons    # @vant-react-native/icon

Initializing the Lerna Project

$ mkdir vant-react-native && lerna init --independent

Yarn Workspaces Integration

Enable useWorkspaces in lerna.json and configure package.json workspaces:

{
  "npmClient": "yarn",
  "useWorkspaces": true
}

{
  "private": true,
  "workspaces": ["packages/*"]
}

Lerna Publish Configuration

Set up ignoreChanges and command.publish.registry to control version bumps and publishing:

{
  "ignoreChanges": ["ignored-file", "**/__tests__/**", "**/*.md"],
  "command": {
    "publish": {
      "registry": "https://registry.npmjs.org"
    }
  }
}

Standardized Git Commit Messages

The author uses Conventional Commits via the @youngjuning/cli tool, providing an init-commit command that configures commitizen , cz-customizable , and commitlint . Husky is replaced by yorkie for Git hooks.

Code Style Enforcement

ESLint, Prettier, and EditorConfig are set up using the author's own configs ( @youngjuning/eslint-config , @youngjuning/prettier-config ). Example ESLint installation and configuration:

yarn add -D eslint-plugin-react \
  eslint-plugin-react-hooks \
  eslint-plugin-jsx-a11y \
  eslint-plugin-import \
  eslint-plugin-react-native

// .eslintrc.js
module.exports = {
  extends: ['@youngjuning/eslint-config/react-native']
};

Creating the First Component – Icons

Icons are generated from an Iconfont service using react-native-iconfont-cli . The workflow includes creating the icons package, generating iconfont.json , and building the React Native components:

# Create packages
$ lerna create vant-react-native -y
$ lerna create @vant-react-native/icons -y

# Generate iconfont config
$ npx iconfont-init   # creates iconfont.json

# Build icons
$ npx iconfont-rn

# package.json scripts for icons
{
  "build": "npx iconfont-rn",
  "prepublishOnly": "yarn build"
}

Exporting Packages and TypeScript Configuration

The main package re‑exports the icon and button modules, and a shared tsconfig.base.json is used for all sub‑packages:

// packages/vant-react-native/src/index.ts
export * from './icon';
export * from './button';

Publishing the Library

First release uses lerna publish 0.0.1 (or sets the initial version to 0.0.0 and runs lerna publish ). The author also mentions checking the published package via jsDelivr.

Development & Debugging Workflow

Because Metro does not support symlinks outside the project root, the author ensures that Lerna’s hoisted packages remain within the root. The typical development loop is:

Modify code.

Run lerna run build to compile each package.

Run yarn ios (or yarn start --reset-cache ) to launch the app.

Repeat.

Real‑time compilation scripts are added (e.g., tsc -w for dev, onchange -i 'iconfont.json' -- yarn build for icons) and orchestrated with npm-run-all :

{
  "predev": "lerna run build --scope @vant-react-native/icons",
  "dev": "lerna run dev --parallel",
  "start": "react-native start",
  "debug": "run-p dev start"
}

On‑Demand Loading

Since Metro cannot tree‑shake, the author uses babel-plugin-import to rewrite imports to specific sub‑folders, enabling on‑demand loading of components and icons. Example Babel config:

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    ["import", { libraryName: 'vant-react-native' }]
  ]
};

A custom plugin ( babel-plugin-import-vant ) is also provided to map named imports like import { Button } from 'vant-react-native' to import Button from '@vant-react-native/button' .

Documentation with Dumi

The library uses Dumi (with dumi-theme-mobile and umi-plugin-react-native ) to generate an interactive documentation site. Key steps include installing Dumi, creating .umirc.ts , adding build scripts, and configuring CI for GitHub Pages and Surge preview deployments.

Unit Testing

Jest and Enzyme are configured for React Native unit tests. The jest.config.js includes preset react-native , coverage collection, module name mapping for assets, and snapshot serializers. A sample test for the Button component demonstrates shallow rendering and snapshot verification.

Conclusion

The tutorial equips developers with a full pipeline—from monorepo setup, code quality enforcement, component generation, on‑demand loading, documentation, to CI/CD—enabling the creation and maintenance of a robust React Native UI component library.

testingcomponent librarydocumentationReact NativelernaYarn WorkspacesOn-demand Loading
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.