React-Create react library: ⚡CLI for creating reusable react libraries.

create-react-library

CLI for easily publishing modern React libraries with Rollup and example usage via create-react-app.

NPM Build Status JavaScript Style Guide

Intro

The purpose of this CLI is to make publishing your own React components as simple as possible.

Features

  • Easy-to-use CLI
  • Handles all modern JS features
  • Bundles cjs and es module formats
  • create-react-app for example usage and local dev
  • Rollup for bundling
  • Babel for transpiling
  • Jest for testing
  • Supports complicated peer-dependencies
  • Supports CSS modules
  • Optional support for TypeScript
  • Sourcemap creation
  • Hundreds of public modules created
  • Thorough documentation ?
  • Chinese docs by @monsterooo

Install

This package requires node >= 4, but we recommend node >= 8.

npm install -g create-react-library

Creating a New Module

create-react-library

Answer some basic prompts about your module, and then the CLI will perform the following steps:

  • copy over the template to a new folder in the current directory
  • install dependencies via yarn or npm
  • link packages together for local development
  • initialize local git repo

At this point, your new module should resemble this screenshot and is all setup for local development.

Development

Local development is broken into two parts.

First, you'll run rollup to watch your src/ module and automatically recompile it into dist/ whenever you make changes.

npm start # runs rollup with watch flag

The second part will be running the example/ create-react-app that's linked to the local version of your module.

# (in another tab)
cd example
npm start # runs create-react-app dev server

Now, anytime you make a change to your library in src/ or to the example app's example/src, create-react-app will live-reload your local dev server so you can iterate on your component in real-time.

Publishing to NPM

The only difference when publishing your library to npm is to make sure that any npm modules you want as peer dependencies are properly marked as peerDependencies in package.json. The rollup config will automatically recognize them as peers and not try to bundle them in your module.

Then publish as per usual.

# note this will build `commonjs` and `es`versions of your module to dist/
npm publish

Github Pages

Deploying the example to github pages is simple. We create a production build of our example create-react-app that showcases your library and then run gh-pages to deploy the resulting bundle. This can be done as follows:

npm run deploy

Examples

Multiple Named Exports

Here is a branch which demonstrates how to use multiple named exports. The module in this branch exports two components, Foo and Bar, and shows how to use them from the example app.

Material-UI

Here is a branch which demonstrates how to make use of a relatively complicated peer dependency, material-ui. It shows the power of rollup-plugin-peer-deps-external which makes it a breeze to create reusable modules that include complicated material-ui subcomponents without having them bundled as a part of your module.

Boilerplate

The CLI is based on this boilerplate, which you can optionally read about here.

Libraries

Here are some example libraries that have been bootstrapped with create-react-library.

Want to add yours to the list? Submit an issue.

License

MIT © Travis Fischer

