Tiptap is an open source headless wrapper around ProseMirror. ProseMirror is a toolkit for building rich text WYSIWYG editors. The best part about Tiptap is that it's headless, which means you can customize and create your rich text editor however you want. I'll be using TailwindCSS for this tutorial.
How to get Tiptap working
Step 1 - Install Tiptap into your project
Tiptap is framework-agnostic, it integrates with many popular frameworks, even PHP and vanilla JavaScript.
There are three packages (@tiptap/react
, @tiptap/pm
, and @tiptap/starter-kit
) you need to install that provide all the necessary extensions for you to add Tiptap to your Next.js project.
npm install @tiptap/react @tiptap/pm @tiptap/starter-kit
Step 2 - Create a Tiptap component
To use Tiptap in your next.js project, you need to add a new Component. And add this piece of code to it.
'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>',
})
return (
<EditorContent editor={editor} />
)
}
export default Tiptap
Let me break it down. Starterkit
is a collection of the most useful extensions in Tiptap for getting started. Extensions are a powerful way to extend the functionality of your Tiptap editor, for example, the History plugin that helps you track changes when editing your document and provides redo and undo options. useEditor
is a hook provided by Tiptap that it initializes an editor instance. This instance provides the interface through which you can interact with the editor's functionality. EditorContent
binds your editor instance and the DOM element where your content is rendered. The content property is optional, but it determines the initial state of your editor. That's all you need to get your rich text editor up and running with Tiptap.
Customizing your editor
Since Tiptap comes raw, it allows you to choose how your editor looks like. By default, it doesn't come with a menu bar for editing your document, but you can add it by using the slotBefore
prop in EditorContent
. What the slotBefore
prop does is that it tells EditorContent
that there should be some content at the top of the editor. To create our menu bar, we can just use buttons. Let's create a menu bar and add a button to toggle boldness.
<div className="flex flex-wrap gap-2">
<button
onClick={() => editor.chain().focus().toggleBold().run()}
disabled={!editor.can().chain().focus().toggleBold().run()}
className={editor.isActive("bold") ? "is-active" : ""}
>
bold
</button>
</div>
That feels like a lot, doesn't it? Let me take it slowly. The commands that are passed into the onClick
prop, and the disabled
prop are a chain of commands. Each of them is explained below:
-
editor
should be a Tiptap instance, -
chain()
is used to tell the editor you want to execute multiple commands, -
focus()
sets the focus back to the editor, -
toggleBold()
marks the selected text bold, or removes the bold mark from the text selection if it’s already applied and -
run()
will execute the chain. -
isActive()
checks if something is applied to the selected text already. -
!editor.can()
: Checks if thetoggleBold
command can be executed. If it can't, the!
operator negates the result.
I'm also using an is-active
class to provide UI feedback when the button is toggled.
Now, the Tiptap component should look like this:
'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>',
})
return (
<EditorContent editor={editor} slotBefore={<div className="flex flex-wrap gap-2">
<button
onClick={() => editor.chain().focus().toggleBold().run()}
disabled={!editor.can().chain().focus().toggleBold().run()}
className={editor.isActive("bold") ? "is-active" : ""}
>
bold
</button>
</div>} />
)
}
export default Tiptap
You can add more buttons to toggle more things in your text like headings, paragraphs, redo/undo, blockquotes, bullet lists, and a lot more.
(Optional) Use the @tailwindcss/typography
to get the best typography styles for your editor
The @tailwindcss/typography
plugin applies typography styles to your HTML elements that follow best practices. By default, TailwindCSS applies minimalistic styles and it may not include detailed typography styles. The content on your Tiptap editor is rendered as HTML by using this plugin you're telling your editor to inherit the typography styles provided by TailwindCSS such as margins, font styles, etc.
npm install --save-dev @tailwindcss/typography
You can use it by installing and then adding it to your list of plugins in your tailwind.config.ts
or tailwind.config.js
file:
import { defineConfig } from 'tailwindcss';
export default defineConfig({
mode: 'jit',
purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [
require('@tailwindcss/typography'), // Add the typography plugin
// other plugins
],
});
All you have to do is to add the plugin. Your Tailwind config file might look different from this, but it's only the plugin that's necessary.
Generating an output from your editor
There are two ways you can get your content from Tiptap. The first way is through JSON, and the second option is with HTML. Tiptap doesn't support Markdown as an output format. But it supports Markdown when you're editing your content. Here's an example in code:
const json = editor.getJSON()
And for HTML:
const html = editor.getHTML()
You can also listen for changes to store your content continuously as it changes. By adding the onUpdate
hook provided by Tiptap that is triggered on every change, generating the data, and storing it:
const editor = useEditor({
extensions: [
StarterKit,
],
// triggered on every change
onUpdate: ({ editor }) => {
const json = editor.getJSON()
// send the content to an API here
},
content: '<p>Hello World! 🌎️</p>',
})
There's also one last option for storing your content, which is y.js. You can head to Tiptap's official docs to learn more about storing your content and how to customize it better.
That's it, that's all you need to get started with Tiptap in your Next.js project.
You can hear more from me on:
Twitter (X) | Instagram
Top comments (1)
Tiptap has limited some extensions now how can I create text uppercase, lowercase, or capitalized extension