12
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vite + React + Tailwind CSSでUIライブラリを作ってみる

Last updated at Posted at 2023-12-04

この記事はファンタアドベントカレンダー2023の5日目です。

現在参加中のプロジェクトでUIライブラリを作成してnpmで配信してみたい!という話になりました。

この記事では、その環境構築手順を共有します

なお本記事で掲載した一部コードを含む全体をGitHubレポジトリで公開しています。

想定読者

  • React * Vite * Tailwind CSSを使ったUIライブラリを実装したい方
  • 自作のコンポーネントをパッケージで配信してみたい方

技術スタック

本記事で使用する主な技術スタックは以下の通りです

  • React
  • Vite
  • TypeScript
  • Tailwind CSS

1. 初期構築

以下のコマンドでViteのreact-tsテンプレートを用いてプロジェクトを作成します

yarn create vite . --template react-ts

パッケージをインストールする

(yarn.lockが無いよ!とエラーが出るため空のファイルを作成しています)

touch yarn.lock
yarn

2. Tailwind CSSの導入

スタイリングはtailwindを使いたいのでパッケージをインストールします

yarn add -D tailwindcss postcss autoprefixer

以下のコマンドでプロジェクトにtailwindを導入します

yarn tailwindcss init -p

この時生成されるpostcss.config.jsの内容はvite.config.tsに移行可能なので移してしまいます

tailwind.config.jsを書き換えます。

この際にtw_ など任意のプレフィックスをつけることができます。
必要に応じて設定してください。

型がある方が補完効いたり、エディタでエラー表示してくれたり何かと安心なので .ts に変えてしまいました。

import type { Config } from "tailwindcss";
export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
} satisfies Config;

プロジェクト初期構築時に生成されたsrc/index.css でtailwindのcssを読み込みます

詳しくは公式ドキュメントを参照してください

src/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

3. ビルド設定周りを修正

vite.config.ts

2でpostcss.config.jsを削除したのでこちらに記述します

css: {
  postcss: {
		plugins: [tailwindcss, autoprefixer]
	}
}

あとはビルド周りも追加します

build: {
  lib: {
    entry: resolve(__dirname, "src/index.ts"),
    name: formattedName,
    formats: ["es", "umd"],
    fileName: (format) => `${formattedName}.${format}.js`,
  },
}

最終的に以下のようなコードになりました

/// <reference types="vitest">
import { resolve } from "path";
import react from "@vitejs/plugin-react";
import autoprefixer from "autoprefixer";
import tailwindcss from "tailwindcss";
import { defineConfig } from "vite";
import dts from "vite-plugin-dts";
import { name } from "./package.json";

const formattedName = name.match(/[^/]+$/)?.[0] ?? name;

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    dts({
      insertTypesEntry: true,
    }),
  ],
  css: {
    postcss: {
      plugins: [tailwindcss, autoprefixer],
    },
  },
  build: {
    lib: {
      entry: resolve(__dirname, "src/index.ts"),
      name: formattedName,
      formats: ["es", "umd"],
      fileName: (format) => `${formattedName}.${format}.js`,
    },
    rollupOptions: {
      external: ["react", "react/jsx-runtime", "react-dom", "tailwindcss"],
      output: {
        globals: {
          react: "React",
          "react/jsx-runtime": "react/jsx-runtime",
          "react-dom": "ReactDOM",
          tailwindcss: "tailwindcss",
        },
      },
    },
  },
});

この時 path が読み込めずエラーになる場合は以下のコマンドで型情報を追加してあげると解決します

yarn add -D @types/node

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "allowSyntheticDefaultImports": true,
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    "allowJs": false,
    "esModuleInterop": false,
    "moduleResolution": "Node"
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

tsconfig.node.json

{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts", "package.json"]
}

7. package.jsonの修正

以下のエントリポイント周りの設定をpackage.jsonに追加します

"main": "./dist/yhi-component-lib.umd.js",
"module": "./dist/yhi-component-lib.es.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"exports": {
  ".": {
    "types": "./dist/index.d.ts",
    "import": "./dist/yhi-component-lib.es.js",
    "require": "./dist/yhi-component-lib.umd.js"
  },
  "./dist/style.css": "./dist/style.css"
},
"files": [
  "dist"
],

scripts にビルドコマンドを追加します

"scripts": {
  // ...
	// ↓追加
	"build": "tsc && vite build",
  // ...
},

コンポーネントの追加

src下にコンポーネントを作成します

コンポーネントは1つのディレクトリにまとめたかったため作成したcomponents ディレクトリ下にまとめました

src/components/Button/Button.tsx

import { ButtonProps } from "./type";

export const Button = (props: ButtonProps) => {
  return <button {...props} className="bg-blue-500 hover:bg-blue-600 px-5 py-2 rounded-lg text-sm text-white " />;
};

src/components/Button/type.ts

import { ComponentProps } from "react";

export type ButtonProps = Pick<ComponentProps<"button">, "children" | "onClick">;

src/components/Button/index.ts

export * from "./Button";

src下でCSSと作成したコンポーネントをexportします

src/index.ts

import "./index.css";
export * from "./components";

8. ビルド

以下のコマンドでパッケージのビルドを実行します

yarn build

dist フォルダ下にファイルが生成されていれば成功です

9. パッケージ公開

npmへのログインが完了していなければ認証情報を入れておきましょう

yarn npm login

以下のコマンドでnpmへパッケージを公開できます

npm publish

10. 動作確認

作成したコンポーネントは以下のように使用することができます

import { Button } from "yhi-component-lib"

export const SamplePage = () => {
  return (
    <div>
      <Button>ボタンA</Button>
      <Button>ボタンB</Button>
      <Button>ボタンC</Button>
    </div>
  )
}

この際、ビルド時に出力したCSSを読み込むのを忘れないようにしましょう(自分は忘れてスタイルが当たらず1日潰しました)

以下のように出力されたCSSをインポートする文を全体で読み込むスタイルに含めておくと良いと思います

@import "yhi-component-lib/dist/style.css"

おわりに

実際にはこの記事の内容に加えて以下のようなこともやっているので、この辺りもどこかで書けたらいいなと思います

  • Storybookの導入
  • Vitestでテスト実行
  • GitHub Actions上でnpmのパッケージ公開自動化
  • Hygenを導入してコンポーネント作成をある程度自動化

明日は筋肉系デザイナーの朝妻さんが担当してくれます!お楽しみに!!!!

参考記事・レポジトリ

12
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?