Tech Blog

Vite meets Storybook

Cover Image for Vite meets Storybook

Vite, React 18, Recoil, Storybook がある程度安定してきたので構築してみる

今回の環境

TL;DR

1. とりあえずサンプルプロジェクトを作る

vite react-ts

npm -y create vite@latest
? Project name: › vite-project
? Select a framework: › - Use arrow-keys. Return to submit.
    Vanilla
    Vue
❯   React
    Preact
    Lit
    Svelte
? Select a variant: › - Use arrow-keys. Return to submit.
    JavaScript
❯   TypeScript

Scaffolding project in ./vite-project...

Done. Now run:

  cd vite-project
  npm install
  npm run dev

cd vite-project
npm install

added 88 packages, and audited 89 packages in 494ms

8 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm run dev

> vite-project@0.0.0 dev
> vite


  VITE v3.1.3  ready in 387 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose

Vite + React

eslint

npm init -y @eslint/config
? How would you like to use ESLint? … 
  To check syntax only
  To check syntax and find problems
▸ To check syntax, find problems, and enforce code style
? What type of modules does your project use? … 
▸ JavaScript modules (import/export)
  CommonJS (require/exports)
  None of these
? Which framework does your project use? … 
▸ React
  Vue.js
  None of these
? Does your project use TypeScript? No / ‣ Yes
? Where does your code run? …  (Press <space> to select, <a> to toggle all, <i> to invert selection)
✔ Browser
  Node
? How would you like to define a style for your project? … 
▸ Use a popular style guide
  Answer questions about your style
? Which style guide do you want to follow? … 
▸ Standard: https://github.com/standard/eslint-config-standard-with-typescript
  XO: https://github.com/xojs/eslint-config-xo-typescript
? What format do you want your config file to be in? … 
  JavaScript
  YAML
▸ JSON

Checking peerDependencies of eslint-config-standard-with-typescript@latest
Local ESLint installation not found.
The config that you've selected requires the following dependencies:

eslint-plugin-react@latest eslint-config-standard-with-typescript@latest @typescript-eslint/eslint-plugin@^5.0.0 eslint@^8.0.1 eslint-plugin-import@^2.25.2 eslint-plugin-n@^15.0.0 eslint-plugin-promise@^6.0.0 typescript@*
? Would you like to install them now? No / ‣ Yes
? Which package manager do you want to use? … 
▸ npm
  yarn
  pnpm

added 199 packages, and audited 288 packages in 8s

85 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Successfully created .eslintrc.json file in ./vite-project
  // .eslintrc.json
  {
  ...
      "extends": [
          "plugin:react/recommended",
+         "plugin:react/jsx-runtime",
          "standard-with-typescript"
      ],
  ...
      "parserOptions": {
          "ecmaVersion": "latest",
+         "project": "./tsconfig.json",
          "sourceType": "module"
      },
  ...
      "rules": {
+         "@typescript-eslint/explicit-function-return-type": "off"
+     },
+     "settings": {
+         "react": {
+             "version": "detect"
+         }
      }
  }

  // src/vite-env.d.ts
+ // eslint-disable-next-line @typescript-eslint/triple-slash-reference
  /// <reference types="vite/client" />

prettier

npm install --save-dev eslint-config-prettier

added 1 package, and audited 289 packages in 546ms

85 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm install --save-dev --save-exact prettier

added 1 package, and audited 290 packages in 566ms

86 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
echo '{ "semi": false, "singleQuote": true }'> .prettierrc.json
  // .eslintrc.json
  {
  ...
      "extends": [
  ...
-         "standard-with-typescript"
+         "standard-with-typescript",
+         "prettier"
      ],
  ...
  }

recoil

npm install --save-dev eslint-plugin-react-hooks

added 1 package, and audited 291 packages in 850ms

86 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm install recoil

added 2 packages, and audited 293 packages in 914ms

