Dark mode
How the editor follows Payload's dark mode, the preview light and dark toggle, and detecting the preview theme in components.
The Puck editor detects when Payload's admin is in dark mode and adjusts its own UI to stay readable. Separately, a toggle lets you preview the page itself in light or dark mode.
How it works
- Editor UI. The editor detects dark mode from Payload's
data-themeattribute (the theme chosen in Payload's account settings), a.darkclass, or the OSprefers-color-schemepreference, then injects overrides for Puck's CSS variables. - Preview iframe. A sun/moon toggle switches the preview content between light and dark, independently of the editor UI.
Configuration
Both are on by default. Change them with props on PuckEditor:
<PuckEditor
autoDetectDarkMode={true} // Follow Payload's dark mode (default: true)
showPreviewDarkModeToggle={true} // Show the light/dark toggle (default: true)
initialPreviewDarkMode={false} // Start the preview in light mode (default: false)
/>Building your own editor
For a custom editor implementation, use the pieces directly:
'use client'
import { useState } from 'react'
import {
DarkModeStyles,
PreviewModeToggle,
useDarkMode,
} from '@delmaredigital/payload-puck/editor'
function CustomEditor() {
const { isDarkMode, source } = useDarkMode()
const [previewDark, setPreviewDark] = useState(false)
return (
<>
<DarkModeStyles />
<PreviewModeToggle
isDarkMode={previewDark}
onToggle={setPreviewDark}
/>
<Puck ... />
</>
)
}useDarkMode() returns isDarkMode and source, which is 'attribute', 'class', 'media-query' or 'none'.
Detecting the preview theme in components
CSS dark-mode variants, such as Tailwind's dark: classes, work in the preview automatically through the data-theme attribute. Values computed in JavaScript, such as an overlay colour, don't update when the toggle changes. For those, read usePuckPreviewTheme(), which updates reactively. It returns null outside the editor:
import { usePuckPreviewTheme } from '@delmaredigital/payload-puck/editor'
import { useState } from 'react'
function useDetectTheme() {
const puckTheme = usePuckPreviewTheme()
// On the frontend (outside the editor), read from the DOM
const [domTheme] = useState(() =>
typeof document !== 'undefined'
? document.documentElement.getAttribute('data-theme') === 'dark'
: false
)
// In the editor: use context. On the frontend: use the DOM.
return puckTheme !== null ? puckTheme : domTheme
}A layout can also start its preview dark with editorDarkMode: true; see Layouts.

