Viteで作成したReactのコンポーネントを従来型Webページに埋め込む手順(Viteライブラリモード)

Reactをコンポーネント単位でexportして、既存のページにコンポーネント単位で埋め込む方法をまとめました

![image-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276579/d68f1941-660f-bfb4-c924-6149af3b2220.png)

## はじめに

1年以上前、従来型のWebページにReactのコンポーネントを埋め込む方法を調べたことがありまます

[従来型Webページの一部としてReactを埋め込む方法](https://qiita.com/murasuke/items/0257c410db2200f07e6e)

この時は`create-react-app`を使って環境を作成後、`rollup.js`でビルドをしましたが、少々古い情報になってきました（rollup.jsのビルド設定も大変でした）

そこで`Vite`を使って環境を作成後、(Viteの)[ライブラリモード](https://ja.vite.dev/guide/build.html#library-mode)を利用してビルドを行う手順をまとめました（設定も少なくとても簡単です）

## 環境について

* node.jsはインストール済の想定(LTSで確認)
* bashを利用(git-bashを使ってます)

## 手順について

1. ViteでReactの環境を作成する
1. 簡単なコンポーネント(Counter)を作成する
1. コンポーネントが動作することをVite環境で確認する
1. ライブラリモードの設定を行い、コンポーネントをビルドしてjsファイルを作成する
1. WebページにReactのコンポーネントを埋め込んで、動作することを確認する


## 1. ViteでReactの環境を作成する

適当なディレクトリでReact＋TypeScriptの環境を作成します

```
$ npm create vite@latest vite-react-component-lib -- --template react-swc-ts
$ cd vite-react-component-lib
$ npm install
```

## 2. 簡単なコンポーネント(Counter)を作成する

```tsx:src/Counter.tsx
import React, { useState } from 'react';
import './Counter.css'; // Import the CSS file for styling

interface CounterProps {
  initialCount?: number;
}

const Counter: React.FC<CounterProps> = ({ initialCount = 0 }) => {
  const [count, setCount] = useState(initialCount);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div className="counter-container">
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

export default Counter;
```

```css:src/Counter.css
.counter-container {
    border: 1px solid black;
    border-radius: 8px;
    padding: 16px;
    display: inline-block;
    text-align: center;
}

button {
    margin-top: 8px;
    padding: 8px 16px;
    border: none;
    background-color: #007bff;
    color: white;
    border-radius: 4px;
    cursor: pointer;
}

button:hover {
    background-color: #0056b3;
}
```


### 3. コンポーネントが動作することをVite環境で確認する

`App.tsx`ファイルで`Counter`コンポーネントを表示します

```tsx:src/App.tsx
import Counter from './Counter';

function App() {
  return (
    <>
      <Counter />
    </>
  )
}

export default App;
```

あと`src/main.tsx`のスタイルをコメントアウトしておきます(背景色が変わってしまうので)
```diff
-import './index.css'
+//import './index.css'
```

Viteで開発サーバ―を起動して、`Counterコンポーネント`が動作することを確認します
```
$ npm run dev

> vite-react-component-lib@0.0.0 dev
> vite

  VITE v6.0.6  ready in 283 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
```

カウンターが正常に動作することを確認します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276579/e3b21d5b-8fcf-2aaa-1010-2096bf33dbb7.png)



### 4. ライブラリモードの設定を行い、コンポーネントをビルドしてjsファイルを作成する

まず、ビルドのエントリーポイントとなるファイルを作成し、必要なコンポーネント、ライブラリをエクスポートします

Webページに埋め込む際、Reactも必要なためexportします

```ts:src/lib.ts
export { default as React } from 'react';
export { default as ReactDOM } from 'react-dom';
export { default as ReactDOMClient } from 'react-dom/client';

// Component
export { default as Counter} from './Counter';
```


`vite.config.ts`にビルド設定`build: {～}`を追加します

設定項目

* entry: ビルドを行うためのエントリポイントとなるファイルを指定
* filename: 生成するファイルのファイル名を指定
* formats: 生成するモジュール形式を指定(デフォルト設定ではumd形式も作成されるのでesのみに変更)
* define: 実行時エラーが発生するため、定義を固定値に書き換える

```js:vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
+  build: {
+   lib: {
+       // https://ja.vitejs.dev/config/build-options.html#build-lib
+       entry: 'src/lib.ts', // エントリポイント
+       fileName: 'bundle', // 生成するファイルのファイル名を指定
+       formats: ['es'], // 生成するモジュール形式を指定。デフォルト['es', 'umd']
+     },
+ },
+ define: {
+   'process.env.NODE_ENV': JSON.stringify('production'), // 実行時エラーが発生するため、定義を固定値に書き換え
+ },
})
```
ビルドを行います

```bash
$ npm run build

> vite-react-component-lib@0.0.0 build
> tsc -b && vite build

vite v6.0.6 building for production...
✓ 26 modules transformed.
dist/bundle.css    0.27 kB │ gzip:  0.19 kB
dist/bundle.js   213.12 kB │ gzip: 53.02 kB
✓ built in 703ms
```

`dist/bundle.js`と`dist/bundle.css`が作成されます


### 5. WebページにReactのコンポーネントを埋め込んで、動作することを確認する

動作確認のためhtmlファイルを作成します。webrootフォルダをルート直下に作成しその中に保存します

```
$ mkdir webroot
$ touch webroot/counter-test.html
```

&lt;div id="root"&gt;&lt;/div&gt;の部分に、Reactコンポーネント(Counter)を描画するコードを記載します

```html:counter-test.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <link rel="stylesheet" crossorigin href="/dist/bundle.css">
    <script type="module">
      import {ReactDOMClient, React, Counter} from '/dist/bundle.js';
      const container = document.getElementById('root');
      const root = ReactDOMClient.createRoot(container);
      root.render(React.createElement(Counter));
    </script>
  </head>
  <body>
    <h2>Webページの一部にReactコンポーネントを表示する</h2>
    <div id="root"></div>
  </body>
</html>
```

動作確認のためにWebサーバーを起動します

```
$ npx http-server .
```

`/webroot/counter-test.html`をブラウザで開くと、Counterコンポーネントが表示されていることを確認できます

![image-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276579/d68f1941-660f-bfb4-c924-6149af3b2220.png)