New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

react-lib-tutorial

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-lib-tutorial

Example repo on how to create a React library

latest
Source
npmnpm
Version
2.0.0
Version published
Weekly downloads
2
-71.43%
Maintainers
1
Weekly downloads
 
Created
Source

How to create a React library

Walkthrough for setting up a React Library repo with Github, React, Typescript, Rollup and Emotion.

Sources:

  • Creating a React Component Library using Rollup, Typescript, Sass and Storybook
  • 3 Ways to Build Your Own React Component Library
  • create-react-library - Github

Development:

yarn install
yarn watch
  • To run the build process of the component library in watch mode
cd example
yarn install
yarn start
  • To run the example React app in development mode
  • This way the component library will re-build and the example React app will re-compile every time a file changes.

Walkthrough:

1. Create a new repository on Github

  • Initialize repo with a README
  • Add .gitignore with Node.js presets
  • Add an MIT License

2. Initialize package

  • Run yarn init

3. Install react and react-dom as peerDependencies

  • yarn add -D react react-dom @types/react
  • Set them as peerDependencies in package.json (use the version you want to support):
...
"peerDependencies": {
  "react": ">=16.8.0",
  "react-dom": ">=16.8.0"
}
...

4. Install and configure typescript

  • yarn add -D typescript
  • Add the following tsconfig.json file in root:
{
  "compilerOptions": {
    "outDir": "dist",
    "module": "esnext",
    "lib": ["es6", "dom", "esnext"],
    "target": "es5",
    "moduleResolution": "node",
    "jsx": "react",
    "sourceMap": true,
    "declaration": true,
    "declarationDir": "dist",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noEmit": true
  },
  "include": ["src/**/*"],
  "exclude": [
    "node_modules",
    "dist",
    "example/build",
    "src/**/*.stories.tsx",
    "src/**/*.test.tsx"
  ]
}

5. Create example component and install emotion

  • yarn add -D @emotion/core @emotion/styled
  • Add the following folder structure:
src/
  TestComponent/
    index.ts
    TestComponent.tsx
  index.ts
  • TestComponent.tsx:
/** @jsx jsx */
import { css, jsx } from '@emotion/core';

export interface TestComponentProps {
  color: 'darkgreen' | 'lightgreen';
}

const TestComponent: React.FC<TestComponentProps> = ({ color }) => (
  <div
    css={css`
      background-color: hotpink;
      &:hover {
        color: ${color};
      }
    `}
  >
    This is a {color} component with a hotpink background.
  </div>
);

export default TestComponent;
  • In src/index.ts, make sure to use named exports (Source):
export { default as TestComponent } from './TestComponent';

6. Install rollup and plugins

  • yarn add -D rollup rollup-plugin-typescript2 @rollup/plugin-commonjs @rollup/plugin-node-resolve rollup-plugin-peer-deps-external
  • Add the following rollup.config.js file in root:
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';

const packageJson = require('./package.json');

export default {
  input: 'src/index.ts',
  output: [
    {
      file: packageJson.main,
      format: 'cjs',
      sourcemap: true
    },
    {
      file: packageJson.module,
      format: 'esm',
      sourcemap: true
    }
  ],
  plugins: [
    peerDepsExternal(),
    resolve(),
    commonjs(),
    typescript({ useTsconfigDeclarationDir: true })
  ]
};
  • Add the following config variables and scripts in package.json:
...
"main": "dist/index.js",
"module": "dist/index.es.js",
"files": ["dist"],
"types": "dist/index.d.ts",
"scripts": {
  "build": "rollup -c",
  "watch": "rollup -cw"
},
...
  • Run yarn build

8. Generate example React app with create-react-app

  • npx create-react-app example --template typescript

9. Install the example app's dependencies in the parent package

  • yarn add -D @testing-library/jest-dom @testing-library/react @testing-library/user-event @types/jest @types/node @types/react-dom react-scripts
...
  "dependencies": {
    ...
    "react": "link:../node_modules/react",
    "react-dom": "link:../node_modules/react-dom",
    "react-scripts": "link:../node_modules/react-scripts",
    ...
  },
  ...
  "scripts": {
    "start": "node ../node_modules/react-scripts/bin/react-scripts.js start",
    ...
  },
  ...
  • Add a link to the development package in package.json:
  ...
  "dependencies": {
    ...
    "how-to-create-a-react-lib": "link:.."
  },
  ...

