Overview
Excalidraw can be directly embedded as a React component in your application. This guide demonstrates how to set up and use Excalidraw with React, including advanced features like custom UI, state management, and export functionality.
Installation
Install dependencies
Install Excalidraw and its peer dependencies using npm or yarn: npm install react react-dom @excalidraw/excalidraw
# or
yarn add react react-dom @excalidraw/excalidraw
To use unreleased changes, install @excalidraw/excalidraw@next
Import styles
Import the Excalidraw CSS in your component: import "@excalidraw/excalidraw/index.css" ;
Configure asset path (optional)
For self-hosting fonts, set the asset path before rendering: window . EXCALIDRAW_ASSET_PATH = "/" ;
Copy fonts from node_modules/@excalidraw/excalidraw/dist/prod/fonts to your public directory.
Basic Usage
Here’s a minimal React component using Excalidraw:
import React from "react" ;
import { Excalidraw } from "@excalidraw/excalidraw" ;
import "@excalidraw/excalidraw/index.css" ;
export default function App () {
return (
< div style = { { height: "100vh" , width: "100vw" } } >
< Excalidraw />
</ div >
);
}
Excalidraw takes 100% of its container’s width and height. Ensure the parent element has non-zero dimensions.
Advanced Integration
Setting up the API Reference
Access Excalidraw’s imperative API to control the editor programmatically:
import React , { useState } from "react" ;
import { Excalidraw , type ExcalidrawImperativeAPI } from "@excalidraw/excalidraw" ;
import "@excalidraw/excalidraw/index.css" ;
export default function App () {
const [ excalidrawAPI , setExcalidrawAPI ] =
useState < ExcalidrawImperativeAPI | null >( null );
return (
< div style = { { height: "100vh" } } >
< Excalidraw excalidrawAPI = { ( api ) => setExcalidrawAPI ( api ) } />
</ div >
);
}
Loading Initial Data
Load elements and configure the initial state:
import React from "react" ;
import { Excalidraw , convertToExcalidrawElements } from "@excalidraw/excalidraw" ;
import type { ExcalidrawInitialDataState } from "@excalidraw/excalidraw/types" ;
const initialData : ExcalidrawInitialDataState = {
elements: convertToExcalidrawElements ([
{
type: "rectangle" ,
x: 10 ,
y: 10 ,
strokeWidth: 2 ,
id: "rect-1" ,
},
{
type: "diamond" ,
x: 120 ,
y: 20 ,
backgroundColor: "#fff3bf" ,
strokeWidth: 2 ,
label: {
text: "HELLO EXCALIDRAW" ,
strokeColor: "#099268" ,
fontSize: 30 ,
},
id: "diamond-1" ,
},
{
type: "arrow" ,
x: 100 ,
y: 200 ,
label: { text: "HELLO WORLD!!" },
start: { type: "rectangle" },
end: { type: "ellipse" },
},
]),
appState: {
viewBackgroundColor: "#AFEEEE" ,
currentItemFontFamily: 5
},
scrollToContent: true ,
};
export default function App () {
return (
< div style = { { height: "100vh" } } >
< Excalidraw initialData = { initialData } />
</ div >
);
}
Event Handlers
onChange
onPointerUpdate
onLinkOpen
import React from "react" ;
import { Excalidraw } from "@excalidraw/excalidraw" ;
import type { NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types" ;
import type { AppState } from "@excalidraw/excalidraw/types" ;
export default function App () {
const handleChange = (
elements : NonDeletedExcalidrawElement [],
state : AppState
) => {
console . log ( "Elements:" , elements );
console . log ( "App state:" , state );
};
return (
< div style = { { height: "100vh" } } >
< Excalidraw onChange = { handleChange } />
</ div >
);
}
Custom UI Components
Excalidraw provides composable UI components for customization:
import React , { useState } from "react" ;
import {
Excalidraw ,
Footer ,
WelcomeScreen ,
MainMenu ,
Sidebar
} from "@excalidraw/excalidraw" ;
export default function App () {
return (
< div style = { { height: "100vh" } } >
< Excalidraw >
< WelcomeScreen />
< MainMenu >
< MainMenu.DefaultItems.SaveAsImage />
< MainMenu.DefaultItems.Export />
< MainMenu.Separator />
< MainMenu.DefaultItems.Help />
< MainMenu.ItemCustom >
< button onClick = { () => alert ( "Custom action!" ) } >
Custom Menu Item
</ button >
</ MainMenu.ItemCustom >
</ MainMenu >
< Sidebar name = "custom" >
< Sidebar.Tabs >
< Sidebar.Header />
< Sidebar.Tab tab = "one" > Content for tab one </ Sidebar.Tab >
< Sidebar.Tab tab = "two" > Content for tab two </ Sidebar.Tab >
< Sidebar.TabTriggers >
< Sidebar.TabTrigger tab = "one" > Tab 1 </ Sidebar.TabTrigger >
< Sidebar.TabTrigger tab = "two" > Tab 2 </ Sidebar.TabTrigger >
</ Sidebar.TabTriggers >
</ Sidebar.Tabs >
</ Sidebar >
< Footer >
< div style = { { padding: "10px" } } >
Custom footer content
</ div >
</ Footer >
</ Excalidraw >
</ div >
);
}
Export Functionality
Export drawings to various formats:
Export to PNG
Export to SVG
Copy to Clipboard
import React , { useState } from "react" ;
import {
Excalidraw ,
exportToBlob ,
type ExcalidrawImperativeAPI
} from "@excalidraw/excalidraw" ;
export default function App () {
const [ excalidrawAPI , setExcalidrawAPI ] =
useState < ExcalidrawImperativeAPI | null >( null );
const exportToPNG = async () => {
if ( ! excalidrawAPI ) return ;
const blob = await exportToBlob ({
elements: excalidrawAPI . getSceneElements (),
mimeType: "image/png" ,
appState: excalidrawAPI . getAppState (),
files: excalidrawAPI . getFiles (),
});
const url = URL . createObjectURL ( blob );
const link = document . createElement ( "a" );
link . href = url ;
link . download = "drawing.png" ;
link . click ();
};
return (
<>
< button onClick = { exportToPNG } > Export to PNG </ button >
< div style = { { height: "calc(100vh - 40px)" } } >
< Excalidraw excalidrawAPI = { ( api ) => setExcalidrawAPI ( api ) } />
</ div >
</>
);
}
Configuration Options
Customize Excalidraw’s behavior with props:
import React from "react" ;
import { Excalidraw } from "@excalidraw/excalidraw" ;
export default function App () {
return (
< div style = { { height: "100vh" } } >
< Excalidraw
viewModeEnabled = { false }
zenModeEnabled = { false }
gridModeEnabled = { true }
renderScrollbars = { true }
theme = "light"
name = "My Drawing"
UIOptions = { {
canvasActions: {
loadScene: false ,
},
tools: {
image: true
},
} }
validateEmbeddable = { true }
/>
</ div >
);
}
Working with Images
Add binary files (images) to your scene:
import React , { useEffect , useState } from "react" ;
import {
Excalidraw ,
MIME_TYPES ,
convertToExcalidrawElements ,
type ExcalidrawImperativeAPI
} from "@excalidraw/excalidraw" ;
import type { BinaryFileData , FileId } from "@excalidraw/excalidraw/types" ;
export default function App () {
const [ excalidrawAPI , setExcalidrawAPI ] =
useState < ExcalidrawImperativeAPI | null >( null );
useEffect (() => {
if ( ! excalidrawAPI ) return ;
const loadImage = async () => {
const res = await fetch ( "/path/to/image.jpg" );
const imageData = await res . blob ();
const reader = new FileReader ();
reader . readAsDataURL ( imageData );
reader . onload = function () {
const imageFile : BinaryFileData = {
id: "image-1" as FileId ,
dataURL: reader . result as string ,
mimeType: MIME_TYPES . jpg ,
created: Date . now (),
lastRetrieved: Date . now (),
};
excalidrawAPI . addFiles ([ imageFile ]);
// Add image element to scene
const imageElement = convertToExcalidrawElements ([{
type: "image" ,
x: 100 ,
y: 100 ,
width: 200 ,
height: 200 ,
fileId: "image-1" as FileId ,
}]);
excalidrawAPI . updateScene ({
elements: [
... excalidrawAPI . getSceneElements (),
... imageElement ,
],
});
};
};
loadImage ();
}, [ excalidrawAPI ]);
return (
< div style = { { height: "100vh" } } >
< Excalidraw excalidrawAPI = { ( api ) => setExcalidrawAPI ( api ) } />
</ div >
);
}
Scene Management
Update and manipulate the scene:
import React , { useState } from "react" ;
import {
Excalidraw ,
convertToExcalidrawElements ,
restoreElements ,
ROUNDNESS ,
type ExcalidrawImperativeAPI
} from "@excalidraw/excalidraw" ;
export default function App () {
const [ excalidrawAPI , setExcalidrawAPI ] =
useState < ExcalidrawImperativeAPI | null >( null );
const updateScene = () => {
if ( ! excalidrawAPI ) return ;
const sceneData = {
elements: restoreElements (
convertToExcalidrawElements ([
{
type: "rectangle" ,
id: "rect-1" ,
fillStyle: "hachure" ,
strokeWidth: 1 ,
strokeStyle: "solid" ,
roughness: 1 ,
angle: 0 ,
x: 100 ,
y: 100 ,
strokeColor: "#c92a2a" ,
width: 186 ,
height: 141 ,
roundness: {
type: ROUNDNESS . ADAPTIVE_RADIUS ,
value: 32 ,
},
},
{
type: "text" ,
x: 300 ,
y: 100 ,
text: "HELLO WORLD!" ,
},
]),
null
),
appState: {
viewBackgroundColor: "#edf2ff" ,
},
};
excalidrawAPI . updateScene ( sceneData );
};
const resetScene = () => {
excalidrawAPI ?. resetScene ();
};
return (
<>
< button onClick = { updateScene } > Update Scene </ button >
< button onClick = { resetScene } > Reset Scene </ button >
< div style = { { height: "calc(100vh - 40px)" } } >
< Excalidraw excalidrawAPI = { ( api ) => setExcalidrawAPI ( api ) } />
</ div >
</>
);
}
Live Example
Check out the complete working example on CodeSandbox .
Next Steps