Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Use TipTap <EditorProvider /> with <EditorContent />?

Background

I'm using React, Next.js, and TipTap as part of a project. I have been using TipTap with useEditor initially, but to allow more flexibility and prevent the need to pass the editor to every child component in the editor, I have been refactoring to use <EditorProvider /> instead.

Issue Summary

When using <EditorProvider>, the code seems to ignore <EditorContent> and place content at the top of the <EditorProvider /> children.

Example Code:

I have separated the Editor into its own reusable component <BlockEditor> that can be nested inside a <EditorProvider />

BlockEditor.js

const BlockEditor = () => {
    const { editor } = useCurrentEditor();

    return (
        <>
            <FloatingMenu editor={null}>
                <BlockMenu />
            </FloatingMenu>
            <BubbleMenu editor={null} className="flex space-x-2">
                <TextMenu />
                <ImageMenu />
                <GalleryMenu />
            </BubbleMenu>
            <EditorContent editor={editor} />
        </>
    );
};

Then I use the<BlockEditor> inside a page or other client component, where I want to control the layout so that other components on the page can access the editor via const { editor } = useCurrentEditor();

ExampleEditor.js

export default function ExampleEditor() {

    // define your extension array
    const extensions = [
        StarterKit
    ];

    const content = `
        <h1>
            This is a test,
        </h1>
    `;

return (
    <EditorProvider
        extensions={extensions}
        content={content}
    >
        <div className="w-full flex-1 flex overflow-hidden">
            {/* Editor */}
            <div className="h-full flex flex-col min-h-200 flex-grow overflow-hidden">
                <div className="w-full p-4 overflow-hidden h-full">
                    <BlockEditor />
                </div>
            </div>

            {/* Settings aside */}

            <div className="h-full w-[350px] border-l border-neutral-200 bg-gray-50">
                <TextInspector />
                <ImageInspector />
                <GalleryInspector />
            </div>
        </div>
    </EditorProvider>
);

}

In this example <TextInspector />, <ImageInspector />, <GalleryInspector /> are all using useCurrentEditor(); to get access to the editor and provide some edit utility to the active element.

The Problem Details

The issue is that the EditorProvider seems to render the content at the top of its children and not where the BlockEditor.js <EditorContent> is. This wasn't an issue when using useEditor, but it seems to be now.

There isn't much documentation on <EditorProvider>, so I'm not sure if it supports <EditorContent> or not, and how I should be using it correctly. If there are any examples of how I should be using and nesting < EditorProvider> and <EditorContent>, it would be a big help.

Note: The other odd thing I've noticed is that occasionally, when running the Next.js app in dev mode and updating the code, it renders correctly for a second, before reverting back to placing the content at the top of the Provider children. I thought this might indicate some server-side rendering issue; however, all Editor components are using "use client".

like image 483
Levi Putna Avatar asked Sep 02 '25 03:09

Levi Putna


1 Answers

Diving into the code in node_modules for EditorProvider, this is how it is defined:

export function EditorProvider({
  children, slotAfter, slotBefore, editorContainerProps = {}, ...editorOptions
}: EditorProviderProps) {
  const editor = useEditor(editorOptions)

  if (!editor) {
    return null
  }

  return (
    <EditorContext.Provider value={{ editor }}>
      {slotBefore}
      <EditorConsumer>
        {({ editor: currentEditor }) => (
          <EditorContent editor={currentEditor} {...editorContainerProps} />
        )}
      </EditorConsumer>
      {children}
      {slotAfter}
    </EditorContext.Provider>
  )
}

You can use editorContainerProps to set the EditorContent related props. You do not need to use EditorContent separately.

<EditorProvider
  extensions={extensions}
  content={content}
  editorContainerProps={/* any EditorContent prop you want to set */}
>
  {/* Editor code */}
</EditorProvider>```
like image 156
UsamaMan Avatar answered Sep 04 '25 18:09

UsamaMan