Note
Excalidraw
Excalidraw exported as a component to directly embed in your projects.
Installation
You can use npm
npm install react react-dom @excalidraw/excalidraw
or via yarn
yarn add react react-dom @excalidraw/excalidraw
After installation you will see a folder excalidraw-assets
and excalidraw-assets-dev
in dist
directory which contains the assets needed for this app in prod and dev mode respectively.
Move the folder excalidraw-assets
and excalidraw-assets-dev
to the path where your assets are served.
By default it will try to load the files from https://unpkg.com/@excalidraw/excalidraw/dist/
If you want to load assets from a different path you can set a variable window.EXCALIDRAW_ASSET_PATH
depending on environment (for example if you have different URL's for dev and prod) to the url from where you want to load the assets.
Note
If you don't want to wait for the next stable release and try out the unreleased changes you can use @excalidraw/excalidraw@next
.
Demo
Usage
Using Web Bundler
If you are using a Web bundler (for instance, Webpack), you can import it as an ES6 module as shown below
View Example
import React, { useEffect, useState, useRef } from "react";
import { Excalidraw } from "@excalidraw/excalidraw";
import InitialData from "./initialData";
import "./styles.scss";
export default function App() {
const excalidrawRef = useRef(null);
const [viewModeEnabled, setViewModeEnabled] = useState(false);
const [zenModeEnabled, setZenModeEnabled] = useState(false);
const [gridModeEnabled, setGridModeEnabled] = useState(false);
const updateScene = () => {
const sceneData = {
elements: [
{
type: "rectangle",
version: 141,
versionNonce: 361174001,
isDeleted: false,
id: "oDVXy8D6rom3H1-LLH2-f",
fillStyle: "hachure",
strokeWidth: 1,
strokeStyle: "solid",
roughness: 1,
opacity: 100,
angle: 0,
x: 100.50390625,
y: 93.67578125,
strokeColor: "#c92a2a",
backgroundColor: "transparent",
width: 186.47265625,
height: 141.9765625,
seed: 1968410350,
groupIds: [],
},
],
appState: {
viewBackgroundColor: "#edf2ff",
},
};
excalidrawRef.current.updateScene(sceneData);
};
return (
<div className="App">
<h1> Excalidraw Example</h1>
<div className="button-wrapper">
<button className="update-scene" onClick={updateScene}>
Update Scene
</button>
<button
className="reset-scene"
onClick={() => {
excalidrawRef.current.resetScene();
}}
>
Reset Scene
</button>
<label>
<input
type="checkbox"
checked={viewModeEnabled}
onChange={() => setViewModeEnabled(!viewModeEnabled)}
/>
View mode
</label>
<label>
<input
type="checkbox"
checked={zenModeEnabled}
onChange={() => setZenModeEnabled(!zenModeEnabled)}
/>
Zen mode
</label>
<label>
<input
type="checkbox"
checked={gridModeEnabled}
onChange={() => setGridModeEnabled(!gridModeEnabled)}
/>
Grid mode
</label>
</div>
<div className="excalidraw-wrapper">
<Excalidraw
ref={excalidrawRef}
initialData={InitialData}
onChange={(elements, state) =>
console.log("Elements :", elements, "State : ", state)
}
onPointerUpdate={(payload) => console.log(payload)}
onCollabButtonClick={() =>
window.alert("You clicked on collab button")
}
viewModeEnabled={viewModeEnabled}
zenModeEnabled={zenModeEnabled}
gridModeEnabled={gridModeEnabled}
/>
</div>
</div>
);
}
To view the full example visit
Since Excalidraw doesn't support server side rendering yet, you should render the component once the host is mounted.
import { useState, useEffect } from "react";
export default function IndexPage() {
const [Comp, setComp] = useState(null);
useEffect(() => {
import("@excalidraw/excalidraw").then((comp) => setComp(comp.default));
}, []);
return <>{Comp && <Comp />}</>;
}
The types
are available at @excalidraw/excalidraw/types
, you can view example for typescript
In Browser
To use it in a browser directly:
For development use
<script
type="text/javascript"
src="https://unpkg.com/@excalidraw/excalidraw/dist/excalidraw.development.js"
></script>
For production use
<script
type="text/javascript"
src="https://unpkg.com/@excalidraw/excalidraw/dist/excalidraw.production.min.js"
></script>
You will need to make sure react
, react-dom
is available as shown in the below example. For prod please use the production versions of react
, react-dom
.
View Example
<!DOCTYPE html>
<html>
<head>
<title>Excalidraw in browser</title>
<meta charset="UTF-8" />
<script src="https://unpkg.com/react@16.14.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.13.1/umd/react-dom.development.js"></script>
<script
type="text/javascript"
src="https://unpkg.com/@excalidraw/excalidraw/dist/excalidraw.development.js"
></script>
</head>
<body>
<div class="container">
<h1>Excalidraw Embed Example</h1>
<div id="app"></div>
</div>
<script type="text/javascript" src="src/index.js"></script>
</body>
</html>
/*eslint-disable */
import "./styles.css";
import InitialData from "./initialData";
const App = () => {
const excalidrawRef = React.useRef(null);
const [viewModeEnabled, setViewModeEnabled] = React.useState(false);
const [zenModeEnabled, setZenModeEnabled] = React.useState(false);
const [gridModeEnabled, setGridModeEnabled] = React.useState(false);
const updateScene = () => {
const sceneData = {
elements: [
{
type: "rectangle",
version: 141,
versionNonce: 361174001,
isDeleted: false,
id: "oDVXy8D6rom3H1-LLH2-f",
fillStyle: "hachure",
strokeWidth: 1,
strokeStyle: "solid",
roughness: 1,
opacity: 100,
angle: 0,
x: 100.50390625,
y: 93.67578125,
strokeColor: "#c92a2a",
backgroundColor: "transparent",
width: 186.47265625,
height: 141.9765625,
seed: 1968410350,
groupIds: [],
},
],
appState: {
viewBackgroundColor: "#edf2ff",
},
};
excalidrawRef.current.updateScene(sceneData);
};
return React.createElement(
React.Fragment,
null,
React.createElement(
"div",
{ className: "button-wrapper" },
React.createElement(
"button",
{
className: "update-scene",
onClick: updateScene,
},
"Update Scene",
),
React.createElement(
"button",
{
className: "reset-scene",
onClick: () => excalidrawRef.current.resetScene(),
},
"Reset Scene",
),
React.createElement(
"label",
null,
React.createElement("input", {
type: "checkbox",
checked: viewModeEnabled,
onChange: () => setViewModeEnabled(!viewModeEnabled),
}),
"View mode",
),
React.createElement(
"label",
null,
React.createElement("input", {
type: "checkbox",
checked: zenModeEnabled,
onChange: () => setZenModeEnabled(!zenModeEnabled),
}),
"Zen mode",
),
React.createElement(
"label",
null,
React.createElement("input", {
type: "checkbox",
checked: gridModeEnabled,
onChange: () => setGridModeEnabled(!gridModeEnabled),
}),
"Grid mode",
),
),
React.createElement(
"div",
{
className: "excalidraw-wrapper",
ref: excalidrawWrapperRef,
},
React.createElement(ExcalidrawLib.Excalidraw, {
initialData: InitialData,
onChange: (elements, state) =>
console.log("Elements :", elements, "State : ", state),
onPointerUpdate: (payload) => console.log(payload),
onCollabButtonClick: () => window.alert("You clicked on collab button"),
viewModeEnabled: viewModeEnabled,
zenModeEnabled: zenModeEnabled,
gridModeEnabled: gridModeEnabled,
}),
),
);
};
const excalidrawWrapper = document.getElementById("app");
ReactDOM.render(React.createElement(App), excalidrawWrapper);
To view the full example visit
Customizing styles
Excalidraw is using CSS variables to style certain components. To override them, you should set your own on the .excalidraw
and .excalidraw.theme--dark
(for dark mode variables) selectors.
Make sure the selector has higher specificity, e.g. by prefixing it with your app's selector:
.your-app .excalidraw {
--color-primary: red;
}
.your-app .excalidraw.theme--dark {
--color-primary: pink;
}
Most notably, you can customize the primary colors, by overriding these variables:
--color-primary
--color-primary-darker
--color-primary-darkest
--color-primary-light
-
--color-primary-contrast-offset
— a slightly darker (in light mode), or lighter (in dark mode)--color-primary
color to fix contrast issues (see Chubb illusion). It will fall back to--color-primary
if not present.
For a complete list of variables, check theme.scss, though most of them will not make sense to override.
Props
Name | Type | Default | Description |
---|---|---|---|
onChange |
Function | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw elements and the current app state. | |
initialData |
{elements?: ExcalidrawElement[], appState?: AppState } |
null | The initial data with which app loads. |
ref |
createRef | useRef | callbackRef | { current: { readyPromise: resolvablePromise } } |
Ref to be passed to Excalidraw | |
onCollabButtonClick |
Function | Callback to be triggered when the collab button is clicked | |
isCollaborating |
boolean |
This implies if the app is in collaboration mode | |
onPointerUpdate |
Function | Callback triggered when mouse pointer is updated. | |
langCode |
string | en |
Language code string |
renderTopRightUI |
Function | Function that renders custom UI in top right corner | |
renderFooter |
Function | Function that renders custom UI footer | |
renderCustomStats |
Function | Function that can be used to render custom stats on the stats dialog. | |
viewModeEnabled |
boolean | This implies if the app is in view mode. | |
zenModeEnabled |
boolean | This implies if the zen mode is enabled | |
gridModeEnabled |
boolean | This implies if the grid mode is enabled | |
libraryReturnUrl |
string | What URL should libraries.excalidraw.com be installed to | |
theme |
THEME.LIGHT | THEME.DARK | THEME.LIGHT | The theme of the Excalidraw component |
name |
string | Name of the drawing | |
UIOptions |
{ canvasActions: CanvasActions } |
DEFAULT UI OPTIONS | To customise UI options. Currently we support customising canvas actions
|
onPaste |
(data: ClipboardData, event: ClipboardEvent | null) => boolean |
Callback to be triggered if passed when the something is pasted in to the scene | |
detectScroll |
boolean | true | Indicates whether to update the offsets when nearest ancestor is scrolled. |
handleKeyboardGlobally |
boolean | false | Indicates whether to bind the keyboard events to document. |
onLibraryChange |
(items: LibraryItems) => void | Promise<any> |
The callback if supplied is triggered when the library is updated and receives the library items. | |
autoFocus |
boolean | false | Implies whether to focus the Excalidraw component on page load |
generateIdForFile |
`(file: File) => string | Promise` | Allows you to override id generation for files added on canvas |
onLinkOpen |
(element: NonDeletedExcalidrawElement, event: CustomEvent) |
This prop if passed will be triggered when link of an element is clicked. | |
onPointerDown |
(activeTool: AppState["activeTool"], pointerDownState: PointerDownState) => void |
This prop if passed gets triggered on pointer down evenets | |
onScrollChange |
(scrollX: number, scrollY: number) | This prop if passed gets triggered when scrolling the canvas. |
Dimensions of Excalidraw
Excalidraw takes 100%
of width
and height
of the containing block so make sure the container in which you render Excalidraw has non zero dimensions.
onChange
Every time component updates, this callback if passed will get triggered and has the below signature.
(excalidrawElements, appState, files) => void;
1.excalidrawElements
: Array of excalidrawElements in the scene.
2.appState
: AppState of the scene.
-
files
: The [BinaryFiles
](BinaryFiles which are added to the scene.
Here you can try saving the data to your backend or local storage for example.
initialData
This helps to load Excalidraw with initialData
. It must be an object or a promise which resolves to an object containing the below optional fields.
Name | Type | Description |
---|---|---|
elements |
ExcalidrawElement[] | The elements with which Excalidraw should be mounted. |
appState |
AppState | The App state with which Excalidraw should be mounted. |
scrollToContent |
boolean | This attribute implies whether to scroll to the nearest element to center once Excalidraw is mounted. By default, it will not scroll the nearest element to the center. Make sure you pass initialData.appState.scrollX and initialData.appState.scrollY when scrollToContent is false so that scroll positions are retained |
libraryItems |
LibraryItems | Promise<LibraryItems> | This library items with which Excalidraw should be mounted. |
files |
BinaryFiles | The files added to the scene. |
{
"elements": [
{
"type": "rectangle",
"version": 141,
"versionNonce": 361174001,
"isDeleted": false,
"id": "oDVXy8D6rom3H1-LLH2-f",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 100.50390625,
"y": 93.67578125,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 186.47265625,
"height": 141.9765625,
"seed": 1968410350,
"groupIds": []
}
],
"appState": { "zenModeEnabled": true, "viewBackgroundColor": "#AFEEEE" }
}
You might want to use this when you want to load excalidraw with some initial elements and app state.
Storing custom data on Excalidraw elements
Beyond attributes that Excalidraw elements already support, you can store custom data on each element in a customData
object. The type of the attribute is Record<string, any>
and is optional.
You can use this to add any extra information you need to keep track of.
You can add customData
to elements when passing them as initialData
, or using updateScene
/updateLibrary
afterwards.
ref
You can pass a ref
when you want to access some excalidraw APIs. We expose the below APIs:
API | Signature | Usage |
---|---|---|
ready | boolean |
This is set to true once Excalidraw is rendered |
readyPromise | resolvablePromise | This promise will be resolved with the api once excalidraw has rendered. This will be helpful when you want do some action on the host app once this promise resolves. For this to work you will have to pass ref as shown here |
updateScene | (scene: sceneData) => void |
updates the scene with the sceneData |
updateLibrary | (opts) => Promise<LibraryItems> |
updates the scene with the sceneData |
addFiles | (files: BinaryFileData) => void |
add files data to the appState |
resetScene | ({ resetLoadingState: boolean }) => void |
Resets the scene. If resetLoadingState is passed as true then it will also force set the loading state to false. |
getSceneElementsIncludingDeleted | () => ExcalidrawElement[] |
Returns all the elements including the deleted in the scene |
getSceneElements | () => ExcalidrawElement[] |
Returns all the elements excluding the deleted in the scene |
getAppState | () => AppState |
Returns current appState |
history | { clear: () => void } |
This is the history API. history.clear() will clear the history |
scrollToContent | (target?: ExcalidrawElement | ExcalidrawElement[]) => void |
Scroll the nearest element out of the elements supplied to the center. Defaults to the elements on the scene. |
refresh | () => void |
Updates the offsets for the Excalidraw component so that the coordinates are computed correctly (for example the cursor position). You don't have to call this when the position is changed on page scroll or when the excalidraw container resizes (we handle that ourselves). For any other cases if the position of excalidraw is updated (example due to scroll on parent container and not page scroll) you should call this API. |
importLibrary | (url: string, token?: string) => void |
Imports library from given URL |
setToast | ({ message: string, closable?:boolean, duration?:number } | null) => void |
This API can be used to show the toast with custom message. |
id | string | Unique ID for the excalidraw component. |
getFiles | () => files |
This API can be used to get the files present in the scene. It may contain files that aren't referenced by any element, so if you're persisting the files to a storage, you should compare them against stored elements. |
setActiveTool | (tool: { type: typeof SHAPES [number]["value"]| "eraser" } | { type: "custom"; customType: string }) => void |
This API can be used to set the active tool |
setCursor | (cursor: string) => void |
This API can be used to set customise the mouse cursor on the canvas |
resetCursor | () => void |
This API can be used to reset to default mouse cursor on the canvas |
readyPromise
const excalidrawRef = { current: { readyPromise: resolvablePromise}}
Since plain object is passed as a ref
, the readyPromise
is resolved as soon as the component is mounted. Most of the time you will not need this unless you have a specific use case where you can't pass the ref
in the react way and want to do some action on the host when this promise resolves. You can check the example for the usage.
updateScene
(scene: sceneData) => void
You can use this function to update the scene with the sceneData. It accepts the below attributes.
Name | Type | Description |
---|---|---|
elements |
ImportedDataState["elements"] |
The elements to be updated in the scene |
appState |
ImportedDataState["appState"] |
The appState to be updated in the scene. |
collaborators |
Map<string, Collaborator> |
The list of collaborators to be updated in the scene. |
commitToHistory |
boolean |
Implies if the history (undo/redo) should be recorded. Defaults to false . |
libraryItems |
LibraryItems | Promise<LibraryItems> | ((currentItems: LibraryItems>) => LibraryItems | Promise<LibraryItems>) | The libraryItems to be update in the scene. |
updateLibrary
(opts: { libraryItems: LibraryItemsSource; merge?: boolean; prompt?: boolean; openLibraryMenu?: boolean; defaultStatus?: "unpublished" | "published"; }) => Promise<LibraryItems>
You can use this function to update the library. It accepts the below attributes.
Name | Type | Default | Description |
---|---|---|---|
libraryItems |
LibraryItems | The libraryItems to be replaced/merged with current library |
|
merge |
boolean | false |
Whether to merge with existing library items. |
prompt |
boolean | false |
Whether to prompt user for confirmation. |
openLibraryMenu |
boolean | false |
Whether to open the library menu before importing. |
defaultStatus |
"unpublished" | "published" |
"unpublished" |
Default library item's status if not present. |
addFiles
(files: BinaryFileData) => void
Adds supplied files data to the appState.files
cache on top of existing files present in the cache.
onCollabButtonClick
This callback is triggered when clicked on the collab button in excalidraw. If not supplied, the collab dialog button is not rendered.
isCollaborating
This prop indicates if the app is in collaboration mode.
onPointerUpdate
This callback is triggered when mouse pointer is updated.
({ x, y }, button, pointersMap}) => void;
1.{x, y}
: Pointer coordinates
2.button
: The position of the button. This will be one of ["down", "up"]
3.pointersMap
: pointers map
of the scene
(exportedElements, appState, canvas) => void
-
exportedElements
: An array of non deleted elements which needs to be exported. -
appState
: AppState of the scene. -
canvas
: TheHTMLCanvasElement
of the scene.
langCode
Determines the language of the UI. It should be one of the available language codes. Defaults to en
(English). We also export default language and supported languages which you can import as shown below.
import { defaultLang, languages } from "@excalidraw/excalidraw";
name | type |
---|---|
defaultLang | string |
languages | Language[] |
renderTopRightUI
(isMobile: boolean, appState: AppState) => JSX | null
A function returning JSX to render custom UI in the top right corner of the app.
renderFooter
(isMobile: boolean, appState: AppState) => JSX | null
A function returning JSX to render custom UI footer. For example, you can use this to render a language picker that was previously being rendered by Excalidraw itself (for now, you'll need to implement your own language picker).
renderCustomStats
A function that can be used to render custom stats (returns JSX) in the nerd stats dialog. For example you can use this prop to render the size of the elements in the storage.
viewModeEnabled
This prop indicates whether the app is in view mode
. When supplied, the value takes precedence over intialData.appState.viewModeEnabled
, the view mode
will be fully controlled by the host app, and users won't be able to toggle it from within the app.
zenModeEnabled
This prop indicates whether the app is in zen mode
. When supplied, the value takes precedence over intialData.appState.zenModeEnabled
, the zen mode
will be fully controlled by the host app, and users won't be able to toggle it from within the app.
gridModeEnabled
This prop indicates whether the shows the grid. When supplied, the value takes precedence over intialData.appState.gridModeEnabled
, the grid will be fully controlled by the host app, and users won't be able to toggle it from within the app.
libraryReturnUrl
If supplied, this URL will be used when user tries to install a library from libraries.excalidraw.com. Defaults to window.location.origin + window.location.pathname
. To install the libraries in the same tab from which it was opened, you need to set window.name
(to any alphanumeric string) — if it's not set it will open in a new tab.
theme
This prop controls Excalidraw's theme. When supplied, the value takes precedence over intialData.appState.theme
, the theme will be fully controlled by the host app, and users won't be able to toggle it from within the app unless UIOptions.canvasActions.toggleTheme
is set to true
, in which case the theme
prop will control Excalidraw's default theme with ability to allow theme switching (you must take care of updating the theme
prop when you detect a change to appState.theme
from the onChange callback).
You can use THEME
to specify the theme.
name
This prop sets the name of the drawing which will be used when exporting the drawing. When supplied, the value takes precedence over intialData.appState.name
, the name
will be fully controlled by host app and the users won't be able to edit from within Excalidraw.
UIOptions
This prop can be used to customise UI of Excalidraw. Currently we support customising canvasActions
and dockedSidebarBreakpoint
. It accepts the below parameters
{ canvasActions: CanvasActions }
canvasActions
Attribute | Type | Default | Description |
---|---|---|---|
changeViewBackgroundColor |
boolean | true | Implies whether to show Background color picker
|
clearCanvas |
boolean | true | Implies whether to show Clear canvas button
|
export |
false | exportOpts | { saveFileToDisk: true } |
This prop allows to customize the UI inside the export dialog. By default it shows the "saveFileToDisk". If this prop is false the export button will not be rendered. For more details visit exportOpts . |
loadScene |
boolean | true | Implies whether to show Load button
|
saveToActiveFile |
boolean | true | Implies whether to show Save button to save to current file |
toggleTheme |
boolean | null | null | Implies whether to show Theme toggle . When defined as boolean , takes precedence over props.theme to show Theme toggle
|
saveAsImage |
boolean | true | Implies whether to show Save as image button
|
dockedSidebarBreakpoint
This prop indicates at what point should we break to a docked, permanent sidebar. If not passed it defaults to MQ_RIGHT_SIDEBAR_MAX_WIDTH_PORTRAIT
. If the width
of the excalidraw
container exceeds dockedSidebarBreakpoint
, the sidebar will be dockable. If user choses to dock the sidebar, it will push the right part of the UI towards the left, making space for the sidebar as shown below.
exportOpts
The below attributes can be set in UIOptions.canvasActions.export
to customize the export dialog. If UIOptions.canvasActions.export
is false
the export button will not be rendered.
Attribute | Type | Default | Description |
---|---|---|---|
saveFileToDisk |
boolean | true | Implies if save file to disk button should be shown |
onExportToBackend |
(exportedElements: readonly NonDeletedExcalidrawElement[],appState: AppState,canvas: HTMLCanvasElement | null) => void |
This callback is triggered when the shareable-link button is clicked in the export dialog. The link button will only be shown if this callback is passed. | |
renderCustomUI |
(exportedElements: readonly NonDeletedExcalidrawElement[],appState: AppState,canvas: HTMLCanvasElement | null) => void |
This callback should be supplied if you want to render custom UI in the export dialog. |
onPaste
This callback is triggered if passed when something is pasted into the scene. You can use this callback in case you want to do something additional when the paste event occurs.
(data: ClipboardData, event: ClipboardEvent | null) => boolean
This callback must return a boolean
value or a promise which resolves to a boolean value.
In case you want to prevent the excalidraw paste action you must return false
, it will stop the native excalidraw clipboard management flow (nothing will be pasted into the scene).
importLibrary
Imports library from given URL. You should call this on hashchange
, passing the addLibrary
value if you detect it as shown below. Optionally pass a CSRF token
to skip prompting during installation (retrievable via token
key from the url coming from https://libraries.excalidraw.com).
useEffect(() => {
const onHashChange = () => {
const hash = new URLSearchParams(window.location.hash.slice(1));
const libraryUrl = hash.get("addLibrary");
if (libraryUrl) {
excalidrawRef.current.importLibrary(libraryUrl, hash.get("token"));
}
};
window.addEventListener("hashchange", onHashChange, false);
return () => {
window.removeEventListener("hashchange", onHashChange);
};
}, []);
Try out the Demo to see it in action.
setToast
This API can be used to show the toast with custom message.
({ message: string, closable?:boolean, duration?:number } | null) => void
Attribute | type | Description |
---|---|---|
message | string | The message to be shown on the toast. |
closable | boolean | Indicates whether to show the closable button on toast to dismiss the toast. |
duration | number | Determines the duration after which the toast should auto dismiss. To prevent autodimiss you can pass Infinity . |
To dismiss an existing toast you can simple pass null
setToast(null);
setActiveTool
This API has the below signature. It sets the tool
passed in param as the active tool.
(tool: { type: typeof SHAPES[number]["value"] | "eraser" } | { type: "custom"; customType: string }) => void
setCursor
This API can be used to customise the mouse cursor on the canvas and has the below signature. It sets the mouse cursor to the cursor passed in param.
(cursor: string) => void
resetCursor
This API can be used to reset to default mouse cursor.
detectScroll
Indicates whether Excalidraw should listen for scroll
event on the nearest scrollable container in the DOM tree and recompute the coordinates (e.g. to correctly handle the cursor) when the component's position changes. You can disable this when you either know this doesn't affect your app or you want to take care of it yourself (calling the refresh()
method).
handleKeyboardGlobally
Indicates whether to bind keyboard events to document
. Disabled by default, meaning the keyboard events are bound to the Excalidraw component. This allows for multiple Excalidraw components to live on the same page, and ensures that Excalidraw keyboard handling doesn't collide with your app's (or the browser) when the component isn't focused.
Enable this if you want Excalidraw to handle keyboard even if the component isn't focused (e.g. a user is interacting with the navbar, sidebar, or similar).
onLibraryChange
This callback if supplied will get triggered when the library is updated and has the below signature.
(items: LibraryItems) => void | Promise
It is invoked with empty items when user clears the library. You can use this callback when you want to do something additional when library is updated for example persisting it to local storage.
id
The unique id of the excalidraw component. This can be used to identify the excalidraw component, for example importing the library items to the excalidraw component from where it was initiated when you have multiple excalidraw components rendered on the same page as shown in multiple excalidraw demo.
autoFocus
This prop implies whether to focus the Excalidraw component on page load. Defaults to false.
generateIdForFile
Allows you to override id
generation for files added on canvas (images). By default, an SHA-1 digest of the file is used.
(file: File) => string | Promise<string>
onLinkOpen
This prop if passed will be triggered when clicked on link. To handle the redirect yourself (such as when using your own router for internal links), you must call event.preventDefault()
.
(element: ExcalidrawElement, event: CustomEvent<{ nativeEvent: MouseEvent }>) => void
Example:
const history = useHistory();
// open internal links using the app's router, but opens external links in
// a new tab/window
const onLinkOpen: ExcalidrawProps["onLinkOpen"] = useCallback(
(element, event) => {
const link = element.link;
const { nativeEvent } = event.detail;
const isNewTab = nativeEvent.ctrlKey || nativeEvent.metaKey;
const isNewWindow = nativeEvent.shiftKey;
const isInternalLink =
link.startsWith("/") || link.includes(window.location.origin);
if (isInternalLink && !isNewTab && !isNewWindow) {
history.push(link.replace(window.location.origin, ""));
// signal that we're handling the redirect ourselves
event.preventDefault();
}
},
[history],
);
onPointerDown
This prop if passed will be triggered on pointer down events and has the below signature.
(activeTool: AppState["activeTool"], pointerDownState: PointerDownState) => void
onScrollChange
This prop if passed will be triggered when canvas is scrolled and has the below signature.
(scrollX: number, scrollY: number) => void
Does it support collaboration ?
No, Excalidraw package doesn't come with collaboration built in, since the implementation is specific to each host app. We expose APIs which you can use to communicate with Excalidraw which you can use to implement it. You can check our own implementation here.
Restore utilities
restoreAppState
Signature
restoreAppState(appState: ImportedDataState["appState"], localAppState: Partial<AppState> | null): AppState
How to use
import { restoreAppState } from "@excalidraw/excalidraw";
This function will make sure all the keys have appropriate values in appState and if any key is missing, it will be set to default value.
When localAppState
is supplied, it's used in place of values that are missing (undefined
) in appState
instead of defaults. Use this as a way to not override user's defaults if you persist them. Required: supply null
/undefined
if not applicable.
restoreElements
Signature
restoreElements(elements: ImportedDataState["elements"], localElements: ExcalidrawElement[] | null | undefined): ExcalidrawElement[]
How to use
import { restoreElements } from "@excalidraw/excalidraw";
This function will make sure all properties of element is correctly set and if any attribute is missing, it will be set to default value.
When localElements
are supplied, they are used to ensure that existing restored elements reuse version
(and increment it), and regenerate versionNonce
. Use this when you import elements which may already be present in the scene to ensure that you do not disregard the newly imported elements if you're using element version to detect the updates.
restore
Signature
restoreElements(data: ImportedDataState, localAppState: Partial<AppState> | null | undefined, localElements: ExcalidrawElement[] | null | undefined): DataState
See restoreAppState()
about localAppState
, and restoreElements()
about localElements
.
How to use
import { restore } from "@excalidraw/excalidraw";
This function makes sure elements and state is set to appropriate values and set to default value if not present. It is a combination of restoreElements and restoreAppState.
restoreLibraryItems
Signature
restoreLibraryItems(libraryItems: ImportedDataState["libraryItems"], defaultStatus: "published" | "unpublished")
How to use
import { restoreLibraryItems } from "@excalidraw/excalidraw";
restoreLibraryItems(libraryItems, "unpublished");
This function normalizes library items elements, adding missing values when needed.
Export utilities
exportToCanvas
Signature
exportToCanvas({ elements, appState getDimensions, files, exportPadding?: number; }: ExportOpts
Name | Type | Default | Description |
---|---|---|---|
elements | Excalidraw Element [] | The elements to be exported to canvas | |
appState | AppState | defaultAppState | The app state of the scene |
getDimensions | (width: number, height: number) => { width: number, height: number, scale?: number } |
undefined | A function which returns the width , height , and optionally scale (defaults 1 ), with which canvas is to be exported. |
maxWidthOrHeight | number |
undefined | The maximum width or height of the exported image. If provided, getDimensions is ignored. |
files | [BinaryFiles](The BinaryFiles
|
undefined | The files added to the scene. |
exportPadding | number | 10 | The padding to be added on canvas |
How to use
import { exportToCanvas } from "@excalidraw/excalidraw";
This function returns the canvas with the exported elements, appState and dimensions.
exportToBlob
Signature
exportToBlob( opts: ExportOpts & { mimeType?: string, quality?: number, exportPadding?: number; })
Name | Type | Default | Description |
---|---|---|---|
opts | This param is passed to exportToCanvas . You can refer to exportToCanvas
|
||
mimeType | string | "image/png" | Indicates the image format |
quality | number | 0.92 | A value between 0 and 1 indicating the image quality. Applies only to image/jpeg /image/webp MIME types. |
exportPadding | number | 10 | The padding to be added on canvas |
How to use
import { exportToBlob } from "@excalidraw/excalidraw";
Returns a promise which resolves with a blob. It internally uses canvas.ToBlob.
exportToSvg
Signature
exportToSvg({ elements: ExcalidrawElement[], appState: AppState, exportPadding?: number, metadata?: string, files?: BinaryFiles })
Name | Type | Default | Description |
---|---|---|---|
elements | Excalidraw Element [] | The elements to exported as svg | |
appState | AppState | defaultAppState | The app state of the scene |
exportPadding | number | 10 | The padding to be added on canvas |
files | [BinaryFiles](The BinaryFiles
|
undefined | The files added to the scene. |
This function returns a promise which resolves to svg of the exported drawing.
exportToClipboard
Signature
exportToClipboard( opts: ExportOpts & { mimeType?: string, quality?: number; type: 'png' | 'svg' |'json' })
| Name | Type | Default | Description |
| --- | --- | --- | --- | --- | --- |
| opts | | | This param is same as the params passed to exportToCanvas
. You can refer to exportToCanvas
. |
| mimeType | string | "image/png" | Indicates the image format, this will be used when exporting as png
. |
| quality | number | 0.92 | A value between 0 and 1 indicating the image quality. Applies only to image/jpeg
/image/webp
MIME types. This will be used when exporting as png
. |
| type | 'png' | 'svg' | 'json' | | This determines the format to which the scene data should be exported. |
How to use
import { exportToClipboard } from "@excalidraw/excalidraw";
Copies the scene data in the specified format (determined by type
) to clipboard.
export\*
APIs
Additional attributes of appState for Name | Type | Default | Description |
---|---|---|---|
exportBackground | boolean | true | Indicates whether background should be exported |
viewBackgroundColor | string | #fff | The default background color |
exportWithDarkMode | boolean | false | Indicates whether to export with dark mode |
exportEmbedScene | boolean | false | Indicates whether scene data should be embedded in svg/png. This will increase the image size. |
Extra API's
serializeAsJSON
Signature
serializeAsJSON({ elements: ExcalidrawElement[], appState: AppState, }): string
Takes the scene elements and state and returns a JSON string. Deleted elements
as well as most properties from AppState
are removed from the resulting JSON. (see serializeAsJSON()
source for details).
If you want to overwrite the source field in the JSON string, you can set window.EXCALIDRAW_EXPORT_SOURCE
to the desired value.
serializeLibraryAsJSON
Signature
serializeLibraryAsJSON({ libraryItems: LibraryItems[],
Takes the library items and returns a JSON string.
If you want to overwrite the source field in the JSON string, you can set window.EXCALIDRAW_EXPORT_SOURCE
to the desired value.
getSceneVersion
How to use
import { getSceneVersion } from "@excalidraw/excalidraw"; getSceneVersion(elements: ExcalidrawElement[])
This function returns the current scene version.
isInvisiblySmallElement
Signature
isInvisiblySmallElement(element: ExcalidrawElement): boolean
How to use
import { isInvisiblySmallElement } from "@excalidraw/excalidraw";
Returns true
if element is invisibly small (e.g. width & height are zero).
loadLibraryFromBlob
import { loadLibraryFromBlob } from "@excalidraw/excalidraw";
Signature
loadLibraryFromBlob(blob: Blob, defaultStatus: "published" | "unpublished")
This function loads the library from the blob. Additonally takes defaultStatus
param which sets the default status for library item if not present, defaults to unpublished
.
loadFromBlob
How to use
import { loadFromBlob } from "@excalidraw/excalidraw";
const scene = await loadFromBlob(file, null, null);
excalidrawAPI.updateScene(scene);
Signature
loadFromBlob( blob: Blob, localAppState: AppState | null, localElements: ExcalidrawElement[] | null, fileHandle?: FileSystemHandle | null ) => Promise<RestoredDataState>
This function loads the scene data from the blob (or file). If you pass localAppState
, localAppState
value will be preferred over the appState
derived from blob
. Throws if blob doesn't contain valid scene data.
loadSceneOrLibraryFromBlob
How to use
import { loadSceneOrLibraryFromBlob, MIME_TYPES } from "@excalidraw/excalidraw";
const contents = await loadSceneOrLibraryFromBlob(file, null, null);
if (contents.type === MIME_TYPES.excalidraw) {
excalidrawAPI.updateScene(contents.data);
} else if (contents.type === MIME_TYPES.excalidrawlib) {
excalidrawAPI.updateLibrary(contents.data);
}
Signature
loadSceneOrLibraryFromBlob( blob: Blob, localAppState: AppState | null, localElements: ExcalidrawElement[] | null, fileHandle?: FileSystemHandle | null ) => Promise<{ type: string, data: RestoredDataState | ImportedLibraryState}>
This function loads either scene or library data from the supplied blob. If the blob contains scene data, and you pass localAppState
, localAppState
value will be preferred over the appState
derived from blob
. Throws if blob doesn't contain neither valid scene data or library data.
getFreeDrawSvgPath
How to use
import { getFreeDrawSvgPath } from "@excalidraw/excalidraw";
Signature
getFreeDrawSvgPath(element: ExcalidrawFreeDrawElement
This function returns the free draw svg path for the element.
isLinearElement
How to use
import { isLinearElement } from "@excalidraw/excalidraw";
Signature
isLinearElement(elementType?: ExcalidrawElement): boolean
This function returns true if the element is linear type (arrow
|line
) else returns false.
getNonDeletedElements
How to use
import { getNonDeletedElements } from "@excalidraw/excalidraw";
Signature
getNonDeletedElements(elements: readonly ExcalidrawElement[]): as readonly NonDeletedExcalidrawElement[]
This function returns an array of deleted elements.
mergeLibraryItems
import { mergeLibraryItems } from "@excalidraw/excalidraw";
Signature
mergeLibraryItems(localItems: LibraryItems, otherItems: LibraryItems) => LibraryItems
This function merges two LibraryItems
arrays, where unique items from otherItems
are sorted first in the returned array.
parseLibraryTokensFromUrl
How to use
import { parseLibraryTokensFromUrl } from "@excalidraw/excalidraw";
Signature
parseLibraryTokensFromUrl(): { libraryUrl: string; idToken: string | null; } | null
Parses library parameters from URL if present (expects the #addLibrary
hash key), and returns an object with the libraryUrl
and idToken
. Returns null
if #addLibrary
hash key not found.
useHandleLibrary
How to use
import { useHandleLibrary } from "@excalidraw/excalidraw";
export const App = () => {
// ...
useHandleLibrary({ excalidrawAPI });
};
Signature
useHandleLibrary(opts: { excalidrawAPI: ExcalidrawAPI, getInitialLibraryItems?: () => LibraryItemsSource });
A hook that automatically imports library from url if #addLibrary
hash key exists on initial load, or when it changes during the editing session (e.g. when a user installs a new library), and handles initial library load if getInitialLibraryItems
getter is supplied.
In the future, we will be adding support for handling library persistence to browser storage (or elsewhere).
sceneCoordsToViewportCoords
import { sceneCoordsToViewportCoords } from "@excalidraw/excalidraw";
Signature
sceneCoordsToViewportCoords({sceneX: number, sceneY: number}, appState: AppState): {x: number, y: number}
This function returns equivalent viewport coords for the provided scene coords in params.
viewportCoordsToSceneCoords
import { viewportCoordsToSceneCoords } from "@excalidraw/excalidraw";
Signature
viewportCoordsToSceneCoords({clientX: number, clientY: number}, appState: AppState): {x: number, y: number}
This function returns equivalent scene coords for the provided viewport coords in params.
Exported constants
FONT_FAMILY
How to use
import { FONT_FAMILY } from "@excalidraw/excalidraw";
FONT_FAMILY
contains all the font families used in Excalidraw
as explained below
Font Family | Description |
---|---|
Virgil | The handwritten font |
Helvetica | The Normal Font |
Cascadia | The Code Font |
Defaults to FONT_FAMILY.Virgil
unless passed in initialData.appState.currentItemFontFamily
.
THEME
How to use
import { THEME } from "@excalidraw/excalidraw";
THEME
contains all the themes supported by Excalidraw
as explained below
Theme | Description |
---|---|
LIGHT | The light theme |
DARK | The Dark theme |
Defaults to THEME.LIGHT
unless passed in initialData.appState.theme
MIME_TYPES
**How to use **
import { MIME_TYPES } from "@excalidraw/excalidraw";
MIME_TYPES
contains all the mime types supported by Excalidraw
.
Need help?
Check out the existing Q&A. If you have any queries or need help, ask us here.
Development
Install the dependencies
yarn
Start the server
yarn start
http://localhost:3001 will open in your default browser.
The example is same as the codesandbox example
Create a test release
You can create a test release by posting the below comment in your pull request
@excalibot trigger release
Once the version is released @excalibot
will post a comment with the release version.
Creating a production release
To release the next stable version follow the below steps
yarn prerelease version
You need to pass the version
for which you want to create the release. This will make the changes needed before making the release like updating package.json
, changelog
and more.
The next step is to run the release
script
yarn release
This will publish the package.
Right now there are two steps to create a production release but once this works fine these scripts will be combined and more automation will be done.