11. Use the development package in the example app

  • Run yarn install in the example app
  • Import the development package in the App.tsx:
import { TestComponent } from 'how-to-create-a-react-lib';

12. Develop the library and run the example app

yarn watch
cd example && yarn start
  • Change the code in the src/ folder

Deploying the example app to Github Pages (Source)

  • Add a homepage variable to both ./package.json and ./example/package.json (Optional)
    • Per CRA docs: we need to set the homepage variable if we use routing in our app
    • Use the base path where you will be serving the React app from. (More info)
...
"homepage": "http://andrewszucs.github.io/how-to-create-a-react-lib",
...
  • yarn add -D gh-pages

  • Add the following scripts to package.json in root:

    • I needed the clean script because I ran into this issue saying: fatal: A branch named 'gh-pages' already exists.
...
"scripts": {
  ...
  "clean" : "gh-pages-clean",
  "predeploy": "cd example && yarn install && yarn build",
  "deploy": "gh-pages -d example/build"
  ...
}
...
  • Run yarn deploy
    • This will create a new branch named gh-pages commit the example/build folder to that branch, push it to our Github repo and set it as the base for Github Pages.

Setting up ESLint, Prettier, Husky and Lint-staged for code quality

1. Add ESLint with the eslint-config-react-app preset

  • yarn add -D eslint-config-react-app @typescript-eslint/eslint-plugin@2.x @typescript-eslint/parser@2.x babel-eslint@10.x eslint@6.x eslint-plugin-flowtype@4.x eslint-plugin-import@2.x eslint-plugin-jsx-a11y@6.x eslint-plugin-react@7.x eslint-plugin-react-hooks@2.x
  • Create an .eslintrc.json file and add the following:
{
  "extends": ["react-app"],
  /** These are optional: */
  "rules": {
    /** Warn if console.log are added */
    "no-console": "warn",

    /** Enforcing rules about import ordering */
    "import/order": [
      "warn",
      {
        "groups": [["builtin", "external"], "internal", ["parent", "sibling", "index"]],
        "newlines-between": "always-and-inside-groups"
      }
    ]
  }
}

2. Add Prettier and integrate it with ESLint

  • yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
  • Add plugin:prettier/recommended to the extends array in eslintrc.json:
{
  "extends": [..., "plugin:prettier/recommended"],
  ...
}
  • Add a .prettierrc file to configure Prettier:
{
  "semi": true,
  "singleQuote": true,
  "jsxSingleQuote": true,
  "trailingComma": "none",
  "tabWidth": 2,
  "bracketSpacing": true,
  "jsxBracketSameLine": false,
  "arrowParens": "always",
  "printWidth": 90
}

3. Add husky and lint-staged to lint and format at each commit

  • yarn add -D husky lint-staged

  • Add a lint and a format script to package.json:

"scripts": {
  "lint": "eslint . --max-warnings=0",
  "format": "prettier --write \"**/*.+(js|jsx|ts|tsx|json|yml|yaml|css|md|vue)\"",
}
  • Add the husky and lint-staged config to package.json:
"husky": {
    "hooks": {
      "pre-commit": "yarn lint && yarn format"
    }
  },
"lint-staged": {
  "*.+(js|jsx|ts|tsx)": [
    "eslint --fix",
    "git add"
  ],
  "*.+(json|css|md)": [
    "prettier --write",
    "git add"
  ]
},

4. Add a script for running Typescript check

  • Add the following to scripts in package.json:
...
"scripts": {
  ...
  "typecheck": "tsc -p ./tsconfig.json",
  "typecheck:watch": "yarn typecheck --watch"
}
...

Adding Minification to the build process

  • yarn add -D rollup-plugin-uglify rollup-plugin-terser
  • Change rollup.config.js

Setting up semantic-release and Github Actions workflow

  • npx semantic-release-cli setup
  • Add this to package.json:
{
  "publishConfig": {
    "registry": "https://npm.pkg.github.com/",
    "pkgRoot": "build"
  }
}
  • Change back version in package.json:
