はじめに
正直なところ、「ページビルダーを自社で作りたい」って思ったことありませんか。
WordPressのElementorやWixのエディタみたいな、ドラッグ&ドロップでページを作れるやつ。クライアントから「コードを書かずにページを編集したい」と言われて、既存のノーコードツールを導入するか悩んだ経験のある人も多いはず。
でも既存ツールって、カスタマイズ性に限界があるんですよね。自社のデザインシステムに合わせたい、独自のコンポーネントを使いたい...そんなときに困る。
そこで登場するのがPuckです。
PuckはGitHubで10,200スター以上を獲得している、React向けのオープンソースビジュアルエディタ。自分で定義したReactコンポーネントを、ユーザーがドラッグ&ドロップで配置できるページビルダーが作れます。
これ、意外と革命的なんですよ。
Puckとは
Puckは、React.js向けのモジュール型ビジュアルエディタです。公式では「The visual editor for React」と謳っています。
最大の特徴は自分のReactコンポーネントをそのまま使えること。他のノーコードツールと違って、既存のデザインシステムやUIライブラリをそのままページビルダーに組み込めます。
// 自分のコンポーネントをそのまま登録
const config = {
components: {
Hero: {
render: ({ title, subtitle }) => (
<div className="hero">
<h1>{title}</h1>
<p>{subtitle}</p>
</div>
),
fields: {
title: { type: "text" },
subtitle: { type: "text" },
},
},
},
};
これだけで、ユーザーがドラッグ&ドロップでHeroコンポーネントを配置し、タイトルやサブタイトルを編集できるようになります。
公式サイト: https://puckeditor.com GitHub: https://github.com/measuredco/puck
特徴・メリット
1. 完全なデータ所有権
PuckはSaaSではなく、オープンソースのライブラリです。編集されたページのデータは完全に自分のものになります。
{
"content": [
{
"type": "Hero",
"props": {
"title": "ようこそ",
"subtitle": "私たちのサービスへ"
}
}
],
"root": {}
}
JSONで出力されるので、どこにでも保存できる。データベースでもファイルでも、S3でも好きなところに。ベンダーロックインを気にする必要がないのは、30代エンジニアとしてはかなり安心感がありますね。
2. Reactエコシステムとの親和性
PuckはReactコンポーネントとして動作します。Next.js、Remix、React Routerなど、好きなフレームワークと組み合わせ可能。
公式で以下のレシピが提供されています:
- Next.js (App Router対応)
- Remix Run v2
- React Router v7
個人的には、Next.jsのApp Routerと組み合わせるのが一番使いやすいと思います。
3. MITライセンス
商用利用も無料です。社内ツールでも、クライアントに納品するシステムでも、ライセンス料を気にせず使えます。
コスパ的に最高ですね。
4. 軽量で依存関係が少ない
パッケージサイズは約98.6kB(gzip圧縮時)。ビジュアルエディタとしては十分軽量です。
重たいエディタを入れたらサイトが遅くなった...という心配は少ない。
5. AI機能のサポート
最新のPuck AI 0.3では、ヘッドレスなAI生成APIが利用可能になりました。AIを使ったページ生成やコンテンツ提案など、先進的な機能も視野に入れた開発が進んでいます。
インストール方法
新規プロジェクトを始める場合
npx create-puck-app my-app
これで、Puckを使ったプロジェクトのひな形が生成されます。時短になりますね。
既存プロジェクトに追加する場合
npm install @measured/puck
または
yarn add @measured/puck
基本的な使い方
エディタを表示する
まず、コンポーネントの設定を定義し、<Puck>コンポーネントをレンダリングします。
import { Puck } from "@measured/puck";
import "@measured/puck/puck.css";
// コンポーネントの設定
const config = {
components: {
HeadingBlock: {
fields: {
text: { type: "text" },
level: {
type: "select",
options: [
{ label: "H1", value: "h1" },
{ label: "H2", value: "h2" },
{ label: "H3", value: "h3" },
],
},
},
defaultProps: {
text: "見出し",
level: "h1",
},
render: ({ text, level }) => {
const Tag = level;
return <Tag>{text}</Tag>;
},
},
TextBlock: {
fields: {
content: { type: "textarea" },
},
defaultProps: {
content: "本文テキスト",
},
render: ({ content }) => <p>{content}</p>,
},
},
};
// 初期データ
const initialData = {
content: [],
root: {},
};
// 保存時の処理
const handlePublish = (data) => {
console.log("保存されたデータ:", data);
// ここでAPIに送信するなど
};
export default function Editor() {
return (
<Puck config={config} data={initialData} onPublish={handlePublish} />
);
}
これだけで、ドラッグ&ドロップでHeadingBlockとTextBlockを配置できるエディタが完成します。
保存したページを表示する
編集したページを公開側で表示するには、<Render>コンポーネントを使います。
import { Render } from "@measured/puck";
export default function Page({ data }) {
return <Render config={config} data={data} />;
}
エディタとビューアを分離できるので、編集権限のあるユーザーだけにエディタを見せる、という実装も簡単です。
Next.js App Routerでの使用例
// app/editor/page.tsx
"use client";
import { Puck } from "@measured/puck";
import "@measured/puck/puck.css";
import { config } from "@/lib/puck-config";
export default function EditorPage() {
const handlePublish = async (data) => {
await fetch("/api/pages", {
method: "POST",
body: JSON.stringify(data),
});
};
return (
<Puck
config={config}
data={{ content: [], root: {} }}
onPublish={handlePublish}
/>
);
}
// app/[slug]/page.tsx
import { Render } from "@measured/puck";
import { config } from "@/lib/puck-config";
import { getPageData } from "@/lib/api";
export default async function Page({ params }) {
const data = await getPageData(params.slug);
return <Render config={config} data={data} />;
}
App Routerを使っている場合、エディタページは"use client"が必要なので注意。
実践的なユースケース
1. 社内向けCMSの構築
マーケティングチームが自分でランディングページを作りたい。でも外部のノーコードツールは社内ルール的に使えない...そんなときにPuckが活躍します。
自社のデザインシステムに沿ったコンポーネントだけを提供すれば、ブランドの一貫性を保ちながら非エンジニアでもページが作れます。
2. クライアント向けページビルダー
Web制作会社なら、納品後にクライアント自身がページを編集できる仕組みを提供できます。WordPressよりも軽量で、カスタマイズ性も高い。
3. メールテンプレートエディタ
Reactでメールテンプレートを作っているなら、Puckで編集UIを提供できます。react-emailなどと組み合わせると便利。
4. ダッシュボードのカスタマイズ
ユーザーが自分でダッシュボードのレイアウトをカスタマイズできるようにする。ウィジェットをドラッグ&ドロップで配置させるUIにPuckを使えます。
5. ドキュメント・ブログエディタ
Notion風のブロックエディタも、Puckをベースに構築可能。ブロックタイプを定義すれば、リッチなコンテンツエディタが作れます。
高度な使い方
カスタムフィールドの定義
標準のtext、textareaの他に、カスタムフィールドも作れます。
const config = {
components: {
ImageBlock: {
fields: {
src: {
type: "custom",
render: ({ value, onChange }) => (
<div>
<input
type="text"
value={value || ""}
onChange={(e) => onChange(e.target.value)}
placeholder="画像URL"
/>
{value && <img src={value} alt="" width={100} />}
</div>
),
},
alt: { type: "text" },
},
render: ({ src, alt }) => (
<img src={src} alt={alt} className="w-full" />
),
},
},
};
画像アップロードUIを独自に実装したい場合などに使えます。
ネストされたコンポーネント
コンポーネントの中に別のコンポーネントを配置することもできます。
const config = {
components: {
Container: {
render: ({ puck: { renderDropZone } }) => (
<div className="container">
{renderDropZone({ zone: "content" })}
</div>
),
},
Card: {
fields: {
title: { type: "text" },
},
render: ({ title, puck: { renderDropZone } }) => (
<div className="card">
<h3>{title}</h3>
{renderDropZone({ zone: "body" })}
</div>
),
},
},
};
これで、ContainerやCardの中に他のコンポーネントをドロップできるようになります。
注意点
良いことばかり書いてきましたが、Puckにも向き不向きがあります。
向いているケース
- Reactベースのプロジェクト
- 自社コンポーネントをそのまま使いたい
- データの所有権を自分で持ちたい
- カスタマイズ性が重要
- 商用利用でライセンス料を払いたくない
向いていないケース
- React以外のフレームワークを使っている
- ノンエンジニアだけでゼロから構築したい
- 既存のWordPressサイトに追加したい
個人的には、「Reactを使っているプロジェクトでページビルダーが必要になったら、まずPuckを検討すべき」だと思っています。
まとめ
正直なところ、Puckは「かゆいところに手が届く」ライブラリですね。
ノーコードツールの便利さと、開発者の柔軟性を両立している。既存のReactコンポーネントをそのまま使えるので、デザインシステムとの整合性も保てる。
以下の条件に当てはまるなら、試してみる価値は間違いなくあります:
- Reactプロジェクトでページビルダーが必要
- 既存のUIコンポーネントをそのまま使いたい
- SaaSのベンダーロックインを避けたい
- 商用利用でライセンス料を気にしたくない
- Next.jsと組み合わせて使いたい
10,200スター以上という数字が、その実用性を証明しています。705フォーク、69人のコントリビューターによる活発な開発も安心材料ですね。
まずはnpx create-puck-app my-appで試してみてください。驚くほど簡単にページビルダーが作れることがわかるはずです。
公式サイト: https://puckeditor.com GitHub: https://github.com/measuredco/puck デモ: https://demo.puckeditor.com/edit npm: https://www.npmjs.com/package/@measured/puck