86 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
  // .eslintrc.json
  {
  ...
      "extends": [
          "plugin:react/recommended",
          "plugin:react/jsx-runtime",
+         "plugin:react-hooks/recommended",
          "standard-with-typescript",
          "prettier"
      ],
  ...
      "plugins": [
-         "react"
+         "react",
+         "react-hooks"
      ],
      "rules": {
-         "@typescript-eslint/explicit-function-return-type": "off"
+         "@typescript-eslint/explicit-function-return-type": "off",
+         "react-hooks/rules-of-hooks": "error",
+         "react-hooks/exhaustive-deps": [
+             "warn", {
+                 "additionalHooks": "(useRecoilCallback|useRecoilTransaction_UNSTABLE)"
+             }
+         ]
      }
  }

  // src/recoil/counter/atom.ts
+ import { atom } from 'recoil'
+
+ const countState = atom({
+   key: 'countState', // unique ID (with respect to other atoms/selectors)
+   default: 0, // default value (aka initial value)
+ })
+
+ export default countState
+
  // src/recoil/counter/index.ts
+ import atom from './atom'
+
+ export default atom
+
  // src/components/Counter.tsx
+   import { useRecoilState } from 'recoil'
+ import reactLogo from '../assets/react.svg'
+ import countState from '../recoil/counter'
+
+ function Counter() {
+   const [count, setCount] = useRecoilState(countState)
+
+   return (
+     <>
+       <div>
+         <a href="https://vitejs.dev" target="_blank" rel="noreferrer">
+           <img src="/vite.svg" className="logo" alt="Vite logo" />
+         </a>
+         <a href="https://reactjs.org" target="_blank" rel="noreferrer">
+           <img src={reactLogo} className="logo react" alt="React logo" />
+         </a>
+       </div>
+       <h1>Vite + React + Recoil</h1>
+       <div className="card">
+         <button onClick={() => setCount((count) => count + 1)}>
+           count is {count}
+         </button>
+         <p>
+           Edit <code>src/App.tsx</code> and save to test HMR
+         </p>
+       </div>
+       <p className="read-the-docs">
+         Click on the Vite and React logos to learn more
+       </p>
+     </>
+   )
+ }
+
+ export default Counter
+
  // src/App.tsx
- import { useState } from 'react'
- import reactLogo from './assets/react.svg'
+ import { RecoilRoot } from 'recoil'
+ import Counter from './components/Counter'
  import './App.css'

  function App() {
-   const [count, setCount] = useState(0)
- 
    return (
-     <div className="App">
-       <div>
-         <a href="https://vitejs.dev" target="_blank" rel="noreferrer">
-           <img src="/vite.svg" className="logo" alt="Vite logo" />
-         </a>
-         <a href="https://reactjs.org" target="_blank" rel="noreferrer">
-           <img src={reactLogo} className="logo react" alt="React logo" />
-         </a>
-       </div>
-       <h1>Vite + React</h1>
-       <div className="card">
-         <button onClick={() => setCount((count) => count + 1)}>
-           count is {count}
-         </button>
-         <p>
-           Edit <code>src/App.tsx</code> and save to test HMR
-         </p>
-       </div>
-       <p className="read-the-docs">
-         Click on the Vite and React logos to learn more
-       </p>
-     </div>
+     <RecoilRoot>
+       <CharacterCounter />
+     </RecoilRoot>
    )
}

export default App

  <!--- index.html -->
  <!DOCTYPE html>
  <html lang="en">
    <head>
  ...
-     <title>Vite + React + TS</title>
+     <title>Vite + React + TS + Recoil</title>
    </head>
  ...
  </html>

tailwind

npm install -D tailwindcss postcss autoprefixer

added 36 packages, and audited 331 packages in 2s

93 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npx tailwindcss init -p

Created Tailwind CSS config file: tailwind.config.cjs
Created PostCSS config file: postcss.config.cjs
  // postcss.config.cjs
  /** @type {import('tailwindcss').Config} */
  module.exports = {
-   content: [],
+   content: [
+     "./index.html",
+     "./src/**/*.{js,ts,jsx,tsx}",
+   ],
  ...
  }

  /* src/App.css */
- #root {
-   max-width: 1280px;
-   margin: 0 auto;
-   padding: 2rem;
-   text-align: center;
- }

  ...
  <!--- index.html -->
  <!DOCTYPE html>
  <html lang="en">
  ...
    <body>
