Building and Maintaining a Sustainable Frontend Component Library with React

This article details the end‑to‑end process of designing, publishing, packaging, optimizing, and continuously delivering a reusable React component library at Ctrip, covering npm workflow, build tooling choices, CSS extraction, on‑demand loading, package splitting, collaborative documentation, CI/CD, and automated testing.

Ctrip Technology
Ctrip Technology
Ctrip Technology
Building and Maintaining a Sustainable Frontend Component Library with React

Component‑based development has become the norm in modern front‑end frameworks such as React and Vue, and building a high‑quality, reusable component library can greatly improve development efficiency and product stability.

Using React as the technical background, the author describes a two‑year effort at Ctrip to create a public component library for various ride‑hailing services (airport, flight, city, address, time controls, etc.). By centralizing components and utilities, teams avoid duplicated code and achieve consistent UI across product lines.

1. Basic npm publishing workflow

To ensure safe and reliable releases, Ctrip maintains an internal npm registry. Publishing is linked to GitLab CI, with permissions enforced via npm‑deploy plugins and GitLab hooks that trigger auto‑publish on push events.

Initially, releases were performed with npm publish after pointing the registry to the internal store, but this posed security risks because any user could modify any package. The workflow was migrated to GitLab, adding permission checks and visual deployment management.

Npm linked with GitLab triggers automatic version bump and publish.

2. Packaging the library

For React Web components, the team switched from UMD to CommonJS2 to reduce bundle size, using Webpack for per‑component builds. Example Webpack output configuration:

module.exports = {
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, '..', 'dist'),
        library: 'Tha',
        libraryTarget: 'commonjs2' // umd
    }
}

Because most projects no longer need AMD/CMD compatibility, CommonJS2 provides faster builds and smaller files.

Instead of adding another build tool like Rollup, the team adopted a Babel‑only compilation approach, removing extra bundlers and relying on modern front‑end pipelines to handle transpilation and minification.

Build script before optimization:

{
  "scripts": {
    "build:components": "webpack --config ./build/webpack.config.js --color",
    "build": "npm run build:components && npm run build:css && npm run copy_package"
  }
}

After optimization:

{
  "scripts": {
    "build:components": "cross-env NODE_ENV=production node ./build/trans"
  }
}

3. Extracting CSS

To avoid large inline CSS bundles and server‑side rendering issues, all component styles are extracted into a separate main.css for basic UI components, while complex business components receive per‑component CSS bundles for on‑demand loading.

4. On‑demand loading of business components

For React Native, lazy loading is achieved by defining getters that require modules only when accessed:

module.exports = {
    // lazy loaded modules
    get AddressList() { return require('./Address/List').default; }
};

For Web, a Babel plugin similar to Ant Design’s approach is used, allowing developers to import components and their styles on demand:

import { Address } from '@ctrip/thanos-ctrip-mobile/components.biz'
/* equivalent to */
import Address from '@ctrip/thanos-ctrip-mobile/components.biz/Address'
import '@ctrip/thanos-ctrip-mobile/components.biz/Address/style.css'

5. Splitting the library to avoid bloat

As the library grows, core functionality is extracted into independent packages such as ui-basic for UI primitives and util for helper methods, reducing the footprint for consumers who only need a subset of features.

After splitting, the architecture becomes more modular and extensible.

6. Shared development environment for sub‑packages

All sub‑packages share a single development repository using Git submodules, allowing developers to maintain multiple packages without duplicating environment setup.

This setup also enables direct source imports between dependent sub‑modules, simplifying debugging.

7. Documentation and collaborative development

The library’s documentation is written in GitBook and deployed internally, with GitLab webhooks automatically updating the docs on changes. The project follows an open‑source‑within‑company model, encouraging engineers to submit issues and merge requests.

8. Unit testing, automation, and CI/CD

Jest is chosen as the test framework, with @testing-library/react for web and @testing-library/react‑native for RN, providing strong support for hooks and aligning with React’s ecosystem.

GitLab CI pipelines run unit tests, perform Sonar quality analysis, and publish the library automatically, ensuring high code quality throughout the development lifecycle.

Conclusion

Building a robust component library requires careful attention to publishing safety, build optimization, modular architecture, shared tooling, documentation, and automated testing; continuous iteration and learning are essential to maintain a healthy front‑end engineering ecosystem.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontend developmentci/cdAutomationReacttestingpackagingComponent Library
Ctrip Technology
Written by

Ctrip Technology

Official Ctrip Technology account, sharing and discussing growth.

0 followers
Reader feedback

How this landed with the community

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.