Excalidraw, hand drawn sketches and an MIT library
Excalidraw is a whiteboard for drawing diagrams whose defining feature is a hand drawn stroke style, and a React library letting you embed that same editor in your own application. The current package version is 0.18, under the MIT licence.
Two things carrying the same name deserve separating, since conflating them leads to wrong conclusions. The open library and the free whiteboard at a public address are one. The paid service with a backend, accounts, and team management is another.
The hand drawn style is not decoration
The stroke appearance looks like an aesthetic decision and is a functional one, worth understanding before choosing a tool.
A diagram drawn with precise lines looks finished. The same diagram in a hand drawn style looks like a sketch. The difference concerns how people react to it: with a sketch it is easier to say "or maybe differently", while a polished drawing invites reluctance to question something somebody evidently refined.
When designing architecture, discussing a flow, and thinking together, that property works in your favour. For a board presentation or documentation meant to look serious it works against you, and then a tool of a different character is the right pick.
That is in fact the main criterion when choosing between this tool and its alternatives: not the feature count but the stage of the conversation at which the diagram appears.
Embedding in your own application
The library is a React component and embeds in a few lines.
import { Excalidraw } from '@excalidraw/excalidraw'
import '@excalidraw/excalidraw/index.css'
export function Board() {
return (
<div style={{ height: '100vh' }}>
<Excalidraw
initialData={{ elements: savedElements }}
onChange={(elements) => save(elements)}
/>
</div>
)
}Three things cause the most trouble on a first attempt.
The stylesheet has to be imported separately. Without it the component renders and looks scattered, easily mistaken for a build configuration fault.
The container needs a defined height. The canvas stretches to its parent, so inside an auto height element it gets zero pixels and vanishes.
In Next.js a third thing appears: the component touches browser objects on mount, so server rendering has to be disabled by loading it dynamically.
import dynamic from 'next/dynamic'
const Excalidraw = dynamic(
() => import('@excalidraw/excalidraw').then((m) => m.Excalidraw),
{ ssr: false, loading: () => <Skeleton /> }
)Without that you get an error about an undefined window object, thrown during the build rather than in the browser, which makes it easy to hunt for the cause in the wrong place.
The drawing's state is an ordinary array of objects you store wherever you like. There is no backend imposed by the library, so persistence, versioning, and permissions are yours to design, exactly as with any other data in an application.
Collaboration and self hosting
This is where care pays, since material about it misleads.
Running the editor alone as a container gives a working whiteboard with no collaboration. You draw, you save locally, that is all.
Collaboration between people requires a separate component, a room server relaying changes between clients. That is a second container and a second thing to maintain, which guides describing a one command launch usually skip.
The practical consequence: if you only want a drawing tool, self hosting is simple. If you want shared work across a company, expect two services plus domain name and certificate configuration.
Know too that drawing data in the public version is encrypted on the browser side, so the relaying server does not see the contents. That is a sensible property while not replacing access control: whoever holds the link holds access, and the link carries the key.
The paid tier and what it gives
The paid service costs 7 dollars per person per month on monthly billing and 6 dollars on annual billing, after a two week trial. That is the only item with a published price: an offering for large companies exists, but the vendor publishes no amounts for it and points you to a sales conversation.
It provides things the library by nature lacks: accounts, cloud storage for drawings, folders, voice conversations, screen sharing, and model assisted features.
Decide whether you need that, since the answer is negative more often than the pricing page suggests. A team that draws diagrams during a call and pastes the results into documentation manages with the free version. A team treating boards as durable artefacts with history and permissions needs a backend, so the choice lies between this service and building that yourself on the library.
The second route is sometimes sensible precisely because the licence is fully open. Building a backend with a drawing list, permissions, and search is a few days of work, and where data must stay in your own infrastructure it is sometimes the only option.
What it genuinely suits
Worth naming concrete uses, since "a drawing whiteboard" says nothing about when to reach for it.
An architecture diagram during a call. Three rectangles, two arrows, and labels appear faster than opening a tool with a palette of ready shapes, and the conversation continues rather than waiting.
An interface sketch before building it. The hand drawn style works twice over here: it draws fast, and nobody imagines this is already a design awaiting approval.
An explanation inside a bug report. A screenshot with arrows and labels drawn on says more than a paragraph of description, and pasting the image into a report takes seconds.
Teaching material. A diagram explaining a data flow or a directory structure looks approachable precisely because it does not look like an official document.
What it does not suit: schematics requiring precision, technical drawings with scale, diagrams generated from code, and anything meant to be produced automatically. For diagrams described in text and rendered at documentation build time, the right tool is a generator rather than a graphical editor, since that one versions like code.
Shape libraries and extending it
Beyond the basic shapes, sets of ready elements exist that deserve knowing before you start drawing icons by hand.
Libraries are collections of reusable elements: cloud icons, network symbols, interface pieces. You add them to the editor and drag them onto the canvas, and your own library can be built from drawn elements and shared with a team as a file.
That last scenario gets undervalued. A team drawing architecture diagrams of the same systems every week stops drawing from scratch after building its own shape set and starts assembling, and the diagrams become consistent across people along the way.
Extending the editor itself is thinner here than in tools built around extensibility. You can replace interface elements, add your own buttons, and drive the editor programmatically, while adding an entirely new shape type with its own logic is not what this library was designed for.
That is another thing separating it from richer solutions and equally the reason it wires in faster. Fewer capabilities mean fewer decisions to make while embedding.
Excalidraw against the alternatives
| Tool | Licence | Style | Pick it when |
|---|---|---|---|
| Excalidraw | MIT, no key | Hand drawn | Sketches, embedding in a product, zero budget |
| tldraw | Own licence, key required | Clean | The canvas is the heart of the product and you have budget |
| Diagramming tools | Closed, paid | Precise | Documentation that must look official |
| Hosted whiteboards | Closed, per person | Polished | You are buying a product rather than building |
The first two rows are today's key comparison, and the difference shifted clearly. tldraw introduced a licence key requirement for production use, so embedding it in a commercial product carries a fee. This library stayed under MIT, with no key and no watermark.
That does not make it better. tldraw offers a richer system of custom shapes and tools, so for a product where the canvas is the main interface that difference is sometimes worth its price. For a whiteboard added beside an application's main function, justifying the bill is hard when a free alternative does the same job.
The third row deserves honest consideration. A diagram headed for a client proposal or compliance documentation looks better drawn precisely, and that is exactly the case where a hand drawn style gets in the way.
Driving the editor from code
Embedding the component is the start, and most real uses require talking to it programmatically.
The component exposes a control object through which you read the current elements, replace the canvas contents, scroll to a chosen place, and change the working mode. That suffices for building things a user perceives as application features rather than whiteboard capabilities.
Typical examples: a button inserting a diagram generated from data onto the canvas, a toggle between edit and preview mode, saving the drawing to your own backend on every change with a delay, loading a template when a new document is created.
The same principle applies to saving as in any editor reacting to changes: the listener fires very often, including on every frame of dragging an element, so saving with no delay means dozens of writes per second.
const [api, setApi] = useState<ExcalidrawImperativeAPI | null>(null)
const saveDebounced = useMemo(
() => debounce((elements: readonly ExcalidrawElement[]) => {
fetch('/api/board', {
method: 'PUT',
body: JSON.stringify({ elements })
})
}, 1000),
[]
)
<Excalidraw
excalidrawAPI={setApi}
onChange={(elements) => saveDebounced(elements)}
/>A delay of about a second past the last change settles it once. The imperative object arrives through a prop handing it to a callback rather than through a ref, and that is what most people trip over on a first attempt.
Know too that the drawing's state is an ordinary data structure, so it can be created and modified without the editor's involvement.
import { convertToExcalidrawElements } from '@excalidraw/excalidraw'
const elements = convertToExcalidrawElements([
{ type: 'rectangle', x: 0, y: 0, width: 180, height: 80, id: 'api' },
{ type: 'rectangle', x: 300, y: 0, width: 180, height: 80, id: 'db' },
{
type: 'arrow',
x: 190, y: 40,
width: 100, height: 0,
start: { id: 'api' },
end: { id: 'db' }
}
])
api?.updateScene({ elements })The helper fills in the fields you did not supply with defaults, so you need not know an element's full shape. Generating a diagram from a description in code and supplying it as initial content is a route that, for automated documentation, often beats drawing by hand.
Export and fitting it into documentation
A drawing exports to a raster image, to a vector format, and to a native format preserving further editability.
The choice between them matters more practically than it looks. A raster image is convenient and uneditable, so a correction a month later means drawing again. The vector format scales without losing sharpness and suits documentation, while also not returning to the editor.
The right practice keeps both: the source file in the repository beside the documentation and the exported image inserted into the text. A correction six months later is then opening a file rather than reconstructing a drawing from a picture.
Know too that the vector format can embed the source data, so one file serves both roles. That route is convenient for documentation kept in a repository.
For embedding a preview in an application, the interface free mode helps, where the component renders the drawing with no toolbar. A diagram in technical documentation then looks like an image while retaining sharpness when zoomed.
Common mistakes
The first is skipping the stylesheet import. The component renders scattered, and the cause looks like a build tool problem.
The second is a container with no defined height. The canvas gets zero pixels and looks as though the component never mounted.
The third is server rendering without disabling it. The component breaks on mount, reaching for a browser object.
The fourth is expecting collaboration after running the editor alone. Exchanging changes between people requires a separate room server, which is a second component.
The fifth is treating link encryption as access control. Whoever holds the link holds access, since the key is part of it.
The sixth is saving only an image export. A correction a month later then means drawing the diagram again rather than opening the source file.
The seventh is saving on every change with no delay. The listener fires on every frame of dragging an element, so on a larger drawing that produces dozens of writes per second and noticeable stutter.
FAQ
Is Excalidraw free?
The library and the whiteboard at the public address are, under the MIT licence, with no key and no watermark. What costs money is a separate service with accounts, cloud storage, and team features, at 7 dollars per person per month or 6 dollars on annual billing.
Can I embed it in a commercial product?
Yes, the MIT licence sets no conditions here and requires no key for production use. That is currently the main difference from tldraw, which since its fourth version requires a licence for production use.
How do I run it myself?
The editor alone is one container and gives a working whiteboard with no collaboration. Exchanging changes between people additionally requires a room server, a second component, which guides describing a one command launch usually skip.
Is data safe in the public version?
Drawing content is encrypted on the browser side, so the relaying server does not see it. That does not replace access control, since the key is part of the link, so anybody receiving it will open the drawing.
When is something else the better pick?
When the diagram must look official, since a hand drawn style hinders documentation for a client. And when the canvas is your product's main interface and you need an extensive custom shape system, since tldraw offers more there.
The library documentation sits on the developer site, and the code in the GitHub repository.