-   <div id="root"></div>
+   <div id="root" class="max-w-7xl my-0 mx-auto p-8 text-center"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

stylelint

npm install --save-dev stylelint stylelint-config-standard stylelint-config-prettier prettier-plugin-tailwindcss

added 94 packages, and audited 425 packages in 1s

107 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
  // .stylelintrc.json
+ {
+     "extends": [
+         "stylelint-config-standard",
+         "stylelint-config-prettier"
+     ],
+     "rules": {
+         "at-rule-no-unknown": [
+             true,
+             {
+                 "ignoreAtRules": [
+                     "tailwind",
+                     "apply",
+                     "variants",
+                     "responsive",
+                     "screen"
+                 ]
+             }
+         ],
+         "declaration-block-trailing-semicolon": null,
+         "no-descending-specificity": null
+     }
+ }
+
  // .prettierrc.json
- { "semi": false, "singleQuote": true }
+ {
+     "plugins": [
+         "prettier-plugin-tailwindcss"
+     ],
+     "semi": false,
+     "singleQuote": true,
+     "tailwindConfig": "./tailwind.config.cjs"
+ }

2. Storybook を入れる

storybook

npx -y sb init --builder @storybook/builder-vite

 storybook init - the simplest way to add a Storybook to your project. 

 • Detecting project type. ✓
 • Adding Storybook support to your "React" app

attention => Storybook now collects completely anonymous telemetry regarding usage.
This information is used to shape Storybook's roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://storybook.js.org/telemetry

added 1365 packages, and audited 1696 packages in 44s

266 packages are looking for funding
  run `npm fund` for details

22 high severity vulnerabilities

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.
. ✓
 • Preparing to install dependencies. ✓


 • Installing dependencies

 up to date, audited 1696 packages in 2s

266 packages are looking for funding
  run `npm fund` for details

22 high severity vulnerabilities

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.
. ✓
🔎 checking possible migrations..
🔎 found a 'eslintPlugin' migration:

╭──────────────────────────────────────────────────────────────────────╮
│                                                                      │
│   We've detected you are not using our eslint-plugin.                │
│                                                                      │
│   In order to have the best experience with Storybook and follow     │
│   best practices, we advise you to install                           │
│   eslint-plugin-storybook.                                           │
│                                                                      │
│   More info:                                                         │
│   https://github.com/storybookjs/eslint-plugin-storybook#readme      │
│                                                                      │
╰──────────────────────────────────────────────────────────────────────╯

? Do you want to run the 'eslintPlugin' migration on your project? › (y/N) yes
✅ Adding dependencies: eslint-plugin-storybook

added 4 packages, and audited 1700 packages in 11s

267 packages are looking for funding
  run `npm fund` for details

22 high severity vulnerabilities

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.
✅ Configuring eslint rules in .eslintrc.json
✅ Adding Storybook to extends list
✅ ran eslintPlugin migration
🔎 found a 'npm7' migration:

╭──────────────────────────────────────────────────────────────────────╮
│                                                                      │
│   We've detected you are running npm 8.15.0                          │
│   which has peer dependency semantics which Storybook is             │
│   incompatible with.                                                 │
│                                                                      │
│   In order to work with Storybook's package structure, you'll need   │
│   to run `npm` with the                                              │
│   `--legacy-peer-deps=true` flag. We can generate an `.npmrc`        │
│   which will do that automatically.                                  │
│                                                                      │
│   More info: https://github.com/storybookjs/storybook/issues/18298   │
│                                                                      │
╰──────────────────────────────────────────────────────────────────────╯
? Do you want to run the 'npm7' migration on your project? › (y/N) yes
✅ ran npm7 migration

✅ migration check successfully ran


To run your Storybook, type:

   npm run storybook

3. 表示してみる

npm run storybook 

╭─────────────────────────────────────────────────╮
│                                                 │
│   Storybook 6.5.10 for React started            │
│   8.6 s for manager and 4.1 s for preview       │
│                                                 │
│    Local:            http://localhost:6006/     │
│    On your network:  http://0.0.0.0:6006/       │
│                                                 │
╰─────────────────────────────────────────────────╯

Storybook

ここまで

参考にしたページ