Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Tiptapで実現する堅牢で柔軟なエディター開発
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
kirik
July 22, 2025
Technology
500
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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
99
Recoil脱却の現状と挑戦
kirik
2
930
Recoilを剥がしている話
kirik
5
12k
Other Decks in Technology
See All in Technology
TinyGo 開発サイクルを高速化する:Go で作るエミュレータ入門
zozotech
PRO
1
740
日経電子版を支えていく Kasane Design System/fec_fukuoka
nikkei_engineer_recruiting
0
1.6k
映像変換サーバーなしで端末内でHLSを生成してライブ配信
hikarusato
0
120
[2026-09-11]SREは誰のもの?運用エンジニアが始める 「SRE領域への越境」とチームの進化の軌跡 〜Road to NEXT CRE
tosite
0
290
Genieを崇めよ
kameitomohiro
0
150
Spring BootからQuarkusへの移行
tatsuya1bm
2
130
おい、エージェントを使って終わらせろ
nwiizo
0
470
幾何アルゴリズムで なめらかなピン操作を / iOSDC Japan 2026 / smoothpin
kazumanagano
0
350
CLIライブラリ開発を支える技術
htnabe
0
120
2026-09-18 gotanda.sre Terraformで複数環境作ったり、複数Stateに分割したりそれとTerragrunt / Terraform multi envs and multi states
masasuzu
1
410
アプリをもっと"iOSアプリっぽく"する小さな工夫 / Small Touches That Make Your App Feel More Like an iOS App
matsuji
2
920
あけおめLINE 傾向とその対策
nasa9084
0
100
Featured
See All Featured
The Mindset for Success: Future Career Progression
greggifford
PRO
0
490
Crafting Experiences
bethany
1
340
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
Everyday Curiosity
cassininazir
0
320
Embracing the Ebb and Flow
colly
88
5.2k
YesSQL, Process and Tooling at Scale
rocio
174
15k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
Automating Front-end Workflow
addyosmani
1369
210k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
5
660
What does AI have to do with Human Rights?
axbom
PRO
1
2.4k
The Language of Interfaces
destraynor
162
27k
Docker and Python
trallard
47
4.2k
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で、