Reactユーザー向けLit入門
React に慣れている開発者が、初めてLitに触れるときのための入門記事です。この記事では、React との違いを意識しながら、Lit の基本的な使い方を解説します。
Litとは
Litは Google が開発している軽量なライブラリで、Web Components を効率的に構築するためのライブラリです。Reactのようなライブラリではなく、純粋な Web 標準の上に構築された、軽くて高速な UI ライブラリです。
Web Componentsとは
Web Components は、ブラウザが標準でサポートしているコンポーネント技術の総称です。以下の3つの技術で構成されます:
-
Custom Elements:
<my-button>
のような独自要素を定義可能 - Shadow DOM:スタイルとDOMをカプセル化(外部CSSの影響を受けない)
- HTML Templates:再利用可能なHTMLのスニペットを定義
これらを組み合わせることで、Reactなどの特定のライブラリに依存しない再利用可能なUI部品を作成できます。
Web Componentsについては、以下の記事にて大変丁寧に紹介されています
Litのメリット / 主な用途
メリット
- 再利用性:Web標準ベースなので、どのフレームワークとも共存可能
- 高速な描画:差分更新による効率的なDOM更新
- TypeScriptサポートが強力
主な用途
- ライブラリやフレームワークに依存しない UIコンポーネントライブラリの構築
- Litで用意したコンポーネントをReactから読み込めるようにすることもできるため、共通的なコンポーネントとしてライブラリ・フレームワークを跨いで参照可能にできる
- デザインシステムとの親和性が高い(StorybookやCode Connectに対応している)
一方で、LitはReactで構築するようなダイナミックなSPAアプリには向いていません。これは、Litは状態管理やルーティングなどの「アプリ全体を構成する機能」がReactなどに比べて弱いためです。アプリケーションの構築ではなく、それに利用するUIコンポーネントライブラリの構築に向いています。
Litが採用されている事例
- Google Material Web
- Adobe Spectrum Web Component
環境構築
Litの環境構築を行います。やり方はいくつかありますが、今回は比較的容易に行うことができるViteを利用した方法をとります。
まず、任意のディレクトリにて以下を実行します。今回はTypescriptを利用して構築します。
npm create vite@latest lit-ts-sample -- --template lit-ts
完了すると、以下のような構成のテンプレートが生成されます(一部抜粋)
.
├── node_modules
├── public
├── package.json
├── tsconfig.json
├── index.html
└── src
├── assets
├── my-element.ts
├── index.css
└── vite-env.d.ts
src/my-element.ts
が、サンプルで用意されているLitベースのコンポーネントです。内部的には、カウンターのコンポーネントが実装されています。
そちらを、index.html
で読み込み、描画していることが確認できます。
<my-element>
<h1>Vite + Lit</h1>
</my-element>
次に以下を実行し、ローカルサーバーを立ち上げます。
npm install
npm run dev
以下のような画面が開いたら完了です。簡単ですね。
基本構文
それでは早速基本的な構文を紹介します。Reactと比較しながら、Buttonコンポーネントを作る想定で進めます。
コンポーネントの定義
Litではクラスを用いてコンポーネント定義を行います。
React の例
export const MyButton = () => {
return ...
}
Lit の例
import { LitElement, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
@customElement('my-button')
class MyButton extends LitElement {
render() {
return ...
}
}
-
@customElement('my-button')
により<my-button>
タグを独自のタグとして定義し、利用可能にする
テンプレート構文
Litでは、html
テンプレートタグを用いて表示するHTMLを表現できます。ReactのJSXに相当します
React (JSX)
return <button className="primary">{children}</button>;
html
テンプレートタグ)
Lit (//Slotで表現する場合
return html`<button class="primary">
<slot></slot>
</button>`;
//Propertyとして受け取ってそれを表現する場合
return html`<button class="primary">
${this.label}
</button>`;
- JSX では
className
、Lit ではclass
-
${}
を使って文字列や値を埋め込む -
html
は Tagged Template Literal(特殊なテンプレート文字列) - タグ利用時に囲まれた部分を描画する方法として、
slot
タグを利用する-
<my-button>OK</my-button>
のOK
の部分が描画される
-
Propsの渡し方
Lit の @property()
デコレータは、コンポーネントのプロパティを外部から渡せるようにするための仕組みです。React でいう「props」に相当します。
React
// 親
<MyButton size="large">OK<MyButton>
// 子
type Props = {
size: "small" | "medium" | "large"
children: ReactNode;
}
export const MyButton:FC<Props> = ({size = "medium" , children}) => {
return <button class={size}>{children}</button>;
}
Lit
// 親
<my-button size="large">OK</my-button>
// 子
@customElement('my-button')
export class MyButton extends LitElement {
@property({ type: String }) size: 'small' | 'medium' | 'large' = 'medium';
render() {
return html`
<button class="${this.size}">
<slot></slot>
</button>
`;
}
}
-
@property
デコレーターを利用して、コンポーネントが受け取る値を制御する
@property
の基本補足
@property({ type: String }) size: 'small' | 'medium' | 'large' = 'medium';
- 主なオプション
オプション名 | 説明 |
---|---|
type |
属性値を JS の型に変換(String, Number, Boolean, Object など) |
attribute |
属性名をカスタマイズできる(デフォルトはプロパティ名と同じ) |
reflect |
プロパティの変更を HTML 属性にも反映する(双方向) |
- 例:
disabled
を reflect させたいとき
@property({ type: Boolean, reflect: true }) disabled = false;
また、このdisabledをHTMLタグに付与する際、以下のように記述します
render() {
return html`
<button class="${this.size}" ?disabled=${this.disabled}>
<slot></slot>
</button>
`;
}
?disabled
という形式で指定していますが、この ?
はBoolean属性であることをLitに伝える記法です。例えば、this.disabled
がtrueの場合は <button disabled>
として描画され、falseの場合は <button>
のように描画されます。該当のBoolean属性を削除するような役割を担います。
State管理
内部で状態を持ちたい場合は @state()
を使います。React の useState
に相当します。
React の場合
export const MyButton:FC<Props> = ({children}) => {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{children}</button>;
}
Lit の例
@customElement('my-button')
export class MyButton extends LitElement {
@state() count = 0;
render() {
html`
<button @click=${() => this.count++}>
<slot></slot>
</button>
`;
}
}
-
@state()
は「内部専用の状態」で、外部から渡せない - 状態が変化すると自動で
render()
が再実行されて UI 更新される
また@state
の話とはズレますが、Litでイベントハンドラを利用する場合は、@{event name}
のような形で指定します。
Reactで、onClick
と指定する部分がLitだと@click
になります。
スタイリング(CSSの使い方)
ReactではCSS ModuleやStyled Componentなど幅広いスタイル方法があります。Litではコンポーネントに閉じたCSSを記述するためのcss
メソッドが提供されています
import { css } from 'lit';
static styles = css`
button {
background: blue;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: darkblue;
}
`;
- Lit の
styles
はコンポーネント内で完結 - Shadow DOM によってスタイルがスコープされる
- 外部 CSS を使う場合は
unsafeCSS()
も使用可能
そして、これらの構文を組み合わせて、以下のButtonコンポーネントを作ることができます
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
@customElement('my-button')
export class MyButton extends LitElement {
@property({ type: String }) size: 'small' | 'medium' | 'large' = 'medium';
@property({ type: Boolean, reflect: true }) disabled = false;
static styles = css`
button {
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
}
button.small {
padding: 4px 8px;
font-size: 12px;
}
button.medium {
padding: 8px 16px;
font-size: 14px;
}
button.large {
padding: 12px 24px;
font-size: 16px;
}
button:disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
`;
render() {
return html`
<button class="${this.size}" ?disabled=${this.disabled}>
<slot></slot>
</button>
`;
}
}
利用例
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Lit + TS</title>
<script type="module" src="/src/my-button.ts"></script>
</head>
<body>
<my-button>OK</my-button>
</body>
</html>
ライフサイクル
LitにもReactのようなライフサイクルごとのメソッドがあります。
Reactとの比較表
よく使うライフサイクルのメソッドをピックアップして紹介します
タイミング | React | Lit |
---|---|---|
renderの処理 | コンポーネント直下に記述 | render() |
初回描画後の処理 | useEffect(() => {...}, []) |
firstUpdated() |
描画後に副作用を実行 | useEffect(() => {...}, [deps]) |
updated(changedProps) |
例:updated()の使い方
updated(changedProps: Map<string, unknown>) {
if (changedProps.has('value')) {
console.log('valueが更新されました:', this.value);
}
}
パフォーマンス
Litの大きな特徴の一つが、仮想DOMを使わずに高速な描画ができることです。
これはLit のテンプレート システム「lit-html」を利用し、動的部分の差分を直接DOMに反映しているためです。
この技術により、Reactよりも高いパフォーマンスでの描画が実現できることが公式ドキュメントに記載されています
Lit のテンプレート システム「lit-html」を React の VDOM と比較した公開ベンチマークでは、lit-html は React と比べ最低でも 8~10% 速く、一般的なユースケースでは 50% 以上速いという結果が出ています。
LitElement(Lit コンポーネントのベースクラス)により lit-html に最小限のオーバーヘッドが追加されますが、メモリ使用量、インタラクション、起動時間などのコンポーネントの機能を比較すると React のパフォーマンスを 16~30% 上回ります。
まとめ
LitはReactとは思想が異なりますが、再利用性や軽量さを活かして、既存システムへのUI追加やデザインシステムに最適です。今回紹介できなかった部分などより詳細を知りたい方は、公式ドキュメントにて解説されていますので、そちらをご覧ください

NCDC株式会社( ncdc.co.jp/ )のエンジニアチームです。 募集中のエンジニアのポジションや、採用している技術スタックの紹介などはこちら( github.com/ncdcdev/recruitment )をご覧ください! ※エンジニア以外も記事を投稿することがあります
Discussion