"version":"1.0.0"
  • Add .releaserc.json to configure semantic-release:
{
  "branches": "master",
  "repositoryUrl": "https://github.com/andrewszucs/how-to-create-a-react-lib",
  "debug": "true",
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/npm",
    "@semantic-release/github",
    [
      "@semantic-release/changelog",
      {
        "changelogFile": "CHANGELOG.md"
      }
    ],
    [
      "@semantic-release/git",
      {
        "assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
        "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
      }
    ]
  ]
}
  • yarn add -D @semantic-release/git @semantic-release/changelog

Need to add a bot user with zero vulnerabilities to push

  • GITHUB_TOKEN is not defined
[11:08:54 PM] [semantic-release] › ✖  Failed step "prepare" of plugin "@semantic-release/git"
[11:08:54 PM] [semantic-release] › ✖  An error occurred while running semantic-release: Error: Command failed with exit code 1: git push --tags https://github.com/andrewszucs/how-to-create-a-react-lib HEAD:master
remote: error: GH006: Protected branch update failed for refs/heads/master.
remote: error: Required status check "Test code" is expected. At least 1 approving review is required by reviewers with write access.
To https://github.com/andrewszucs/how-to-create-a-react-lib
 ! [remote rejected] HEAD -> master (protected branch hook declined)
error: failed to push some refs to 'https://github.com/andrewszucs/how-to-create-a-react-lib'
    at makeError (/home/runner/work/how-to-create-a-react-lib/how-to-create-a-react-lib/node_modules/@semantic-release/git/node_modules/execa/lib/error.js:59:11)
    at handlePromise (/home/runner/work/how-to-create-a-react-lib/how-to-create-a-react-lib/node_modules/@semantic-release/git/node_modules/execa/index.js:114:26)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async push (/home/runner/work/how-to-create-a-react-lib/how-to-create-a-react-lib/node_modules/@semantic-release/git/lib/git.js:51:3)
    at async module.exports (/home/runner/work/how-to-create-a-react-lib/how-to-create-a-react-lib/node_modules/@semantic-release/git/lib/prepare.js:69:5)
    at async prepare (/home/runner/work/how-to-create-a-react-lib/how-to-create-a-react-lib/node_modules/@semantic-release/git/index.js:28:3)
    at async validator (/home/runner/work/how-to-create-a-react-lib/how-to-create-a-react-lib/node_modules/semantic-release/lib/plugins/normalize.js:34:24)
    at async /home/runner/work/how-to-create-a-react-lib/how-to-create-a-react-lib/node_modules/semantic-release/lib/plugins/pipeline.js:37:34
    at async /home/runner/work/how-to-create-a-react-lib/how-to-create-a-react-lib/node_modules/semantic-release/lib/plugins/pipeline.js:31:3
    at async Object.pluginsConf.<computed> [as prepare] (/home/runner/work/how-to-create-a-react-lib/how-to-create-a-react-lib/node_modules/semantic-release/lib/plugins/index.js:80:11) {
  shortMessage: 'Command failed with exit code 1: git push --tags https://github.com/andrewszucs/how-to-create-a-react-lib HEAD:master',
  command: 'git push --tags https://github.com/andrewszucs/how-to-create-a-react-lib HEAD:master',
  exitCode: 1,
  signal: undefined,
  signalDescription: undefined,
  stdout: '',
  stderr: 'remote: error: GH006: Protected branch update failed for refs/heads/master.        \n' +
    'remote: error: Required status check "Test code" is expected. At least 1 approving review is required by reviewers with write access.        \n' +
    'To https://github.com/andrewszucs/how-to-create-a-react-lib\n' +
    ' ! [remote rejected] HEAD -> master (protected branch hook declined)\n' +
    "error: failed to push some refs to 'https://github.com/andrewszucs/how-to-create-a-react-lib'",
  failed: true,
  timedOut: [secure],
  isCanceled: [secure],
  killed: [secure],
  pluginName: '@semantic-release/git'
}

Setting up commitizen

  • yarn add -D commitizen
  • npx commitizen init cz-conventional-changelog --yarn --dev --exact

FAQs

Package last updated on 28 Jul 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts