ReactからWeb Components製のコンポーネントを利用してみよう
再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ 第7回
ダウンロード サンプルファイル (45.2 KB)
ReactとWeb Componentsを組み合わせる
これまでの連載で学んできた通り、Web Componentsはカスタム要素という形で、組み込みのHTML要素のように振る舞うことができます。Reactは、JSXというHTMLライクな記法の中に、HTML要素を書くことができます。それなら、リスト2のように、Web ComponentsをReactの中で使うこともできるのではないでしょうか。
import React from 'react';
import './my-custom-element'; // Web Componentsを読み込む
export default function App() {
return (
<div>
<my-custom-element></my-custom-element>
</div>
);
}
リスト2のようなシンプルな例であれば、動作する可能性は高いです。しかし、Props(属性)を渡し始めたり、カスタム要素の中で発生したイベントのコールバックをReactで受け取ろうとすると、うまくいかなくなります。これは、React DOMで管理しているのはDOMツリーまでであり、Shadow DOMとの連携については、まだサポートできていないのが原因です。
カスタム要素の属性をReact側の値と紐付ける方法については、React 19でのサポートを目指した取り組みがありますので、将来に期待しましょう。
@lit/react
実は、第3回と第4回で解説したLitというライブラリには、Reactとの連携をサポートするライブラリがあります。それが、@lit/reactです。このライブラリを使うと、ReactとWeb Componentsを比較的容易に組み合わせることができます。
NPM管理下のプロジェクトであれば、リスト3のようにしてインストールできます。
$ npm install lit @lit/react
おそらく lit は無くても動きますが、今回のサンプルの中ではLitを利用してカスタム要素を作っているので、一緒にインストールしておきます。
これで @lit/react を試す準備ができました。
Reactコンポーネントを作る
さて、まずは属性を持たないプレーンなカスタム要素をReactコンポーネントとして使えるようにしてみましょう。リスト4のカスタム要素を対象にします。
import {LitElement, html} from 'lit';
export class MyCounterElement extends LitElement {
static properties = {
count: { type: Number, reflect: true }
}
constructor() {
super();
this.count = 0;
}
render() {
return html^^
<p>カウント: ${this.count}</p>
<button @click="${() => this.increment()}">+1</button>
^^;
}
increment() {
this.count += 1;
}
}
customElements.define('my-counter-element', MyCounterElement);
ボタンを押すとカウントアップするだけの、シンプルなカスタム要素です。このカスタム要素をReactコンポーネントとして使えるようにするには、リスト5のようにします。
import React from 'react';
import { createComponent } from '@lit/react';
import { MyCounterElement } from './my-counter-element';
export const MyCounterElementComponent = createComponent({ // (1)
tagName: 'my-counter-element',
elementClass: MyCounterElement,
react: React,
})
(1)の createComponent は、カスタム要素をReactコンポーネントとして使えるようにするための関数です。リスト5の例では、次の3つのパラメータを渡しています。
-
tagName:カスタム要素のタグ名 -
elementClass:カスタム要素のクラス -
react:Reactライブラリ
react が少しわかりづらいので補足しますと、Reactにはpreactのような互換ライブラリが存在しており、@lit/react は互換ライブラリ向けにコンポーネントを作成することもできるのです。今回はReactを使うので、React を渡しています。
さて、リスト5で生み出したReactコンポーネントの MyCounterElementComponent を、実際に使ってみましょう。サンプルコードの src/App.jsx に配置してみました(リスト6)。
import { MyCounterElementComponent } from './simple-counter/MyCounterElementComponent';
import './App.css'
function App() {
return (
<>
<h1>ReactからWeb Componentsを使う</h1>
<h2>カスタム要素をコンポーネントとして配置する</h2>
<MyCounterElementComponent />{/ (1) /}
</>
)
}
export default App
(1)のように、Reactコンポーネントとして配置しました。ブラウザで http://localhost:5173/ を開くと、図3のように表示されます。
まるで普通のReactコンポーネントのようですね。ただ、ここまでなら直接 <my-counter-element> を配置した場合にも、同じ挙動になりますので、まだ @lit/react の恩恵を受けているとは言い難いです。
Reactからプロパティを流し込む
次に、Reactからpropsの形でプロパティを流し込んでみましょう。リスト7のように、名前を表示するだけの簡単なサンプルを用意しました。
import {LitElement, html} from 'lit';
export class MyNamePropertyElement extends LitElement {
static properties = {
person: {},
}
constructor() {
super();
this.person = { name: "名無し" };
}
render() {
return html^^
<p>こんにちは、${this.person.name}さん!</p>
^^;
}
}
customElements.define('my-name-property-element', MyNamePropertyElement);
ここで注目したいのが、プロパティとして定義した person に渡すのが、オブジェクトである、という点です。渡すのが文字列であれば、カスタム要素を直接書いても問題なく動くのですが、オブジェクト参照を渡すとなると、ReactとWeb Componentsの連携においては苦手な分野です。つまり、@lit/react がその苦手な分野をうまく吸収してくれるのか、という点が今回のポイントです。
では、リスト8のように、@lit/react を使ってReactコンポーネントを作成して、動かしてみましょう。
import React from 'react';
import { createComponent } from '@lit/react';
import { MyNamePropertyElement } from './name-properties-element';
const MyNamePropertyElementComponent = createComponent({ // (1)
tagName: 'my-name-property-element',
elementClass: MyNamePropertyElement,
react: React,
})
export const LitPropertiesAsReactProps = () => {
return (
<MyNamePropertyElementComponent person={{ name: 'リアクト' }} />{/ (2) /}
)
};
(1)の createComponent の設定内容は、リスト5と同じです。(2)では、person というプロパティに、オブジェクトを渡しています。ブラウザで http://localhost:5173/ を開くと、図4のように表示されます。
うまく表示されましたね。では、カスタム要素を直接書いた場合はどうなるのでしょうか。リスト9のように、returnの中を書き換えてみます。
// (略)
return (
<my-name-property-element person={{ name: 'リアクト' }} />
)
// (略)
ブラウザで http://localhost:5173/ を開くと、図5のように表示されます。
名前が表示されませんでした。カスタム要素の内部でログを取ってみると、person には文字列の [object Object] が入っており、オブジェクトを渡すことには失敗しています。ここを上手くやってくれるのが、@lit/react です。
まとめ
今回は、ReactとWeb Componentsの連携方法について、特にプロパティの話題を解説しました。次回は、コールバックイベントの取り扱いや、childrenとslotの関係について解説します。
この記事は参考になりましたか?
既に会員の方はを行ってください。
- 再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ連載記事一覧
-
- Web ComponentsのイベントやスロットをReactで扱う
- ReactからWeb Components製のコンポーネントを利用してみよう
- 【Web Components入門】Ionic Frameworkで使えるカスタム要素を学...
- この記事の著者
-
WINGSプロジェクト 中川 幸哉(ナカガワ ユキヤ)
<WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
-
山田 祥寛(ヤマダ ヨシヒロ)
静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
