Tiptapに入門してみた
はじめに
この記事では、簡単にWYSIWYGエディタを作成できるTiptapの基本的な使い方をハンズオン形式で解説していきます。
Tiptapとは?
- WYSIWYGエディタを作成できるライブラリ
- 標準でエディタにスタイリングされていないため、UIを自由に実装できる
- 共同編集機能も実装できるっぽい
- Markdown記法はサポートしてない (https://tiptap.dev/guide/output#not-an-option-markdown)
- GitHubでPRを書くときのようなMarkdownエディタはできない(TiptapはあくまでWYSIWYGエディタを構築するためのライブラリ)
- Tiptap markdown というパッケージを使えばMarkdownエディタを作成できるかも?しれないが、よくわからない…
-
Markdown shortcuts があるので、キー入力にMarkdown記法を使うことはできる
- 例として
#
+半角スペース
を入力すると、H1タグが生成・表示される
- 例として
作成できるエディタのイメージ
以下のようなエディタを簡単に作成できます。
以前に同じWYSIWYGエディタを作成できるSlateというライブラリを使用したことがあるのですが、Tiptapのほうが簡単に実装できました。公式ドキュメントもわかりやすかったです。
Next.jsでハンズオン
1. Next.jsプロジェクトを作成
今回はTypeScriptを使用して作成します。
❯ npx create-next-app my-tiptap-project --ts
❯ cd my-tiptap-project
2. Tiptapをインストール
❯ yarn add @tiptap/react @tiptap/pm @tiptap/starter-kit
@tiptap/react
はTiptapをReact(Next.js)で使用するためのパッケージです。
@tiptap/starter-kit
はエディタを構築する際に使用する主要な機能のパッケージを集めたスターターキットです。
3. Tiptapエディタを表示するコンポーネントを作成
プロジェクトディレクトリ直下に components/Tiptap.tsx を作成し、以下のコードを記述します。
import { EditorContent, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import styles from "/styles/components/Tiptap.module.css";
export const Tiptap: React.FC = () => {
const editor = useEditor({
extensions: [StarterKit],
content: "<p>Hello World! 🌎️</p>",
editorProps: {
attributes: {
class: styles.editorContent,
},
},
});
if (!editor) return null;
return (
<div className={styles.container}>
<EditorContent editor={editor} />
</div>
);
};
useEditor
を 使用して Editor
クラスのインスタンスを作成し、実装するエディタの設定を行います。
content
には初期値としてエディタに表示したいHTMLを記述します。
editorProps
では <EditorContent />
にクラス名を付与しています。
extensions
にはエディタに追加したい機能(パッケージ)を記述します。こちらには、先ほどインストールした Starterkit
を追加します。
Starterkitについて
先ほどにも説明した通り、Starterkitは主要な機能のパッケージを集めたもので、これを extentions に追加するだけで下記の機能を使用できます。
Blockquote : <blockquote>
タグを使用できる
BulletList : <ul>
タグを使用できる
CodeBlock : <pre>
と<code>
タグによるコードブロックを使用できる
Document : <body>
タグのようなもの
HardBreak : <br>
タグで改行できる
Heading : <h1>
〜<h6>
タグを使用できる
HorizonalRule : <hr>
タグを使用できる
ListItem : <li>
タグを使用できる
OrderedList : <ol>
タグを使用できる
Paragraph : 段落を扱うために必要
Text : 何らかのテキストを扱う場合に必要
Bold : テキストを太字にする
Code : <code>
タグを使用できる
Italic : テキストを斜体で表示する
Strike : テキストに打ち消し線を引く
History : エディタ編集の履歴をサポートする
Dropcursor
Gapcursor
4. /pages/index.tsx ファイルを更新
import type { NextPage } from "next";
import { Tiptap } from "../components/Tiptap";
import styles from "../styles/Home.module.css";
const Home: NextPage = () => {
return (
<div className={styles.container}>
<Tiptap />;
</div>
);
};
export default Home;
5. CSSでスタイリング
今回はSassを使ってスタイリングしていきます。
❯ yarn add sass --dev
/styles/Home.module.css
を/styles/Home.module.scss
に変更し、下記を記述します。
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}
新たに/styles/components/Tiptap.module.scss
を作成します。
.container {
width: 400px;
height: 300px;
border: 2px solid #19191a;
}
.editorContent {
padding: 10px 20px;
background-color: #ffff;
}
.editorContent:focus {
outline: none;
}
.editorContent > * {
margin-bottom: 15px;
}
.editorContent > ul,
.editorContent > ol {
padding-left: 15px;
}
.editorContent > blockquote {
border-left: 0.25em solid #e4ebee;
padding: 0 1em;
}
.editorContent > code {
color: #767b7d;
background-color: rgba(#767b7d, 0.1);
}
.editorContent > pre {
background: #19191a;
color: #ffff;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
}
.editorContent > pre > code {
color: inherit;
padding: 0;
background: none;
font-size: 0.8rem;
}
6. Next.jsを起動
ここまで実装すれば、下記のようなエディタが表示されます。
Markdown shortcuts によるキー入力で様々なノードやマークを作成できます。
例として、-
+ スペース
でリストが作成されます。
7. メニューを追加
続いては、ボタンのクリックによりh1やリスト、打ち消し線などを表示できるように実装していきます。
Tiptapコンポーネントにボタンを追加していきます。
return (
<>
<div className={styles.menu}>
<button
className={styles.button}
onClick={() =>
editor.chain().focus().toggleHeading({ level: 1 }).run()
}
>
h1
</button>
<button
className={styles.button}
onClick={() => editor.chain().focus().toggleBulletList().run()}
>
リスト
</button>
<button
className={styles.button}
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
>
コードブロック
</button>
<button
className={styles.button}
onClick={() => editor.chain().focus().toggleStrike().run()}
>
打ち消し線
</button>
<button
className={styles.button}
onClick={() => editor.chain().focus().toggleItalic().run()}
>
斜体
</button>
</div>
<div className={styles.container}>
<EditorContent editor={editor} />
</div>
</>
);
ボタンがクリックされたときにeditor
から複数のコマンド(メソッド)を実行しています。
h1ボタンがクリックされたときの実行の流れは以下のようになります。
chain()
: 複数のコマンドを1度に呼び出すコマンドチェーンを作成する
focus()
: フォーカスをエディターに戻す
toggleHeading({ level: 1 })
: <h1>
タグに変更する(既に<h1>
のときは外す)
run()
: 作成したコマンドチェーンを実行する
メニューのスタイリングを追加します。
.menu {
display: flex;
gap: 15px;
}
.button {
border: 2px solid #19191a;
border-radius: 6px;
background-color: #ffff;
}
以上の実装が完了すると、下記のようになります。
ボタンをクリックすると、それぞれノードやマークが追加・適応されると思います。
8. エディタ内の文字数を表示する
次はエディタ内の文字数をリアルタイムで表示するUIを作成していきます。
この機能は Starterkit には入っていないので、こちらを参考にパッケージを追加する必要があります。
❯ yarn add @tiptap/extension-character-count
インストールが完了したらTiptap.tsx
内でインポートし、useEditor
のextensions
に追加します。
const editor = useEditor({
extensions: [StarterKit, CharacterCount],
content: "<p>Hello World! 🌎️</p>",
editorProps: {
attributes: {
class: styles.editorContent,
},
},
});
下記のようにタグを追加すれば完成です。
<div className={styles.container}>
<EditorContent editor={editor} />
</div>
<div>{editor.storage.characterCount.characters()}文字</div>
最後に
この記事ではWYSIWYGエディタを作成できるTiptapの基本的な使い方を解説しました。
WYSIWYGエディタと聞くと実装が難しいように思えますが、Tiptapを使えばサクッと実装できるのでオススメです。
今回作成したコードはGitHubに載せているので、よろしければご覧ください。
Discussion