Preact で埋め込みウィジェット配布:他サイトに設置できる軽量 UI の作り方
自社サービスのウィジェットを他のサイトに簡単に埋め込んでもらいたい、そんなニーズはありませんか。チャットボット、SNS シェアボタン、レビューウィジェットなど、様々なサービスが埋め込み型の UI を提供しています。Preact を使えば、わずか 3KB という軽量さで、React 風の開発体験を保ちながら、どんな Web サイトにも設置できるウィジェットを作成できるのです。
この記事では、Preact を使った埋め込みウィジェットの作り方を、初心者の方にもわかりやすく解説していきます。実装の基礎から配布方法まで、実践的な知識が身につくでしょう。
背景
Web 埋め込みウィジェットとは
Web 埋め込みウィジェットは、外部サイトに設置できる独立した UI コンポーネントです。利用者は自分の Web サイトに数行の HTML コードを追加するだけで、高機能な UI を組み込めます。
代表的な例として、以下のようなサービスがあります。
| # | サービス例 | 用途 |
|---|---|---|
| 1 | Google Analytics | アクセス解析 |
| 2 | Intercom | チャットサポート |
| 3 | Disqus | コメントシステム |
| 4 | AddThis | SNS シェアボタン |
| 5 | Stripe Elements | 決済フォーム |
これらのウィジェットは、設置先のサイトのスタイルや JavaScript と干渉せず、独立して動作する必要があります。
なぜ Preact が最適なのか
React、Vue、Angular など、多くのフレームワークが存在する中で、Preact が埋め込みウィジェットに適している理由を見ていきましょう。
軽量性が最大の魅力です。ウィジェットは外部サイトに読み込まれるため、ファイルサイズが大きいとページの読み込み速度に悪影響を与えてしまいます。Preact は本体がわずか 3KB と非常に軽量で、React の約 10 分の 1 のサイズです。
以下の図で、主要フレームワークと Preact のサイズ比較を示します。
mermaidflowchart TB
subgraph frameworks["フレームワークサイズ比較"]
react["React<br/>約40KB"]
vue["Vue 3<br/>約34KB"]
preact["Preact<br/>約3KB"]
vanilla["Vanilla JS<br/>約0KB"]
end
subgraph impact["ページ読み込みへの影響"]
large["大きいサイズ<br/>読み込み遅延"]
medium["中程度のサイズ<br/>軽微な影響"]
small["小さいサイズ<br/>影響なし"]
end
react --> large
vue --> medium
preact --> small
vanilla --> small
style preact fill:#90EE90
style small fill:#90EE90
Preact は軽量でありながら、React とほぼ同じ API を提供しているため、React 開発者であればすぐに習得できます。JSX や Hooks も使えるので、モダンな開発体験を損なうことはありません。
課題
埋め込みウィジェット開発の難しさ
埋め込みウィジェットを作成する際には、通常の Web アプリ開発とは異なる課題に直面します。これらを理解しておくことで、適切な設計ができるようになるでしょう。
スタイルの独立性確保
設置先のサイトには様々な CSS が適用されています。グローバルな CSS ルールがウィジェットに影響を与えてしまうと、意図しない見た目になってしまいます。
逆に、ウィジェットのスタイルが設置先のサイトに影響を与えることも避けなければなりません。
JavaScript の競合回避
設置先のサイトでは、jQuery やその他のライブラリが使われている可能性があります。グローバル変数の競合やイベントリスナーの干渉を防ぐ必要があるでしょう。
また、複数のバージョンの React が存在する環境でも動作する必要があります。
パフォーマンスへの配慮
ウィジェットの読み込みで、設置先のページ表示が遅くなってはいけません。非同期読み込みやコード分割など、最適化技術が求められます。
以下の図で、ウィジェット実装時の主な課題を整理します。
mermaidflowchart TD
widget["埋め込みウィジェット"]
widget --> style_issue["スタイル課題"]
widget --> js_issue["JavaScript課題"]
widget --> perf_issue["パフォーマンス課題"]
style_issue --> style1["グローバルCSS<br/>の影響を受ける"]
style_issue --> style2["設置先サイトの<br/>スタイルを破壊"]
js_issue --> js1["ライブラリの<br/>競合"]
js_issue --> js2["グローバル変数<br/>の衝突"]
perf_issue --> perf1["読み込み時間<br/>の増加"]
perf_issue --> perf2["メモリ使用量<br/>の増加"]
style perf_issue fill:#FFB6C1
style js_issue fill:#FFB6C1
style style_issue fill:#FFB6C1
初期化とクリーンアップ
ウィジェットはページ読み込み後に動的に初期化され、場合によっては動的に削除されることもあります。適切なライフサイクル管理が必要でしょう。
解決策
Preact による埋め込みウィジェットの設計方針
上記の課題を解決するため、以下の設計方針でウィジェットを構築していきます。
Shadow DOM でのカプセル化
Shadow DOM を使用することで、スタイルと DOM を完全に独立させられます。設置先の CSS の影響を受けず、逆に影響も与えません。
名前空間の活用
グローバルスコープを汚染しないよう、すべての機能を 1 つの名前空間内に収める設計にします。
遅延読み込みの実装
ウィジェットの初期化を非同期で行い、ページ表示をブロックしないようにします。
以下の図で、Preact ウィジェットのアーキテクチャを示します。
mermaidflowchart TB
subgraph host["設置先サイト"]
html["HTML<br/>【script タグ】"]
dom["既存のDOM"]
end
html -->|非同期読み込み| loader["ローダースクリプト"]
loader -->|初期化| widget["ウィジェット本体"]
subgraph widget_internal["ウィジェット内部"]
shadow["Shadow DOM<br/>【独立した空間】"]
preact["Preact<br/>コンポーネント"]
styles["スコープ付き<br/>CSS"]
end
widget --> shadow
shadow --> preact
shadow --> styles
dom -.独立.-> shadow
style shadow fill:#E6F3FF
style widget_internal fill:#F0F0F0
この設計により、スタイルの独立性、JavaScript の競合回避、パフォーマンスの最適化をすべて実現できます。
実装の全体像
ウィジェット実装は、以下の 3 つのパーツで構成されます。
| # | パーツ名 | 役割 |
|---|---|---|
| 1 | ローダースクリプト | ウィジェットの初期化を担当 |
| 2 | Preact コンポーネント | UI ロジックの実装 |
| 3 | ビルド設定 | 配布可能な形式への変換 |
それぞれを順番に実装していきましょう。
具体例
プロジェクトのセットアップ
まずは、Preact プロジェクトの初期化から始めます。Yarn を使ってプロジェクトを作成しましょう。
必要なパッケージのインストール
新しいディレクトリを作成し、必要なパッケージをインストールします。
bashmkdir preact-widget
cd preact-widget
yarn init -y
次に、Preact と開発に必要なツールをインストールします。
bashyarn add preact
yarn add -D vite @preact/preset-vite
ここでは、高速なビルドツールである Vite を使用します。Vite は開発サーバーの起動が速く、HMR(Hot Module Replacement)も優れているため、開発効率が向上するでしょう。
プロジェクト構造の作成
以下のようなディレクトリ構造でファイルを配置します。
bashpreact-widget/
├── src/
│ ├── widget.tsx # Preactコンポーネント
│ ├── loader.ts # ローダースクリプト
│ └── styles.css # ウィジェットのスタイル
├── package.json
└── vite.config.ts # Viteの設定
Preact コンポーネントの実装
それでは、実際のウィジェットコンポーネントを作成していきます。シンプルなチャットウィジェットを例に解説します。
基本的なコンポーネント構造
src/widget.tsx ファイルを作成し、基本となるコンポーネントを実装します。
typescriptimport { h } from 'preact';
import { useState } from 'preact/hooks';
まず必要なモジュールをインポートします。h 関数は JSX を変換するために必要で、useState はコンポーネントの状態管理に使用します。
typescriptinterface WidgetProps {
position?: 'bottom-right' | 'bottom-left';
primaryColor?: string;
}
ウィジェットの設定を Props として受け取れるよう、TypeScript のインターフェースを定義します。これにより、利用者が表示位置や色をカスタマイズできるようになります。
typescriptexport function ChatWidget({
position = 'bottom-right',
primaryColor = '#0066FF',
}: WidgetProps) {
// ウィジェットの開閉状態を管理
const [isOpen, setIsOpen] = useState(false);
// メッセージの入力内容を管理
const [message, setMessage] = useState('');
return (
<div class={`widget-container ${position}`}>
{/* コンテンツは次のステップで実装 */}
</div>
);
}
状態管理のための Hooks を設定します。isOpen でチャットウィンドウの開閉、message で入力中のテキストを管理しましょう。
UI の実装
チャットウィジェットの UI を段階的に構築していきます。
typescript// トグルボタンの実装
const toggleButton = (
<button
class='widget-toggle'
onClick={() => setIsOpen(!isOpen)}
style={{ backgroundColor: primaryColor }}
aria-label='チャットを開く'
>
{isOpen ? '✕' : '💬'}
</button>
);
ウィジェットを開閉するためのボタンを作成します。アクセシビリティのため、aria-label 属性も設定しています。
typescript// チャットウィンドウの実装
const chatWindow = isOpen && (
<div class='widget-window'>
<div
class='widget-header'
style={{ backgroundColor: primaryColor }}
>
<h3>チャットサポート</h3>
</div>
<div class='widget-body'>
<div class='widget-messages'>
<div class='message message-bot'>
こんにちは!何かお困りですか?
</div>
</div>
</div>
<div class='widget-footer'>
{/* フッターは次のステップで実装 */}
</div>
</div>
);
チャットウィンドウの基本構造を作成します。ヘッダー、メッセージ表示エリア、入力エリアの 3 つのセクションで構成されます。
typescript// 入力フォームの実装
const inputForm = (
<form
onSubmit={(e) => {
e.preventDefault();
console.log('送信:', message);
setMessage('');
}}
>
<input
type='text'
value={message}
onInput={(e) => setMessage(e.currentTarget.value)}
placeholder='メッセージを入力...'
class='widget-input'
/>
<button type='submit' class='widget-send'>
送信
</button>
</form>
);
メッセージ入力フォームを実装します。送信時の処理は実際の API と連携させることもできるでしょう。
完全なコンポーネント
すべてのパーツを組み合わせた完全なコンポーネントは以下のようになります。
typescriptexport function ChatWidget({
position = 'bottom-right',
primaryColor = '#0066FF',
}: WidgetProps) {
const [isOpen, setIsOpen] = useState(false);
const [message, setMessage] = useState('');
return (
<div class={`widget-container ${position}`}>
<button
class='widget-toggle'
onClick={() => setIsOpen(!isOpen)}
style={{ backgroundColor: primaryColor }}
aria-label={
isOpen ? 'チャットを閉じる' : 'チャットを開く'
}
>
{isOpen ? '✕' : '💬'}
</button>
{isOpen && (
<div class='widget-window'>
<div
class='widget-header'
style={{ backgroundColor: primaryColor }}
>
<h3>チャットサポート</h3>
</div>
<div class='widget-body'>
<div class='widget-messages'>
<div class='message message-bot'>
こんにちは!何かお困りですか?
</div>
</div>
</div>
<div class='widget-footer'>
<form
onSubmit={(e) => {
e.preventDefault();
console.log('送信:', message);
setMessage('');
}}
>
<input
type='text'
value={message}
onInput={(e) =>
setMessage(e.currentTarget.value)
}
placeholder='メッセージを入力...'
class='widget-input'
/>
<button type='submit' class='widget-send'>
送信
</button>
</form>
</div>
</div>
)}
</div>
);
}
スタイルの実装
ウィジェットのスタイルを src/styles.css ファイルで定義します。Shadow DOM 内で適用されるため、外部のスタイルと競合しません。
ベーススタイル
css/* リセットとベーススタイル */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.widget-container {
position: fixed;
z-index: 999999;
font-family: -apple-system, BlinkMacSystemFont,
'Segoe UI', sans-serif;
}
最初にリセット CSS を適用し、ウィジェットを固定位置に配置します。z-index を高く設定することで、他の要素より前面に表示されます。
位置指定のスタイル
css/* 表示位置の設定 */
.widget-container.bottom-right {
bottom: 20px;
right: 20px;
}
.widget-container.bottom-left {
bottom: 20px;
left: 20px;
}
利用者が選択した位置にウィジェットを配置するためのスタイルです。
トグルボタンのスタイル
css/* トグルボタン */
.widget-toggle {
width: 60px;
height: 60px;
border-radius: 50%;
border: none;
color: white;
font-size: 24px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition: transform 0.2s, box-shadow 0.2s;
}
.widget-toggle:hover {
transform: scale(1.05);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
円形のトグルボタンに、ホバー時のアニメーションを追加します。視覚的なフィードバックがあることで、ユーザー体験が向上するでしょう。
チャットウィンドウのスタイル
css/* チャットウィンドウ */
.widget-window {
position: absolute;
bottom: 80px;
width: 360px;
height: 500px;
background: white;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
overflow: hidden;
}
チャットウィンドウをトグルボタンの上に表示し、角丸とシャドウで洗練された見た目にします。
css/* ヘッダー */
.widget-header {
padding: 16px;
color: white;
}
.widget-header h3 {
font-size: 18px;
font-weight: 600;
}
ヘッダー部分のスタイルを定義します。プライマリーカラーが背景色として適用されます。
css/* メッセージエリア */
.widget-body {
flex: 1;
overflow-y: auto;
padding: 16px;
background: #f7f7f7;
}
.widget-messages {
display: flex;
flex-direction: column;
gap: 12px;
}
.message {
padding: 12px;
border-radius: 8px;
max-width: 80%;
}
.message-bot {
background: white;
align-self: flex-start;
}
メッセージ表示エリアのスタイルです。スクロール可能にし、メッセージごとに適切な間隔を設定します。
入力フォームのスタイル
css/* フッター(入力エリア) */
.widget-footer {
padding: 16px;
background: white;
border-top: 1px solid #e0e0e0;
}
.widget-footer form {
display: flex;
gap: 8px;
}
.widget-input {
flex: 1;
padding: 10px 12px;
border: 1px solid #e0e0e0;
border-radius: 6px;
font-size: 14px;
outline: none;
}
.widget-input:focus {
border-color: #0066ff;
}
.widget-send {
padding: 10px 20px;
background: #0066ff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: background 0.2s;
}
.widget-send:hover {
background: #0052cc;
}
入力フォームと送信ボタンのスタイルを定義します。フォーカス時の視覚的フィードバックも忘れずに実装しましょう。
ローダースクリプトの実装
ローダースクリプトは、ウィジェットを初期化し、Shadow DOM に描画する役割を担います。
基本的な初期化関数
src/loader.ts ファイルを作成し、初期化ロジックを実装します。
typescriptimport { render, h } from 'preact';
import { ChatWidget } from './widget';
import styles from './styles.css?inline';
必要なモジュールとスタイルをインポートします。Vite の ?inline クエリを使うことで、CSS を文字列として読み込めます。
typescriptinterface WidgetConfig {
position?: 'bottom-right' | 'bottom-left';
primaryColor?: string;
containerId?: string;
}
ウィジェットの設定を定義するインターフェースを用意します。
typescriptfunction initWidget(config: WidgetConfig = {}) {
// デフォルト値の設定
const {
position = 'bottom-right',
primaryColor = '#0066FF',
containerId = 'preact-chat-widget',
} = config;
// 次のステップで実装
}
初期化関数のベースを作成します。利用者が設定を省略した場合のデフォルト値も定義しておきましょう。
Shadow DOM の作成と初期化
typescriptfunction initWidget(config: WidgetConfig = {}) {
const {
position = 'bottom-right',
primaryColor = '#0066FF',
containerId = 'preact-chat-widget',
} = config;
// コンテナ要素の作成
const container = document.createElement('div');
container.id = containerId;
document.body.appendChild(container);
// 次のステップで実装
}
ウィジェットを配置するためのコンテナ要素を作成し、body に追加します。
typescript// Shadow DOMの作成
const shadowRoot = container.attachShadow({ mode: 'open' });
// Shadow DOM内にスタイルを注入
const styleElement = document.createElement('style');
styleElement.textContent = styles;
shadowRoot.appendChild(styleElement);
Shadow DOM を作成し、スタイルを注入します。これにより、外部の CSS から完全に独立した環境が作られます。
typescript// Preactコンポーネントのレンダリング用要素を作成
const mountPoint = document.createElement('div');
shadowRoot.appendChild(mountPoint);
// Preactコンポーネントをレンダリング
render(
h(ChatWidget, { position, primaryColor }),
mountPoint
);
Preact コンポーネントをレンダリングします。h 関数を使って JSX を構築し、render 関数で Shadow DOM 内にマウントします。
グローバル API の公開
typescript// グローバルスコープにAPIを公開
declare global {
interface Window {
PreactChatWidget: {
init: (config?: WidgetConfig) => void;
};
}
}
window.PreactChatWidget = {
init: initWidget,
};
利用者がスクリプト読み込み後にウィジェットを初期化できるよう、グローバル API を公開します。名前空間を使うことで、他のスクリプトとの競合を避けられるでしょう。
typescript// 自動初期化のサポート
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
initWidget();
});
} else {
initWidget();
}
DOM の読み込み完了後に自動的に初期化する機能を追加します。これにより、利用者は何も設定せずに使い始めることもできます。
ビルド設定
Vite を使って、配布可能な形式にビルドする設定を行います。
Vite の設定ファイル
vite.config.ts ファイルを作成し、ビルドオプションを設定します。
typescriptimport { defineConfig } from 'vite';
import preact from '@preact/preset-vite';
Vite と preact のプリセットをインポートします。
typescriptexport default defineConfig({
plugins: [preact()],
build: {
lib: {
entry: 'src/loader.ts',
name: 'PreactChatWidget',
fileName: 'widget',
formats: ['iife'],
},
rollupOptions: {
output: {
inlineDynamicImports: true,
},
},
},
});
ライブラリモードでビルドし、IIFE(即時実行関数式)形式で出力します。すべてのコードを 1 つのファイルにバンドルすることで、利用者は 1 つの script タグを追加するだけで済みます。
以下の図で、ビルドプロセスを視覚化します。
mermaidflowchart LR
subgraph source["ソースコード"]
tsx["widget.tsx<br/>【Preact コンポーネント】"]
loader["loader.ts<br/>【初期化スクリプト】"]
css["styles.css<br/>【スタイル】"]
end
subgraph build["ビルドプロセス"]
vite["Vite"]
bundle["バンドル処理"]
minify["最小化"]
end
subgraph output["出力ファイル"]
iife["widget.iife.js<br/>【配布用ファイル】"]
end
tsx --> vite
loader --> vite
css --> vite
vite --> bundle
bundle --> minify
minify --> iife
style iife fill:#90EE90
package.json のスクリプト設定
package.json にビルドコマンドを追加します。
json{
"name": "preact-chat-widget",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"preact": "^10.19.3"
},
"devDependencies": {
"@preact/preset-vite": "^2.8.1",
"typescript": "^5.3.3",
"vite": "^5.0.10"
}
}
これで開発、ビルド、プレビューのコマンドが使えるようになります。
ビルドと配布
それでは実際にビルドを実行し、配布用ファイルを生成しましょう。
bashyarn build
ビルドが完了すると、dist ディレクトリに以下のファイルが生成されます。
pythondist/
├── widget.iife.js # 本番用(最小化済み)
└── widget.iife.js.map # ソースマップ
この widget.iife.js ファイルを CDN や Web サーバーにアップロードすれば、配布準備は完了です。
利用者への実装方法
ウィジェットを配布した後、利用者は以下の HTML コードを自分の Web サイトに追加するだけで使えます。
基本的な設置方法
html<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>ウィジェット設置例</title>
</head>
<body>
<h1>私のWebサイト</h1>
<p>ここに通常のコンテンツが表示されます。</p>
<!-- ウィジェットの読み込み -->
<script src="https://your-cdn.com/widget.iife.js"></script>
</body>
</html>
たったこれだけで、チャットウィジェットが表示されます。自動初期化が有効なため、追加の設定は不要です。
カスタマイズ設定
利用者が表示位置や色をカスタマイズしたい場合は、以下のように設定できます。
html<script src="https://your-cdn.com/widget.iife.js"></script>
<script>
// 自動初期化を無効にしたい場合は、初期化前に設定を変更
window.PreactChatWidget.init({
position: 'bottom-left',
primaryColor: '#FF5722',
});
</script>
このように、シンプルな API で柔軟なカスタマイズが可能になっています。
開発時のデバッグ
開発中は、Vite の開発サーバーを使うと効率的です。
bashyarn dev
開発サーバーが起動したら、index.html ファイルを作成してテストできます。
html<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>ウィジェット開発</title>
</head>
<body>
<h1>開発用ページ</h1>
<script type="module" src="/src/loader.ts"></script>
</body>
</html>
ファイルを保存すると即座にブラウザに反映されるため、快適に開発を進められるでしょう。
エラー処理とデバッグ
実際の運用では、エラー処理も重要です。以下のようなエラーハンドリングを追加することをお勧めします。
初期化エラーの処理
typescriptfunction initWidget(config: WidgetConfig = {}) {
try {
// Shadow DOMがサポートされているか確認
if (!document.body.attachShadow) {
console.error(
'PreactChatWidget: Shadow DOM is not supported'
);
return;
}
// 既に初期化済みか確認
if (
document.getElementById(
config.containerId || 'preact-chat-widget'
)
) {
console.warn(
'PreactChatWidget: Widget is already initialized'
);
return;
}
// 初期化処理...
} catch (error) {
console.error(
'PreactChatWidget: Initialization failed',
error
);
}
}
Error: Shadow DOM is not supported
このエラーは、古いブラウザで Shadow DOM がサポートされていない場合に発生します。
発生条件
- Internet Explorer 11 以前
- Safari 9 以前
- 古い Android ブラウザ
解決方法
- ポリフィルを導入する
- サポート対象ブラウザを明確にする
- フォールバック用の UI を用意する
typescript// ポリフィルの例
import '@webcomponents/shadydom';
パフォーマンス最適化
ウィジェットのパフォーマンスをさらに向上させるためのテクニックを紹介します。
遅延読み込みの実装
typescript// 非同期での初期化
function initWidget(config: WidgetConfig = {}) {
// ページ読み込み完了まで待機
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
requestIdleCallback(() => {
performInit(config);
});
});
} else {
requestIdleCallback(() => {
performInit(config);
});
}
}
function performInit(config: WidgetConfig) {
// 実際の初期化処理
}
requestIdleCallback を使うことで、ブラウザのアイドル時間を利用して初期化できます。これにより、ページの読み込みパフォーマンスへの影響を最小限に抑えられるでしょう。
コード分割
大きなウィジェットの場合、コード分割を検討しましょう。
typescript// 動的インポートの例
async function openChat() {
const { ChatWindow } = await import(
'./components/ChatWindow'
);
// チャットウィンドウを表示
}
初回はトグルボタンのみを読み込み、ユーザーがクリックしたときに本体を読み込む方式にすることで、初期読み込みサイズを削減できます。
セキュリティ考慮事項
埋め込みウィジェットでは、セキュリティにも配慮が必要です。
XSS 対策
ユーザー入力を扱う場合は、必ずサニタイズを行いましょう。
typescriptimport DOMPurify from 'dompurify';
function sanitizeMessage(message: string): string {
return DOMPurify.sanitize(message, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong'],
ALLOWED_ATTR: [],
});
}
Error: XSS vulnerability detected
このエラーは、サニタイズされていないユーザー入力が表示される場合に発生する可能性があります。
発生条件
- ユーザーが入力したテキストをそのまま表示
- API レスポンスをそのまま HTML に挿入
解決方法
- DOMPurify などのライブラリでサニタイズ
- Preact のテキストノードとして表示(自動エスケープ)
- Content Security Policy の設定
CSP(Content Security Policy)対応
厳格な CSP が設定されている環境でも動作するよう、インラインスタイルを避ける設計にすることが望ましいです。
まとめ
Preact を使った埋め込みウィジェットの作り方を、セットアップから配布まで詳しく解説してきました。
重要なポイントを振り返りましょう。Preact は 3KB という軽量さで、設置先サイトのパフォーマンスに影響を与えません。Shadow DOM を使うことで、スタイルと JavaScript を完全に独立させ、設置先サイトとの競合を回避できます。Vite による効率的なビルドプロセスで、開発体験も優れています。
実装の流れは以下の通りです。
| # | ステップ | 主な作業 |
|---|---|---|
| 1 | セットアップ | Preact と Vite のインストール |
| 2 | コンポーネント実装 | UI ロジックの作成 |
| 3 | スタイル実装 | 独立した CSS の定義 |
| 4 | ローダー実装 | Shadow DOM 初期化 |
| 5 | ビルド設定 | IIFE 形式での出力 |
| 6 | 配布 | CDN へのアップロード |
この記事で紹介した方法を使えば、チャットサポート、通知バナー、評価ウィジェットなど、様々な埋め込み型 UI を作成できるでしょう。
さらに高度な機能として、複数のウィジェットインスタンスの管理、リアルタイム通信の実装、アクセシビリティの強化なども検討できます。あなたのサービスに最適なウィジェットを作成し、多くの Web サイトで利用してもらえるといいですね。
関連リンク
articlePreact で埋め込みウィジェット配布:他サイトに設置できる軽量 UI の作り方
articlePreact でミニブログを 1 日で公開:ルーティング・MDX・SEO まで一気通貫
articlePreact でスケーラブルな状態管理:Signals/Context/外部ストアの責務分離
articlePreact チートシート【保存版】:JSX/Props/Events/Ref の書き方早見表
articleVite で始める Preact:公式プラグイン設定と最短プロジェクト作成【完全手順】
article【徹底比較】Preact vs React 2025:バンドル・FPS・メモリ・DX を総合評価
articleSvelte のコンパイル出力を読み解く:仮想 DOM なしで速い理由
articleTauri で Markdown エディタを作る:ライブプレビュー・拡張プラグイン対応
articleStorybook で“仕様が生きる”開発:ドキュメント駆動 UI の実践ロードマップ
articleshadcn/ui で B2B SaaS ダッシュボードを組む:権限別 UI と監査ログの見せ方
articleSolidJS の Control Flow コンポーネント大全:Show/For/Switch/ErrorBoundary を使い分け
articleRemix で管理画面テンプレ:表・フィルタ・CSV エクスポートの鉄板構成
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来