How to Embed Excalidraw Drawings in the React TipTap Editor: A Complete Implementation Guide

The reactjs-tiptap-editor library ships with a dedicated Excalidraw extension that treats each drawing as a block-level node, rendering static SVG previews in the document and providing an interactive canvas for editing.

Embedding Excalidraw drawings directly into rich-text content requires specialized handling for both data storage and rendering. The hunghg255/reactjs-tiptap-editor repository provides a complete solution by implementing a custom TipTap node that stores Excalidraw JSON data and renders it as scalable vector graphics. This architecture cleanly separates the heavy interactive editor from lightweight preview rendering, ensuring optimal performance while maintaining full drawing capabilities.

Extension Architecture and Core Components

The Excalidraw integration follows a three-layer architecture: node definition, view rendering, and user interface controls.

Node Definition in Excalidraw.ts

At src/extensions/Excalidraw/Excalidraw.ts, the extension registers a TipTap Node named excalidraw. This declaration specifies three critical attributes:

  • width – Controls the display width of the embedded drawing
  • height – Defines the vertical dimensions
  • data – Stores the complete Excalidraw JSON serialization

The file also exposes the setExcalidraw command, which either updates an existing node or inserts a new one at the current cursor position. According to the source code, this command accepts an object containing the drawing data and optional dimension parameters.

SVG Preview Rendering with NodeViewExcalidraw

The NodeViewExcalidraw.tsx component handles how drawings appear within the document. It lazily imports the exportToSvg helper from the Excalidraw package to convert the stored JSON into a static SVG representation. This approach avoids loading the full Excalidraw library during initial page render, significantly improving bundle size and load times.

The component wraps the SVG in a resizable container, allowing users to adjust dimensions directly within the editor interface. When the node is selected, resize handles appear, modifying the width and height attributes stored in the document model.

Rich-Text Interface Components

Two React components provide the user interface for creating and editing drawings:

RichTextExcalidraw.tsx – Located at src/extensions/Excalidraw/components/RichTextExcalidraw.tsx, this toolbar button opens a modal dialog containing the full @excalidraw/excalidraw canvas. The component dynamically imports the heavy drawing library only when the user clicks the button. Upon saving, it executes editor.chain().focus().setExcalidraw({ data }) to embed the drawing.

RichTextBubbleExcalidraw.tsx – Found at src/components/Bubble/RichTextBubbleExcalidraw.tsx, this bubble menu appears when selecting an existing drawing node. It provides quick actions for size adjustment, deletion, and toggling the drawing picker without accessing the main toolbar.

Implementing the Excalidraw Extension

To embed Excalidraw drawings in your React TipTap editor, you must register the extension, add the toolbar control, and render the editor instance.

Register the Extension

Import the Excalidraw node from the package and include it in your extensions array:

import { Excalidraw } from 'reactjs-tiptap-editor/excalidraw';
import { Document, Text, Paragraph } from '@tiptap/extension-document';
import { useEditor } from '@tiptap/react';

const extensions = [
  Document,
  Text,
  Paragraph,
  // ...other core extensions
  Excalidraw,                 // ← register the node
];

const editor = useEditor({ extensions });

Add the Toolbar Button

Include the RichTextExcalidraw component in your toolbar to provide the drawing interface:

import { RichTextExcalidraw } from 'reactjs-tiptap-editor/excalidraw';

const RichTextToolbar = () => (
  <>
    {/* other toolbar buttons */}
    <RichTextExcalidraw />   {/* opens the Excalidraw modal */}
  </>
);

Complete Editor Setup

Wrap your components with the RichTextProvider and render the editable area:

import { RichTextProvider, EditorContent } from 'reactjs-tiptap-editor';

export const App = () => {
  const editor = useEditor({ 
    extensions: [
      Document,
      Text,
      Paragraph,
      Excalidraw,
      // ...other extensions
    ] 
  });

  return (
    <RichTextProvider editor={editor}>
      <RichTextToolbar />
      <EditorContent editor={editor} />
    </RichTextProvider>
  );
};

Programmatic Drawing Insertion

Beyond the UI controls, you can programmatically embed Excalidraw drawings using the setExcalidraw command.

Basic Programmatic Insertion

Invoke the command chain to insert a drawing at the current cursor position:

// `drawingData` must be the JSON format produced by Excalidraw
editor
  .chain()
  .focus()
  .setExcalidraw({ data: drawingData, width: '100%', height: 300 })
  .run();

Handling External Events

The repository demonstrates advanced integration patterns in playground/src/App.tsx, where drawings can be inserted in response to external events. Using the custom event bus, you can listen for Excalidraw data from outside the editor:

import { EVENTS } from '@/utils/customEvents/events.constant';
import { useListener } from '@/components/ReactBus';
import { useEditorInstance } from '@/store/editor';

const editor = useEditorInstance();
const EVENT_ID = EVENTS.EXCALIDRAW((editor as any).id);

useListener((payload) => {
  // `payload.data` is the Excalidraw JSON
  editor.chain().focus().setExcalidraw({ data: payload.data }).run();
}, [EVENT_ID]);

This pattern enables integrations with file dropzones, external drawing managers, or collaborative editing systems.

Summary

  • Excalidraw Node Definition: The extension at src/extensions/Excalidraw/Excalidraw.ts defines a block-level node with width, height, and data attributes, plus the setExcalidraw command for insertion.
  • Performance-Optimized Rendering: NodeViewExcalidraw.tsx renders drawings as SVGs using lazy-loaded exportToSvg, keeping the initial bundle size small while supporting resizable previews.
  • Interactive Editing: RichTextExcalidraw.tsx dynamically imports the full Excalidraw canvas only when needed, embedding results via the setExcalidraw command chain.
  • Contextual Controls: RichTextBubbleExcalidraw.tsx provides bubble-menu actions for quick adjustments to existing drawings.
  • Programmatic API: The setExcalidraw command supports both manual insertion and event-driven workflows, accepting drawing JSON and dimension parameters.

Frequently Asked Questions

How do I install the Excalidraw extension for reactjs-tiptap-editor?

The Excalidraw extension ships as part of the main reactjs-tiptap-editor package. Import it from the reactjs-tiptap-editor/excalidraw subpath and add it to your TipTap extensions array. The heavy @excalidraw/excalidraw dependency is loaded on-demand only when users open the drawing modal, so it won't bloat your initial JavaScript bundle.

What data format does the Excalidraw extension store in the document?

The extension stores the native Excalidraw JSON serialization in the data attribute of the node. This is the same format produced by Excalidraw's serialization methods, containing all scene elements, app state, and binding information. The width and height attributes store display dimensions separately, allowing you to resize the visual representation without modifying the underlying drawing data.

Can I edit an existing Excalidraw drawing after embedding it?

Yes. When you select an embedded drawing in the editor, the bubble menu provided by RichTextBubbleExcalidraw.tsx offers an edit action that reopens the full Excalidraw canvas with the existing data loaded. You can modify the diagram and save changes back to the document using the same setExcalidraw command, which updates the node in place.

How do I set default dimensions for all embedded Excalidraw drawings?

Pass width and height parameters when calling setExcalidraw programmatically, or modify the default attribute values in the node configuration when initializing the editor. The SVG preview container respects these attributes for initial rendering, though users can still resize individual drawings via the drag handles provided by NodeViewExcalidraw.tsx unless you disable the resizable node view.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →