Frequently Asked Questions
When I copy the content of the editor, into a text field I get a bunch of newlines
By default, in Tiptap, the clipboard text serializer is set to produce paragraphs with 2 newlines in between (like you would do in Markdown).
So you'd paste content and it would look like this:
This is a paragraph.
This is another paragraph.
When you probably want it to look like this (with no extra newline between paragraphs):
This is a paragraph.
This is another paragraph.
To do this you have to change the clipboardTextSerializer
to use a single newline instead of two.
Like this:
new Editor({
// other options
coreExtensionOptions: {
clipboardTextSerializer: {
blockSeparator: '\n',
},
},
})
Which will make the clipboard serializer use a single newline as a separator between blocks.
My drag and drop isn't working
Some libraries like react-dnd
or react-beautiful-dnd
might interfere with the drag & drop functionality of Tiptap, by registering themselves globally.
If you're using one of these libraries, you might have to disable them (or at least confine the elements that they listen to) for the drag and drop functionality of the editor to work properly.
Why both parseHTML
and renderHTML
parseHTML
and renderHTML
can be thought of as opposites:
parseHTML
: HTML -> JSON, taking the HTML representation and parsing that as JSON.renderHTML
: JSON -> HTML, taking the JSON representation and outputting that as HTML to render in the editor.
This is what allows Tiptap to be so flexible with its content representation. You can define your content as JSON, and then render it as HTML, or you can define your content as HTML, and then parse it into JSON.
Additionally, parseHTML
and renderHTML
can also be used in different contexts:
renderHTML
is used during copy events, because what you copy from an editor needs to be serialized as HTML to go into your clipboard.
parseHTML
is used during paste events, because that HTML in your clipboard is serialized as HTML and needs to be parsed into JSON.
On initial render, if your content is defined as JSON, parseHTML
is completely skipped, and would only be used for pastes.
Whereas your renderHTML
method determines how the content is represented as HTML within the editor.
renderHTML
can be overridden within the editor view with node views and mark views, to allow for custom rendering of nodes and marks.
I want to edit HTML content with Tiptap
Tiptap is based on Prosemirror, and it is not an arbitrary HTML editor, it is a rich text editor that needs to have full control of the HTML that it is editing to make changes to it. Prosemirror does this by attempting to parse the content in such a way that it conforms to a schema (which is derived from the nodes & marks that you specify as Tiptap extensions), this means content which does not conform to the schema WILL BE LOST. This is a tradeoff, Prosemirror can accept a fair amount of HTML content & parse it into a structured format but it cannot reliably do so with arbitrary HTML (liberal in what it accepts, strict in what it outputs).
One common situation where this can occur, is attempting to nest marks of different types like: <span class="my-class"><span style="color: red">editable text</span></span>
these two marks cannot be nested like this and Prosemirror will attempt to correct it by dropping one of them to abide by the rule that marks cannot be wrapping each other. To fix this, the HTML that is input into the editor should be in the format of one element with multiple attributes like: <span class="my-class" style="color: red">editable text</span>
, this would be correctly parsed by Prosemirror as attempting to apply two marks to the text.
There is not a way to configure this behavior in Prosemirror, as it is already complex enough to parse arbitrary HTML. Depending on the source of the HTML you are attempting to parse, you can:
- Rely on the default behavior & drop the unknown content
- Attempt to parse the HTML yourself with a custom HTML parser and make it conform to what Prosemirror would expect (e.g. to merge spans)
React context is not working with NodeViews
To use React context within a NodeView, you need to wrap the EditorContent component with the context provider. Within the nodeview, the context should be available within the NodeView component.
import React from 'react'
import { EditorContent } from '@tiptap/react'
const TiptapEditor = ({ editor }) => {
return (
<MyContext.Provider value={{ foo: 'bar' }}>
<EditorContent editor={editor} />
</MyContext.Provider>
)
}
Why are there extra divs when I use a React Node View
The reason that these divs exist is that prosemirror expects a node view to be generated synchronously, whereas React can only render asynchronously. So we inject an element, and later let React asynchronously render into that element. Causing there to be a wrapping div in the DOM.
Similarly, when using a NodeViewContent
there is an element, which has created by React, and then Prosemirror renders into that element. This has to exist as a handoff from React to Prosemirror, because React's default behavior is to clear the element that it is rendering into, and we need to not have React destroy the element that prosemirror is trying to render the rich text into.
So, all of this to say, there is no good way to get rid of these intermediate elements because of how React works. You'd either have to use the JS NodeView API, or Vue because as either of those have more sane rendering approaches.