Overview
Excalidraw supports light and dark themes out of the box. You can control the theme programmatically or let users toggle between themes.
Theme Basics
Available Themes
Excalidraw provides two built-in themes:
light - Light color scheme optimized for bright environments
dark - Dark color scheme optimized for low-light environments
import { Excalidraw, THEME } from "@excalidraw/excalidraw";
// THEME constants from @excalidraw/common
console.log(THEME.LIGHT); // "light"
console.log(THEME.DARK); // "dark"
Setting the Theme
Import Excalidraw
import { Excalidraw } from "@excalidraw/excalidraw";
Pass the theme prop
function App() {
return <Excalidraw theme="dark" />;
}
Optional: Enable theme toggle
Omit the theme prop to show the theme toggle button:function App() {
return <Excalidraw />;
}
Dynamic Theme Switching
User-Controlled Theme
Let users switch between themes:
import { Excalidraw } from "@excalidraw/excalidraw";
import { useState } from "react";
function App() {
const [theme, setTheme] = useState("light");
return (
<>
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<Excalidraw theme={theme} />
</>
);
}
System Theme Detection
Sync with the user’s system theme preference:
import { Excalidraw } from "@excalidraw/excalidraw";
import { useState, useEffect } from "react";
function App() {
const [theme, setTheme] = useState("light");
useEffect(() => {
// Detect system theme preference
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
setTheme(mediaQuery.matches ? "dark" : "light");
// Listen for theme changes
const handler = (e) => setTheme(e.matches ? "dark" : "light");
mediaQuery.addEventListener("change", handler);
return () => mediaQuery.removeEventListener("change", handler);
}, []);
return <Excalidraw theme={theme} />;
}
Persisting Theme Preference
Save the user’s theme choice:
import { Excalidraw } from "@excalidraw/excalidraw";
import { useState, useEffect } from "react";
function App() {
const [theme, setTheme] = useState(() => {
// Load from localStorage
return localStorage.getItem("excalidraw-theme") || "light";
});
useEffect(() => {
// Save to localStorage when theme changes
localStorage.setItem("excalidraw-theme", theme);
}, [theme]);
return (
<>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Toggle Theme
</button>
<Excalidraw theme={theme} />
</>
);
}
Automatic Toggle
When you don’t pass the theme prop, Excalidraw shows a built-in theme toggle:
import { Excalidraw } from "@excalidraw/excalidraw";
function App() {
// No theme prop = automatic theme toggle button
return <Excalidraw />;
}
From the source code at packages/excalidraw/index.tsx:82-87, when UIOptions.canvasActions.toggleTheme is null and theme is undefined, the toggle button is automatically enabled.
Disabling the Toggle
Explicitly disable the theme toggle button:
import { Excalidraw } from "@excalidraw/excalidraw";
function App() {
return (
<Excalidraw
UIOptions={{
canvasActions: {
toggleTheme: false, // Disable toggle button
},
}}
/>
);
}
Export with Theme
Dark Mode Export
Control the theme used when exporting:
import { Excalidraw } from "@excalidraw/excalidraw";
import { useState } from "react";
function App() {
const [excalidrawAPI, setExcalidrawAPI] = useState(null);
const exportWithDarkMode = async () => {
if (!excalidrawAPI) return;
const elements = excalidrawAPI.getSceneElements();
const appState = excalidrawAPI.getAppState();
// Update app state to export with dark mode
await excalidrawAPI.updateScene({
appState: {
...appState,
exportWithDarkMode: true,
},
});
};
return (
<>
<button onClick={exportWithDarkMode}>
Export with Dark Theme
</button>
<Excalidraw excalidrawAPI={(api) => setExcalidrawAPI(api)} />
</>
);
}
The exportWithDarkMode flag in AppState controls whether exports use dark theme styling, independent of the current editor theme.
Theme and App State
Initial Theme in AppState
Set the initial theme through initialData:
import { Excalidraw } from "@excalidraw/excalidraw";
function App() {
return (
<Excalidraw
initialData={{
appState: {
theme: "dark",
},
}}
/>
);
}
When both the theme prop and initialData.appState.theme are provided, the theme prop takes precedence.
Tracking Theme Changes
Monitor theme changes with the onChange callback:
import { Excalidraw } from "@excalidraw/excalidraw";
function App() {
const handleChange = (elements, appState) => {
console.log("Current theme:", appState.theme);
};
return <Excalidraw onChange={handleChange} />;
}
Internationalization and Theme
RTL Support with Themes
Excalidraw automatically handles right-to-left (RTL) languages with themes:
import { Excalidraw } from "@excalidraw/excalidraw";
function App() {
return (
<Excalidraw
theme="dark"
langCode="ar-SA" // Arabic (RTL)
/>
);
}
From packages/excalidraw/i18n.ts:92-95, when setting a language, Excalidraw automatically sets document.documentElement.dir to "rtl" or "ltr" based on the language’s RTL property.
Advanced Theming
Custom Background Colors
Customize the view background color:
import { Excalidraw } from "@excalidraw/excalidraw";
function App() {
return (
<Excalidraw
initialData={{
appState: {
viewBackgroundColor: "#1e1e1e",
},
}}
/>
);
}
Theme-Aware Components
Create custom UI components that adapt to the theme:
import { Excalidraw } from "@excalidraw/excalidraw";
import { useState } from "react";
function App() {
const [currentTheme, setCurrentTheme] = useState("light");
return (
<Excalidraw
theme={currentTheme}
renderTopRightUI={() => (
<div
style={{
padding: "10px",
background: currentTheme === "dark" ? "#1e1e1e" : "#ffffff",
color: currentTheme === "dark" ? "#ffffff" : "#000000",
}}
>
<button onClick={() => setCurrentTheme(currentTheme === "light" ? "dark" : "light")}>
Toggle Theme
</button>
</div>
)}
/>
);
}
Theme Constants
Using Theme Enums
Import theme constants from Excalidraw:
import { Excalidraw, THEME } from "@excalidraw/excalidraw";
function App() {
return <Excalidraw theme={THEME.DARK} />;
}
Available from @excalidraw/common:
THEME.LIGHT - “light”
THEME.DARK - “dark”
Best Practices
Default to system preference
Respect the user’s system theme preference by default.
Persist user choice
Save the user’s theme preference to localStorage.
Smooth transitions
Add CSS transitions for a smooth theme switching experience.
Test both themes
Ensure your custom UI components work well in both light and dark themes.
Next Steps