Multiple themes
Register several named themes and switch between them at runtime.
import {
DEFAULT_THEME,
Editor,
TLTheme,
TLThemeId,
TLThemes,
Tldraw,
TldrawUiButton,
TldrawUiButtonLabel,
structuredClone,
toRichText,
useEditor,
useValue,
} from 'tldraw'
import 'tldraw/tldraw.css'
import './multiple-themes.css'
// There's a guide at the bottom of this file!
// [1]
declare module '@tldraw/tlschema' {
interface TLThemes {
ocean: TLTheme
sunset: TLTheme
}
}
// [2]
const OCEAN_THEME: TLTheme = structuredClone(DEFAULT_THEME)
OCEAN_THEME.id = 'ocean'
OCEAN_THEME.colors.light.blue.solid = '#2b7ab5'
OCEAN_THEME.colors.light.violet.solid = '#6a5acd'
OCEAN_THEME.colors.light.violet.noteFill = '#b8a9e8'
OCEAN_THEME.colors.light.violet.noteText = '#000000'
OCEAN_THEME.colors.dark.blue.solid = '#3a9bd5'
OCEAN_THEME.colors.dark.violet.solid = '#7b6bd4'
OCEAN_THEME.colors.dark.violet.noteFill = '#3a2d6e'
OCEAN_THEME.colors.dark.violet.noteText = '#f2f2f2'
const SUNSET_THEME: TLTheme = structuredClone(DEFAULT_THEME)
SUNSET_THEME.id = 'sunset'
SUNSET_THEME.colors.light.blue.solid = '#e07038'
SUNSET_THEME.colors.light.violet.solid = '#9b4dca'
SUNSET_THEME.colors.light.violet.noteFill = '#dab0f0'
SUNSET_THEME.colors.light.violet.noteText = '#000000'
SUNSET_THEME.colors.dark.blue.solid = '#f0884a'
SUNSET_THEME.colors.dark.violet.solid = '#b060d8'
SUNSET_THEME.colors.dark.violet.noteFill = '#5c2870'
SUNSET_THEME.colors.dark.violet.noteText = '#f2f2f2'
// [3]
const themes: Partial<TLThemes> = {
ocean: OCEAN_THEME,
sunset: SUNSET_THEME,
}
function createInitialShapes(editor: Editor) {
if (editor.getCurrentPageShapeIds().size > 0) return
editor.createShapes([
{
type: 'geo',
x: 100,
y: 100,
props: { w: 200, h: 200, color: 'blue', richText: toRichText('Try switching themes') },
},
{
type: 'note',
x: 350,
y: 100,
props: { color: 'violet', richText: toRichText('Colors change per theme') },
},
])
}
export default function MultipleThemesExample() {
return (
<div className="tldraw__editor">
{/* [4] */}
<Tldraw
persistenceKey="multiple-themes-example"
themes={themes}
onMount={createInitialShapes}
>
<ThemeSwitcher />
</Tldraw>
</div>
)
}
// [5]
const THEME_LABELS: Record<TLThemeId, string> = {
default: 'Default',
ocean: 'Ocean',
sunset: 'Sunset',
}
function ThemeSwitcher() {
const editor = useEditor()
const currentThemeId = useValue('themeId', () => editor.getCurrentThemeId(), [editor])
return (
<div className="tlui-menu theme-switcher">
{(Object.keys(THEME_LABELS) as TLThemeId[]).map((id) => (
<TldrawUiButton
key={id}
type={currentThemeId === id ? 'primary' : 'normal'}
onClick={() => editor.setCurrentTheme(id)}
>
<TldrawUiButtonLabel>{THEME_LABELS[id]}</TldrawUiButtonLabel>
</TldrawUiButton>
))}
</div>
)
}
/*
[1]
Extend the `TLThemes` interface to register custom theme IDs.
This gives you type-safe theme names throughout the API — `editor.setCurrentTheme('ocean')`
will autocomplete, and passing an unregistered name will be a type error.
[2]
Define theme objects by cloning `DEFAULT_THEME` and overriding just the colors
you want to change. Each theme contains both `light` and `dark` color palettes.
[3]
Collect the themes in a `Partial<TLThemes>`. The `default` theme is always registered,
so only the additional themes need to be listed. Keep this object at module level: it's
a prop of `<Tldraw>`, and a new object each render would re-register the themes.
[4]
`<Tldraw>` registers the `themes` at mount time and keeps them updated if the prop
changes. You can also register or update themes later with `editor.updateTheme()`.
[5]
`editor.setCurrentTheme(id)` switches theme at runtime and `editor.getCurrentThemeId()`
is reactive, so reading it in `useValue` keeps the active button highlighted.
*/
Themes are keyed by id in the TLThemes interface. Augment that interface with your theme ids so editor.setCurrentTheme() is type-checked, build each theme by cloning DEFAULT_THEME and overriding the colors you care about, and pass them to <Tldraw> through the themes prop. Switch at runtime with editor.setCurrentTheme(id); editor.getCurrentThemeId() is reactive.
Try clicking the theme buttons at the top left. The blue rectangle and violet note recolor because those palette entries differ between the themes.
Is this page helpful?
Prev
Custom themeNext
Hide UI components