Overview
WebEditor includes automatic theme detection and management with support for:
Auto mode : Follows system preferences
Light mode : Forces light theme
Dark mode : Forces dark theme
Persistent storage : Saves user preference to localStorage
Reactive updates : Responds to system theme changes
Automatic theme detection
By default, WebEditor automatically detects and applies your browser’s preferred theme:
import { WebEditor } from "@devscribe-team/webeditor" ;
import "@devscribe-team/webeditor/styles" ;
function App () {
return < WebEditor /> ;
}
The editor will:
Check for a saved theme preference in localStorage
Fall back to the system’s preferred color scheme
Apply the appropriate theme classes to the document root
Using the useTheme hook
The useTheme hook provides full control over the editor’s theme:
import { WebEditor , useTheme } from "@devscribe-team/webeditor" ;
import "@devscribe-team/webeditor/styles" ;
function App () {
const { theme , resolvedTheme , setTheme } = useTheme ();
return (
< div >
< div className = "theme-controls" >
< button onClick = { () => setTheme ( "light" ) } > Light </ button >
< button onClick = { () => setTheme ( "dark" ) } > Dark </ button >
< button onClick = { () => setTheme ( "auto" ) } > Auto </ button >
< span >
Current: { theme } (resolved: { resolvedTheme } )
</ span >
</ div >
< WebEditor />
</ div >
);
}
Hook return values
theme
The current theme setting: "light", "dark", or "auto"
const { theme } = useTheme ();
console . log ( theme ); // "auto", "light", or "dark"
resolvedTheme
The actual theme being displayed: "light" or "dark"
const { resolvedTheme } = useTheme ();
console . log ( resolvedTheme ); // "light" or "dark"
When theme is set to "auto", resolvedTheme reflects the system preference. Otherwise, it matches the theme setting.
Use resolvedTheme when you need to know the actual theme being displayed, regardless of whether it’s set manually or automatically.
setTheme
Function to change the theme:
const { setTheme } = useTheme ();
// Set to light mode
setTheme ( "light" );
// Set to dark mode
setTheme ( "dark" );
// Set to auto (follow system)
setTheme ( "auto" );
The new theme preference is automatically saved to localStorage under the key "webeditor-theme".
toggleTheme
Function to cycle through themes: auto → light → dark → auto
const { toggleTheme } = useTheme ();
// Cycles through: auto → light → dark → auto
toggleTheme ();
Theme persistence
Theme preferences are automatically saved to localStorage:
// The hook automatically handles persistence
const { setTheme } = useTheme ();
setTheme ( "dark" );
// Saved to localStorage as: webeditor-theme = "dark"
On subsequent page loads, the editor will:
Read the saved theme from localStorage
Apply it immediately
Continue watching for system preference changes (if in auto mode)
Reactive system updates
When using auto mode, the editor listens for system theme changes:
const { theme , resolvedTheme } = useTheme ();
// When theme is "auto":
// - User changes system from light to dark
// - resolvedTheme automatically updates to "dark"
// - Editor re-renders with dark theme
Auto mode provides the best user experience by respecting system-wide preferences and time-of-day settings.
Building a theme toggle
Here’s a complete example of a theme toggle component:
import { useTheme } from "@devscribe-team/webeditor" ;
import { Moon , Sun , Monitor } from "lucide-react" ;
function ThemeToggle () {
const { theme , setTheme } = useTheme ();
return (
< div className = "flex gap-2" >
< button
onClick = { () => setTheme ( "light" ) }
className = { theme === "light" ? "active" : "" }
aria-label = "Light mode"
>
< Sun className = "w-4 h-4" />
</ button >
< button
onClick = { () => setTheme ( "dark" ) }
className = { theme === "dark" ? "active" : "" }
aria-label = "Dark mode"
>
< Moon className = "w-4 h-4" />
</ button >
< button
onClick = { () => setTheme ( "auto" ) }
className = { theme === "auto" ? "active" : "" }
aria-label = "Auto mode"
>
< Monitor className = "w-4 h-4" />
</ button >
</ div >
);
}
How it works
The theme system works by:
Reading initial preference from localStorage or system (use-theme.ts:11-25)
Applying theme classes to document.documentElement (use-theme.ts:95-105)
Watching system changes with window.matchMedia (use-theme.ts:58-92)
Persisting changes to localStorage on theme updates (use-theme.ts:39-45)
View the useTheme implementation
export function useTheme () : {
theme : Theme ;
resolvedTheme : 'light' | 'dark' ;
setTheme : ( theme : Theme ) => void ;
toggleTheme : () => void ;
} {
// Initialize from localStorage or default to auto
const [ theme , setThemeState ] = useState < Theme >(() => {
if ( typeof window === 'undefined' ) return 'auto' ;
const savedTheme = localStorage . getItem ( 'webeditor-theme' ) as Theme | null ;
if ( savedTheme && ( savedTheme === 'light' || savedTheme === 'dark' || savedTheme === 'auto' )) {
return savedTheme ;
}
return 'auto' ;
});
// Resolve actual theme being displayed
const [ resolvedTheme , setResolvedTheme ] = useState < 'light' | 'dark' >(() => {
if ( typeof window === 'undefined' ) return 'light' ;
if ( theme === 'auto' ) {
return window . matchMedia ( '(prefers-color-scheme: dark)' ). matches ? 'dark' : 'light' ;
}
return theme === 'dark' ? 'dark' : 'light' ;
});
// Save to localStorage and update state
const setTheme = ( newTheme : Theme ) => {
setThemeState ( newTheme );
if ( typeof window !== 'undefined' ) {
localStorage . setItem ( 'webeditor-theme' , newTheme );
}
};
// Apply theme class to document root
useEffect (() => {
if ( typeof document !== 'undefined' ) {
const root = document . documentElement ;
root . classList . remove ( 'dark' , 'light' );
root . classList . add ( resolvedTheme );
}
}, [ resolvedTheme ]);
return { theme , resolvedTheme , setTheme , toggleTheme };
}
Best practices
Use auto mode as default
Let users’ system preferences take precedence by defaulting to auto mode
Provide manual controls
Always offer light/dark toggle buttons for users who want to override
Indicate current theme
Show which theme is active in your UI to avoid confusion
Test both themes
Ensure your custom styles work in both light and dark modes