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 drawingheight– Defines the vertical dimensionsdata– 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.tsdefines a block-level node withwidth,height, anddataattributes, plus thesetExcalidrawcommand for insertion. - Performance-Optimized Rendering:
NodeViewExcalidraw.tsxrenders drawings as SVGs using lazy-loadedexportToSvg, keeping the initial bundle size small while supporting resizable previews. - Interactive Editing:
RichTextExcalidraw.tsxdynamically imports the full Excalidraw canvas only when needed, embedding results via thesetExcalidrawcommand chain. - Contextual Controls:
RichTextBubbleExcalidraw.tsxprovides bubble-menu actions for quick adjustments to existing drawings. - Programmatic API: The
setExcalidrawcommand 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →