はじめに
@Sicut_study さんの【Reactアプリ100本ノック】09 Memo をクリアするために、Tiptapを使用したので簡単に使い方をまとめます。
成果物
Tiptapとは
リッチなテキストエディタをつくることができるものです。
多くの拡張機能が用意されているのが特徴です。
標準でエディタにスタイリングされていないため、UIを自由に実装できます。(このことをヘッドレスっていうのかな?)
セットアップ
Tiptapは以下の環境での使用を提供しています。
- Vanilla JavaScript
- React
- Next.js
- Vue 3
- Vue 2
- Nuxt.js
- Svelte
- Alpine.js
- PHP
- CDN
私は今回Next.js
でセットアップをしていきます。
プロジェクト作成
npx create-next-app my-tiptap-project
作成したプロジェクトに移動
cd my-tiptap-project
インストール
tiptap
のインストールのついでに、便利なスターターキット
もインストールします。
npm install @tiptap/react @tiptap/pm @tiptap/starter-kit
Tiptap.tsxを作成
/components/Tiptap.tsx
を作成して以下を貼り付けましょう。
'use client'
import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
const Tiptap = () => {
const editor = useEditor({
extensions: [
StarterKit,
],
content: '<p>Hello World! 🌎️</p>',
})
if (!editor) {
return null
}
return (
<EditorContent editor={editor} />
)
}
export default Tiptap
page.tsxを作成
先ほど作ったTiptap.tsx
を表示するためにapp/page.tsx
でインポートしましょう。
import Tiptap from '../components/Tiptap'
export default function Home() {
return (
<Tiptap />
)
}
確認
正常に動いているかローカルサーバを立ち上げて確認しましょう。
npm run dev
下の画像のようになっていれば成功です。
ちょっといじる
このままではしょぼいのでちょっといじりましょう。
拡張機能
沢山の拡張機能があるのでいくつかご紹介します。
ToolMenu.tsxを作成
ツールを表示するコンポーネント/components/ToolMenu.tsx
を作成します。
import { Editor } from '@tiptap/react'
import {
MdFormatBold,
MdFormatStrikethrough,
MdRedo,
MdUndo,
} from 'react-icons/md'
const ToolMenu = ({ editor }: { editor: Editor }) => {
if (!editor) {
return null
}
return (
<div className="flex flex-wrap gap-2 border-b border-gray-600 p-4 text-2xl">
<button
type="button"
onClick={() => editor.chain().focus().toggleBold().run()}
className={!editor.isActive('bold') ? 'opacity-20' : ''}
>
<MdFormatBold />
</button>
<button
type="button"
onClick={() => editor.chain().focus().toggleStrike().run()}
className={!editor.isActive('strike') ? 'opacity-20' : ''}
>
<MdFormatStrikethrough />
</button>
<button
onClick={() => editor.chain().focus().undo().run()}
type="button"
>
<MdUndo />
</button>
<button
onClick={() => editor.chain().focus().redo().run()}
type="button"
>
<MdRedo />
</button>
</div>
)
}
export default ToolMenu
button
のonCilck
属性に色々メソッドを割り当てることで様々な機能を使用できます。
今回は、コードの上から順に、
- 太字
- 取り消し線
- 一つ戻る
- 一つ進む
を実装しています。
各ボタンのアイコンにはReact Iconsを使用しています。
確認
再度ローカルサーバを立ち上げて正常に実装できているか確認しましょう。
下の画像のようにツールメニューに4つのアイコンが表示されていれば成功です。
stateで内容を管理する
エディタの内容をstate
で管理したいことあると思います。
その方法を書いときます。
page.tsxを編集
+ 'use client'
import Tiptap from '@/features/Tiptap/components/Editor'
+ import { useState } from 'react'
export default function Home() {
+ const [sentence, setSentence] = useState<string>('初期値')
return (
<>
+ <p>state = {sentence}</p>
+ <Tiptap sentence={sentence} setSentence={setSentence} />
</>
)
}
useState
を使うので、先頭にuse client
を付けてクライアントコンポーネント
にします。
pタグ
でuseState
の内容を逐次表示します。
TipTap
コンポーネントにstate
と更新用関数
を渡します。
Tiptap.tsxを編集
'use client'
import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import ToolMenu from './ToolMenu'
+ type Props = {
+ sentence: string
+ setSentence: (sentence: string) => void
+ }
+ const Tiptap = ({ sentence, setSentence }: Props) => {
const editor = useEditor({
extensions: [StarterKit],
+ content: sentence,
editorProps: {
attributes: {
class: 'prose prose-base m-5 focus:outline-none',
},
},
})
+ if (editor) {
+ editor.on('update', () => {
+ setSentence(editor.getText())
+ })
+ }
if (!editor) {
return null
}
return (
<div className="w-2/3 mt-10 mx-auto border-gray-500 border-2">
<ToolMenu editor={editor} />
<div className="p-3 overflow-y-scroll h-[70vh] overflow-hidden mt-3">
<EditorContent editor={editor} />
</div>
</div>
)
}
export default Tiptap
content
にはエディタの初期値を格納できるので、useState
で管理するためsentence
を入れています。
Tiptap
が提供しているイベント
の中に、update
というものがあります。
これを使うことでエディタが更新されたら、何かしらの関数を発火させることができます。
editor.getText()
で現在のエディタの内容を文字列で取得し、setSentence
を使って、sentence
を更新しています。
確認
再度ローカルサーバを立ち上げて正常に実装できているか確認しましょう。
下の画像のようにエディタの内容と上のstate
が連動していれば成功です。
おわりに
便利ものツールはどんどん利用しましょう。
参考にさせていただいた記事
この方のコードをかなり参考にさせていただきました。