Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Tiptapで実現する堅牢で柔軟なエディター開発
Search
kirik
July 22, 2025
Technology
450
1
Share
Tiptapで実現する堅牢で柔軟なエディター開発
2025/7/22 に行われたエディター勉強会の発表資料
https://prtimes.connpass.com/event/358977/
kirik
July 22, 2025
More Decks by kirik
See All by kirik
Tiptapで校正機能を作った時に考えたこと
kirik
0
67
Recoil脱却の現状と挑戦
kirik
2
850
Recoilを剥がしている話
kirik
5
12k
Other Decks in Technology
See All in Technology
大規模災害時でも高い信頼性を維持するアプリケーション基盤の実現/nikkei-tech-talk46
nikkei_engineer_recruiting
0
120
20260528_生成AIを専属DSに_Howの次にすべきことを考える
doradora09
PRO
0
260
マーケットプレイス版Oracle WebCenter Content For OCI
oracle4engineer
PRO
5
1.7k
Kiro CLI v2.0.0がやってきた!
kentapapa
0
220
Anthropic AIネイティブ・スタートアップ構築のプレイブック を理解する
nagatsu
0
220
AI駆動開発でなんでもハンズオン環境をつくってみた
yoshimi0227
0
180
Agentic Design Patterns
glaforge
0
290
Agentic AI時代における メルカリのAIガバナンスとガードレール実装
naoichihara
16
17k
地元にいないローカルオーガナイザーの立ち回り
uvb_76
1
370
Claude Codeですべての日常業務を爆速化しよう!
minorun365
PRO
16
16k
最低限これだけ押さえれ大丈夫_Claude Enterprise/Team企業展開ガバナンス入門
tkikuchi
1
550
プラットフォームエンジニア ワークショップ/ platform-workshop
databricksjapan
0
140
Featured
See All Featured
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.5k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
590
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
330
Abbi's Birthday
coloredviolet
2
7.8k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
150
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
230
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
560
Producing Creativity
orderedlist
PRO
348
40k
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
440
It's Worth the Effort
3n
188
29k
Transcript
Tiptapで実現する堅牢で柔軟なエディター開発 2024/7/22 PR TIMES.DEV エディター勉強会 #1 株式会社PR TIMES 桐澤 康平
@kiririLee
ProseMirrorのスキーマを活用したHTMLの正規化 Reactコンポーネントの組み込みと プラグインによる機能拡張 Tiptap の Extension 単位での単体テスト 今日話すこと
ProseMirrorのスキーマを活用した HTMLの正規化
ProseMirror ?? Tiptapの話じゃないの?
TiptapはProseMirrorのラッパー ・ProseMirrorはTypeScriptで実装された WYSIWYGエディターライブラリ ・Tiptapはヘッドレスで React、Vue、Svelte などの モダンなUIフレームワークとの統合を実現している ・TiptapはProseMirrorの概念を一部抽象化しているが、 ProseMirrorの哲学は知っておく必要がある。スキーマもその一つ
Tiptapによる抽象化の例
ProseMirror Tiptap https://tiptap.dev/docs/editor/core-concepts/schema
エディタで最も基本的な機能である 段落機能(p要素)のスキーマ定義 👉 スキーマの定義をしないと段落すら 扱えない エディタで使用するHTMLは 全てスキーマを定義する TiptapはスキーマをExtensionという 概念で抽象化している Paragraph
Extension
contentで子要素に持てるコンテンツを指定 parseHTMLで読み込むHTMLを指定 renderHTMLで出力するHTMLを指定 重要な点 ・ content 指定に違反するコンテンツは破棄される (子要素のタグ消去) ・ 読み込んだHTMLと出力するHTMLを変えることができる
Extensionの概要
実務での適用例
CKEditor から Tiptap へリプレイス ・ jQuery+CKEditorのレガシー実装でバグ修正・機能追加が困難 ・ Tiptapへのリプレイス目的は機能追加であったため、 UIと機能を保ったまま PR
TIMESにおけるエディタの歴史
Tiptapでリプレイス後、リニューアル ・ UIのアップデートに加え、新機能が追加された PR TIMESにおけるエディターの歴史
一 jQuery+CKEditor のエディターを v1 として Tiptapでリプレイスしたエディターを v2 リニューアルにより新機能追加したエディターを v3 3つのエディタが出てきたので整理
Tiptapで2つのHTML構造を扱う必要性 V1 V2 V3 HTMLは同じ 機能アップデートにより HTMLが変わる jQuery+CKEditor Tiptap+React Tiptap+React
v1からv2へのリプレイス時のHTML v2からv3へのリニューアル時のHTML
CKEditor から Tiptap へのリプレイス 前提として、エディターから出力されるHTMLはそのままDBに保存され、 他システム用に加工される CKEditorのHTML メール用のコンテンツ RSS用のコンテンツ
ブラウザ用のコンテンツ
CKEditor から Tiptap へのリプレイス バックエンドの工数削減のためリプレイス後のTiptapから 出力されるHTMLは保つ必要がある CKEditorのHTML メール用のコンテンツ RSS用のコンテンツ
ブラウザ用のコンテンツ バックエンドの実装は変えない TiptapのHTML
CKEditorとTiptapの統合
CKEditorから出力された画像機能のHTML構造
https://prtimes.jp/main/html/rd/p/000001300.000000112.html 公開されたプレスリリースのHTML
https://prtimes.jp/main/html/rd/p/000001300.000000112.html 公開されたプレスリリースのHTML
公開されるプレスリリースのHTMLでは必要ない属性は 消されている ブラウザ用に加工されている
例えば、「data-nheight」属性はメールに必要な情報で欠落すると メールが壊れる。 このようにほかシステムとの依存関係を持った情報が HTMLにはたくさん埋め込まれている。 重要なのは、、、
このHTMLをどうやって読み込み、壊さずにそのまま出力するか?
まずは parseHTML で読み込み CSSセレクタで指定 priorityで 通常の段落機能(pタグ) 読み込みとの競合を 避ける
tagでパースした Elementの子要素が 全て参照できる getAttrs の働き pタグの子要素である imgタグの属性を 取得する
取得した属性は オブジェクトで return getAttrs の働き
そして renderHTML で出力 HTMLAttributesで パースした属性が 受け取れる 元々のHTMLと 辻褄を合わせるために 属性の値を加工する
画像機能で出力するHTML構造を 配列で定義 ちなみにTiptap v3からJSXで定義できる!! span、imgは他のExtensionで 読み込まれていない このExtensionでもパースしてないが 最終的な出力には含められる 細かいけど重要
CKEditor と Tiptap の統合は以上
リプレイス後、リニューアルによる新機能追加
リプレイス後、リニューアルによる新機能追加 ・ v2をβ版としてリリースし、v1を完全廃止してから リニューアルプロジェクト開始 ・ 画像機能に大幅なアップデート ・ メールなど他システムで扱いやすいようにこのタイミングで 画像機能で出力するHTML構造も大幅に変更
画像機能に大幅なアップデート 1種類のみだった画像機能が 7種類に増えた 画像機能で出力するHTMLも 大幅に変える CKEditorで出力していた HTMLとの統合が必要 https://prtimes.jp/main/html/rd/p/000001357.000000112.html
v2とv3で画像機能を統合する ・ v1 から v2 は機能差がなくHTMLを保つだけで廃止が完了 ・ v2 から v3
は機能差があったため、v2 と v3 を同時に 運用する並行期間を設けていた ・ お客様は v2 から v3 へと編集中のプレスリリースを 切り替えることができる ・ よって、画像機能を例にすると v2 の画像機能のHTML構造をv3で 読み込んだ時に v3 の大画像もしくは中画像機能のHTML構造に する必要がある ※ 現在、v2エディターは廃止されており、v3のみ利用可能
v3 の大画像と中画像 大画像 中画像
大画像のHTML構造 ※ 例として src の URL は placeholder を指定
中画像のHTML構造 figure class 属性の --large を --medium に切り替えて区別 ※ 例として
src の URL は placeholder を指定
再掲: v2の画像機能のHTML構造
v2 -> v3 でもスキーマを活用しよう
まずは parseHTML を定義 配列で読み込むHTMLを 複数指定できる v3で出力した画像と v2で出力した画像をどちらも指定 getFigureNodeAttrsFromV2で v3用に属性を統合する
getFigureNodeAttrsFromV2 v2の画像機能で お馴染みの data-nheight属性 などを取得
getFigureNodeAttrsFromV2 v2で取得した属性値によって v3の大画像にするか中画像に するかを決定する --large or --medium を 切り替える
そして renderHTML する このExtensionではfigureの パースのみ行なっている img 要素のパースは他の Extensionで行なっている 0 は
hole といって子要素のパースを 他のExtensionに委ねられる 重要
小要素の img のパースとレンダー
画像機能は全部で4つのExtensionを 組み合わせている div, figure, img, figcaption の4つ ※ 例として src
の URL は placeholder を指定
・ Extensionはそれぞれの単一のタグのみパースしている ・ HTML構造を強制したい! div.pr_img の小要素のみで必ず figure.pr-img__item-large が存在するようにしたい ・ メールなど他システムでも決まったHTML構造を期待しているため
この↓HTML構造を強制したい
スキーマをより堅牢にする
Extensionの Content と Group が役に立つ Content 子要素に持つことができる HTML(Extension)を 定義できる Group
自分がどのExtensionに所属 するかを定義できる
大画像で扱う Extension の Content と Group を定義する
・ SingleImageExtensionは block要素の内側にしか 存在できない ・ 子要素に pr_figure の Extension しか存在
させない (blockの指定は結構広め、トップレベルのDocExtension配下に存在できる 一番外側の div 要素
・ pr_single_image の内側に しか存在できない ・ 子要素に pr_figure_imageと pr_figure_captionしか 存在させない div
要素の子要素である figure 要素
・ pr_figure の内側にしか存在 できない figure 要素の子要素であるimg要素
・ pr_figure の内側にしか存在 できない ・ (Tips) inline* 指定と hole 指定でカーソル入力を
設置できる figure 要素の子要素である figcaption 要素
各Extensionの定義によってHTML構造を強制できる!!
Reactコンポーネントの組み込みと プラグインによる機能拡張
ブログ書きました 📝 プレスリリースのエディターでTiptapを 使って新機能開発をした話 https://developers.prtimes.jp/2024/09/05/developing-new-features-in-editor-using-tiptap-react-typescript/ 詳しくはWEBで、
Tiptap の Extension 単位での単体テスト
ブログ書きました 📝 Tiptapエディターのテスト戦略:Playwright、Vitest Browser Mode、Editorインスタンスを用いたテスト https://developers.prtimes.jp/2025/02/20/press-release-editor-frontend-testing-tips/ 詳しくはWEBで、