Comments

  • npm start just finishes, doesn't generate dist
    npm start just finishes, doesn't generate dist

    Nov 6, 2021

    Hey just trying this project out for the first time.

     npm start
    > [email protected] start /Users/beamer/GitHub/sk8view
    > microbundle-crl watch --no-compress --format modern,cjs
    Watching source, compiling to dist:
    

    that's it - it doesn't watch it just complete and gives me back my terminal

    Reply
  • Configure css modules hinting
    Configure css modules hinting

    Nov 7, 2021

    Hello As i wrote in topic, i want to add hinting for class in imported module. In screenshot then bellow i haven't this. I've installed "eslint-plugin-react-css-module-hints" and "babel-plugin-react-css-modules" packages, add "--css-modules true" parameter to microbundle-crl run in package.json but it don';t work. obraz My css.module file: obraz

    I read somewhere that i need webpack config for that. Can I do it without them? Or if is required , could you will write me how to add webpack with config to create-react-library project?

    Thanks for your time.

    Reply
  • ESLint couldn't find the config
    ESLint couldn't find the config "react-app" to extend from.

    Nov 8, 2021

    When I try to do yarn test with my github CI action, I get the following:

    Ran all test suites.
    
    $ eslint .
    
    Oops! Something went wrong! :(
    
    ESLint: 7.32.0
    ESLint couldn't find the config "react-app" to extend from. Please check that the name of the config is correct.
    The config "react-app" was referenced from the config file in "/home/runner/work/undp-radar/undp-radar/example/package.json".
    If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.
    

    Tried to reference eslint-config-react-app from the folder where it is npm ls eslint-config-react-app using link:, tried also to install it (using version from npm ls) but nothing worked. Any help would be most appreciated! :)

    Reply
  • How do you run tests with coverage?
    How do you run tests with coverage?

    Dec 17, 2021

    I tried several permutations on the scripts:

    "test:coverage": "react-scripts test -- --coverage --env=jest-environment-jsdom-fourteen",
    "test:coverage": "cross-env CI=1 react-scripts test -- --coverage --env=jest-environment-jsdom-fourteen",
    "test:coverage": "cross-env CI=1 react-scripts test -- --env=jest-environment-jsdom-fourteen --coverage",
    
    "test:coverage": "CI=true npm test -- --env=jest-environment-jsdom-fourteen --coverage",
    
    "test:coverage": "CI=true npm test -- --coverage --collectCoverageFrom='src/**/*.js' --collectCoverageFrom='!src/**/*.stories.js' --collectCoverageFrom='!src/**/reportWebVitals.js' --collectCoverageFrom='!src/**/index.js'  --collectCoverageFrom='!src/**/api.js' --collectCoverageFrom='!src/**/*Context*.js'",
    
    "test:coverage": "cross-env CI=1 react-scripts test -- --env=jest-environment-jsdom-fourteen --coverage --collectCoverageFrom='src/**/*.js' --collectCoverageFrom='!src/**/*.stories.js' --collectCoverageFrom='!src/**/reportWebVitals.js' --collectCoverageFrom='!src/**/index.js'  --collectCoverageFrom='!src/**/api.js' --collectCoverageFrom='!src/**/*Context*.js'",
    

    but none of these would work.

    I tried turning coverage on in the jest configuration:

    "jest": {
        "collectCoverage": true
    }
    

    but this was not in the list of accepted jest options.

    I added the following options:

     "jest": {
        "collectCoverageFrom": [
          "src/**/*.js",
          "!src/**/index.js",
          "!src/**/*.stories.js"
        ],
        "coverageReporters": ["clover", "json", "lcov", "text"],
        "coverageThreshold": {
          "global": {
            "branches": 80,
            "functions": 80,
            "lines": 80,
            "statements": -10
          }
        }
    }
    

    which were accepted but didn't seem to generate any coverage report.

    Reply
  • Issues with babel jsx transform
    Issues with babel jsx transform

    Jan 4, 2022

    I am having issues with babel jsx transform following the steps in this article (it's a bit old but seems valid) https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html. I am trying to remove the need to import React into every component that uses JSX and can't seem to figure out how to get that working with this library. The relevant parts of my package.json and babel.config.js are as follows:

    // package.json
    {
      "main": "dist/index.js",
      "module": "dist/index.modern.js",
      "source": "src/index.js",
      "files": [
        "dist"
      ],
      "engines": {
        "node": ">=10"
      },
      "scripts": {
        "build": "microbundle-crl --no-compress --format modern,cjs",
        "start": "microbundle-crl watch --no-compress --format modern,cjs",
        "prepare": "run-s build",
        "test": "run-s test:unit test:lint test:build",
        "test:build": "run-s build",
        "test:lint": "eslint .",
        "test:unit": "cross-env CI=1 react-scripts test --env=jsdom",
        "test:watch": "react-scripts test --env=jsdom",
        "predeploy": "cd example && npm install && npm run build",
        "deploy": "gh-pages -d example/build"
      },
      "peerDependencies": {
        "react": "^16.0.0",
        "react-html-parser": "^2.0.2",
        "lodash": "^4.17.19",
        "@material-ui/core": "^4.11.0",
        "react-redux": "^7.1.1",
        "redux": "^4.0.1",
        "redux-localstorage": "^0.4.1",
        "redux-logger": "^3.0.6",
        "redux-thunk": "^2.3.0",
        "react-router-dom": "^5.1.1",
        "react-dom": "^16.13.1",
        "react-scripts": "^3.4.1",
        "react-svg": "^12.0.0",
        "reselect": "^4.0.0"
      },
      "devDependencies": {
        "microbundle-crl": "^0.13.10",
        "babel-eslint": "^10.0.3",
        "cross-env": "^7.0.2",
        "eslint": "^6.8.0",
        "eslint-config-prettier": "^6.7.0",
        "eslint-config-standard": "^14.1.0",
        "eslint-config-standard-react": "^9.2.0",
        "eslint-plugin-import": "^2.18.2",
        "eslint-plugin-node": "^11.0.0",
        "eslint-plugin-prettier": "^3.1.1",
        "eslint-plugin-promise": "^4.2.1",
        "eslint-plugin-react": "^7.17.0",
        "eslint-plugin-standard": "^4.0.1",
        "gh-pages": "^2.2.0",
        "npm-run-all": "^4.1.5",
        "prettier": "^2.0.4",
        "babel-plugin-module-resolver": "^3.2.0"
      },
      "dependencies": {
        "@babel/core": "^7.16.7",
        "@babel/preset-env": "^7.16.7",
        "@babel/preset-react": "^7.16.7",
        "node-sass": "^7.0.0"
       }
    }
    
    // babel.config.js
    module.exports = {
      presets: [
        [
          '@babel/preset-react',
          {
            runtime: 'automatic'
          }
        ]
      ],
      plugins: [
        [
          'module-resolver',
          {
            alias: {
              src: './src',
              actions: './src/actions',
              components: './src/components',
              constants: './src/constants',
              contexts: './src/contexts',
              hoc: './src/hoc',
              selectors: './src/selectors',
              styles: './src/styles',
              themes: './src/themes',
              utils: './src/utils'
            },
            extensions: ['.js', '.jsx', '.es', '.es6', '.mjs', '.scss']
          }
        ]
      ]
    }
    
    Reply
  • UMD export does not work - missing ReactDOM
    UMD export does not work - missing ReactDOM

    Jan 13, 2022

    Hello, I want to create a UMD library embeddable on any HTML page. I've created a project with npx create-react-library app --template typescript and then add the dependencies required to make it UMD (see my package.json below). But still receive an error: image image

    My package.json (I've add react-dom to peerDependencies):

    {
      "name": "app",
      "version": "1.0.0",
      "description": "Made with create-react-library",
      "author": "me",
      "license": "MIT",
      "repository": "me/grpc-docs",
      "main": "dist/index.js",
      "module": "dist/index.modern.js",
      "source": "src/index.tsx",
      "engines": {
        "node": ">=10"
      },
      "scripts": {
        "build": "microbundle-crl --no-compress --format modern,cjs,umd",
        "start": "microbundle-crl watch --no-compress --format modern,cjs",
        "prepare": "run-s build",
        "test": "run-s test:unit test:lint test:build",
        "test:build": "run-s build",
        "test:lint": "eslint .",
        "test:unit": "cross-env CI=1 react-scripts test --env=jsdom",
        "test:watch": "react-scripts test --env=jsdom",
        "predeploy": "cd example && yarn install && yarn run build",
        "deploy": "gh-pages -d example/build"
      },
      "peerDependencies": {
        "react": "^16.0.0",
        "react-dom": "^16.13.1"
      },
      "devDependencies": {
        "@testing-library/jest-dom": "^4.2.4",
        "@testing-library/react": "^9.5.0",
        "@testing-library/user-event": "^7.2.1",
        "@types/jest": "^25.1.4",
        "@types/node": "^12.12.38",
        "@types/react": "^16.9.27",
        "@types/react-dom": "^16.9.7",
        "@typescript-eslint/eslint-plugin": "^2.26.0",
        "@typescript-eslint/parser": "^2.26.0",
        "babel-eslint": "^10.0.3",
        "cross-env": "^7.0.2",
        "eslint": "^6.8.0",
        "eslint-config-prettier": "^6.7.0",
        "eslint-config-standard": "^14.1.0",
        "eslint-config-standard-react": "^9.2.0",
        "eslint-plugin-import": "^2.18.2",
        "eslint-plugin-node": "^11.0.0",
        "eslint-plugin-prettier": "^3.1.1",
        "eslint-plugin-promise": "^4.2.1",
        "eslint-plugin-react": "^7.17.0",
        "eslint-plugin-standard": "^4.0.1",
        "gh-pages": "^2.2.0",
        "microbundle-crl": "^0.13.10",
        "npm-run-all": "^4.1.5",
        "prettier": "^2.0.4",
        "react": "^16.13.1",
        "react-dom": "^16.13.1",
        "react-scripts": "^3.4.1",
        "typescript": "^3.7.5"
      },
      "files": [
        "dist"
      ],
      "dependencies": {
      }
    }
    

    index.tsx:

    import * as React from 'react'
    import ReactDOM from 'react-dom'
    
    interface Config {
      elementId: string
    }
    export const init = (config: Config) => {
      const el = document.getElementById(config.elementId)
      if (!el) {
        console.error('Unable to find element with id=' + config.elementId)
        return
      }
      ReactDOM.render(<div>This is react component</div>, el)
    }
    

    HTML test file (in the root of the app):

    <html>
    	<body>
    		Hello, world!
        <script src="./dist/index.umd.js">
        </script>
        <div id="root"></div>
    		<script>
          app.init({
            elementId: "root",
          })
    		</script>
    	</body>
    </html>
    

    Adding CDN URLs in the HTML file doesn't fix issues:

    ......
    		Hello world!
        <script crossorigin src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
        <script src="./dist/index.umd.js">
        </script>
    ....
    

    How to fix it?

    Reply
  • upgrade to babel 7
    upgrade to babel 7

    Nov 22, 2018

    resolve #86

                                                                                                                                                                                                           
    Reply
  • className not working without using styles
    className not working without using styles

    Dec 12, 2018

    When I am building the component. The following method of importing css does not work

    Method 1

    import  "./index.css"
    
    const Mycomponent =()=>
    <div className="title">Title</div>
    

    It seems that the compiler was not able to recognize className = "". If I define the body{} by import "./index.css", it works fine.

    Instead, I need to use the styles method

    Method2

    import styles from "./index.css"
    
    const Mycomponent =()=>
    <div className={styles.title}">Title</div>
    

    It would be very difficult to use some external library, which rely on method 1 for styling. Is there a way to use method 1 instead of method 2

    It would probably be a rollup config issue? I set

    postcss({
          modules: false,
        }),
    

    it is still not working.

    Reply
  • yarn install failing
    yarn install failing

    Mar 31, 2020

    Getting the following error when I try creating a new project

    Initializing npm dependencies. This will take a minute.

    √ Running yarn install in root directory × Running yarn install in example directory Error: spawn C:\windows\system32\cmd.exe ENOENT at Process.ChildProcess._handle.onexit (internal/child_process.js:267:19) at onErrorNT (internal/child_process.js:467:16) at processTicksAndRejections (internal/process/task_queues.js:84:21) { errno: -4058, code: 'ENOENT', syscall: 'spawn C:\windows\system32\cmd.exe', path: 'C:\windows\system32\cmd.exe', spawnargs: [ '/q', '/s', '/c', '"yarn install"' ], stdout: '', stderr: '', failed: true, signal: null, cmd: 'C:\windows\system32\cmd.exe /q /s /c "yarn install"', timedOut: false, killed: false }

    The error occurs for both yarn as well as npm.

    node version: 13.12.0 yarn version: 1.21.1

    Reply
  • Building Modular Libraries
    Building Modular Libraries

    Sep 17, 2018

    Can you add an example rollup configuration for building modular libraries?

    For example:

    Multiple imports Right Now-
    import { A, B } from 'package';
    
    With Modular Imports -
    import A from 'package/A'; 
    import B from 'package/B';
    

    ^ This approach helps reduce the bundle size, as only the file needed is imported and added to the bundle.

    Reply
  • Add more template options?
    Add more template options?

    Mar 9, 2018

    Some ideas:

    • webpack vs rollup
    • flow or typescript support
    • prettier integration ...

    I'd really like to keep the default template as minimal as possible, but people should be able to easily swap out different options if they want. Thoughts / suggestions?

    enhancement question 
    Reply
  • Error when using hooks
    Error when using hooks

    Nov 3, 2018

    I'm bootstrapping a React component library with this tool and I get: Hooks can only be called inside the body of a function component.

    Steps to reproduce:

    • Create a library with create-react-library.
    • Update react and react-dom to v16.7.0-alpha.0 in ./package.json and ./src/package.json.
    • Use new React hooks feature inside src/.

    If you try the example you get the error. One weird thing is that if you use hooks in the example you don't get any error.

    Reply