
本稿はJCB Advent Calendar 2025の12月11日の記事です。
こんにちは。JCBデジタルソリューション開発部の小松です。
現在運用中のサービスに存在する「お知らせ機能」の更新工数削減を目指し、CMS(Contents Management System)というwebサイトの専門的な知識なしでお知らせを投稿可能な機能の導入を進めています。
今回は既存サイトのデザインを維持した記事投稿機能が実現可能か、技術検証をしました。
特にTiptapエディタの特徴であるJSON形式での出力をそのままCMS(Strapi)に保存・活用する方法について中心に記載します。
1. はじめに:背景と目的
現在の運用サイトのお知らせ機能では、アンケートやメンテナンス告知など、ビジネスサイドからユーザーへ届けたい情報を掲載しています。
しかし、従来の運用は「ビジネスサイドの要望を受け、開発チームが実装・デプロイする」という開発者依存の強いものであり、リードタイムやリソース面で課題となっていました。
そこで、運用の抜本的な刷新を目指し、「CMSの導入」と「投稿画面の自作」というアプローチをしました。
① なぜ、既存のお知らせ機能をCMS化するのか
「お知らせ更新」を開発工数なしで実現するためです。
現状は更新のたびに開発者がコードを修正し、本番環境へのデプロイをする必要があります。
この状況は開発チームのリソースを圧迫するだけでなく、ビジネスサイドにとっても「依頼から公開までのタイムラグ」という課題が生じていました。
この状態から脱却し、ビジネスサイドのみで更新が完結するお知らせ投稿機能の構築を目指しました。
② なぜ、標準管理画面ではなく「投稿画面」を自作するのか
通常CMS導入といえば標準の管理画面(StrapiのAdmin Panelなど)を使うのが一般的だと思いますが、今回の検討では以下の背景から「投稿画面も自前で作る」方向で進めています。
- 作業ミス削減: 汎用的なCMS画面の操作を新たに習得してもらうのは、学習コストがかかるうえミスの温床にもなりかねません。 そこでビジネスサイドが使い慣れている既存サイトの管理画面内に「投稿機能」を統合し、直感的にミスなく投稿可能な状態を目指すため。
- シームレスな体験: 別システムへのログインを不要として、日常業務の延長線上でスムーズにお知らせを投稿可能な状態を目指すため。
2. 技術選定:Strapi × Tiptap
検証にあたり、フロントエンドにTiptap、バックエンドにStrapiを採用する構成を選定しました。
両者の関係とデータの流れは以下の通りです。

バックエンド:Strapi
- Headless CMS: APIファーストで扱いやすいため。
- Content-Type Builder: 独自のデータ構造(後述するJSON用フィールドなど)を定義しやすいため。
フロントエンド(エディタ):Tiptap
- Headless Editor: 固定のUIを持たず、既存サイトのデザインシステムに合わせてカスタム拡張(Extension)等を活用することでフルカスタマイズが可能なため。
- JSON Output: HTML文字列ではなく、構造化されたJSON出力が可能となっており、データが扱いやすいため。(詳細は後述します。)
3. データ設計の検討:HTML文字列 or JSON
Strapiでリッチテキストを扱う際、通常は標準の「Rich Textフィールド(Markdown/HTML)」を使います。しかし今回はTiptapのメリットを最大限活かすため、以下の構成としました。
構成:Dynamic Zonesを用いた「JSONコンポーネント」
Strapiの記事本文フィールドには 「Dynamic Zones」 を採用し、その中に自作の 「JSON Component」 と、補助的に画像を扱う 「Media Component」 を配置する設計としました。
Strapiのフィールド構成:
- Dynamic Zones (Article Body)
- JSON Component: Tiptapの出力するJSONオブジェクトをそのまま格納するフィールド
- Media Component: 画像を扱うフィールド
なぜ HTML ではなく JSON なのか?
HTML文字列として保存してしまうと、フロントエンドでの表示時に dangerouslySetInnerHTML を使う必要が出てきたり、特定の要素だけ後からスタイル変更したい場合に正規表現でのパースが必要になるなど不具合埋め込みのリスクが上がります。
一方、JSONとして保存しておけば、「この要素にはこのCSSクラスを当てる」 といった制御が容易で、既存サイトで使用しているCSSをそのまま適用できるため、デザインの一貫性を保ちやすくなります。
4. 実装イメージ:Tiptap JSONの保存と表示制御
この構成の大きなメリットは、既存サイトのCSSを編集画面側にも適用することで、「執筆中も本番とほぼ同じ見た目(WYSIWYG)」 を実現できる点です。この実装をどのように実現しているかを下記で説明します。

JSONによる表示のコントロール
例えば、サイト独自のスタイル定義として「赤色の太字」を使いたい場合、Tiptap側でカスタム拡張(Extension)を定義することで、以下のようなJSONを出力させることができます。
{
"type": "text",
"marks": [
{
"type": "redBold" // 独自に定義したマーク
}
],
"text": "赤太字です。"
}
このように独自の type を定義してJSONに保存することで、フロントエンドでの描画時に、既存サイトで定義済みのCSSクラスを的確にマッピングできます。
// フロントエンドでのマッピングイメージ
switch (type) {
case 'underline':
return <span className="underline">{children}</span>;
case 'redBold':
return <strong className="red-bold">{children}</strong>;
default:
return <>{children}</>;
}
これによりCMS導入のために新たなコンポーネントやスタイルシートを作る必要がなく、既存サイトのデザイン資産(CSS)を活かした記事作成が可能になります。
保存処理のイメージ(フロントエンド)
投稿処理自体も以下のようにシンプルです。
// 投稿ボタン押下時の処理イメージ
const handleSubmit = async () => {
// Tiptapで生成したJSON構造をそのまま入れる
const editContents = [
{
type: 'paragraph',
content: [
{
type: 'text',
marks: [
{
type: 'bold',
},
],
text: '太字',
},
],
},
];
// Strapiの形式に合わせてペイロードを作成
const payload = {
data: {
title: '記事タイトル',
main: [
{
__component: 'content.json-block', // Dynamic Zonesのコンポーネント名
content: {
type: 'doc',
content: editContents,
},
},
],
},
};
// POSTする
await fetch(url, { method: 'POST', headers, body: payload });
};
このように独自のスタイルの情報もTiptapのJSON構造の中に含まれるため、データ構造を分解する必要がありません。
6. まとめ
技術検証の結果、StrapiとTiptapを組み合わせ、さらにデータのやり取りをJSON形式とすることで不具合埋め込みリスクを抑えた使い勝手の良いCMS基盤構築が可能という結論に達しました。
- JSONの使用: HTML文字列操作ではなく、JSONオブジェクトとして記事データを扱えるため、高品質な開発が可能と判断。
- 既存CSSの活用: Tiptapのカスタム拡張(Extension)により、マークとCSSクラスを紐づけるだけで、既存サイトのデザインを利用可能と判断。
終わりに
最後になりましたが、JCBでは我々と一緒に働きたいという人材を募集しています。
詳しい募集要項等については採用ページをご覧下さい。