How DrawDB Implements the Settings System for Editor Preferences: A React Context Deep Dive

DrawDB manages editor preferences through a centralized React Context that persists data to localStorage, supports URL-based theme overrides, and synchronizes UI state via reactive side effects.

The drawdb-io/drawdb repository powers a browser-based database diagramming tool requiring robust state management for user preferences. The settings system for editor preferences is implemented using a clean React Context architecture combined with localStorage persistence, ensuring consistent user experiences across browser sessions while allowing programmatic control and shareable configurations.

Architecture Overview: React Context as the Single Source of Truth

DrawDB treats editor preferences as application-wide state rather than component-local state. This design choice centralizes configuration management and eliminates prop drilling through the component tree.

The SettingsContext Provider

The core implementation resides in src/context/SettingsContext.jsx. This file defines the defaultSettings object, which specifies initial values for:

  • Strict mode (development safeguards)
  • Theme mode (dark/light appearance)
  • Autosave behavior (automatic diagram persistence)
  • UI toggles (grid visibility, comment display, snap-to-grid)

When the SettingsContextProvider mounts, it executes initialization logic that retrieves any previously saved preferences from localStorage. The system merges these stored values with defaultSettings, ensuring backward compatibility when new preferences are added to the application.

Provider Hierarchy and Application Wrapping

The provider wraps the entire editor interface to ensure all child components can access preference data. In src/pages/Editor.jsx and src/App.jsx, the SettingsContextProvider sits at the top of the component hierarchy, supplying the current settings object and the setSettings updater function to the entire render tree.

// In src/App.jsx or src/pages/Editor.jsx
<SettingsContextProvider>
  <EditorLayout />
</SettingsContextProvider>

Persistence and Initialization Strategy

The settings system implements a bidirectional data flow between React state and browser storage, ensuring user preferences survive page reloads.

Hydration from Local Storage

During the provider's initial mount, the system checks localStorage for existing preference data. If found, it deserializes the stored object and merges it with defaultSettings, giving precedence to user-saved values while maintaining defaults for any new or missing keys.

Automatic Serialization

A dedicated useEffect hook in src/context/SettingsContext.jsx serializes the complete settings object back to localStorage whenever state changes occur. This automatic persistence requires no manual intervention from consuming components.

// Automatic persistence handled by the provider
setSettings(prev => ({ ...prev, autosave: false }));
// Immediately written to localStorage via side effect

URL-Based Theme Override

Beyond localStorage persistence, the system supports external configuration via URL parameters, enabling users to share links that open DrawDB with a specific visual theme pre-selected.

Query Parameter Parsing

The provider checks queryConfig.theme during initialization. If the URL contains a valid theme parameter (e.g., ?theme=dark), this value overrides both the default setting and any stored localStorage preference for the mode property.

// Pseudocode from src/context/SettingsContext.jsx
const themeFromUrl = queryConfig.theme;
if (validThemes.includes(themeFromUrl)) {
  initialSettings.mode = themeFromUrl;
}

This implementation allows teams to share diagram links that open consistently across different user environments, regardless of individual browser settings.

Side Effect Synchronization

The settings system maintains tight integration with the DOM and browser APIs through reactive side effects defined in src/context/SettingsContext.jsx.

Theme Attribute Synchronization

One useEffect hook monitors settings.mode and updates the theme-mode attribute on the <body> element whenever the theme changes. This attribute selector allows CSS-in-JS solutions or traditional stylesheets to react dynamically to appearance changes without JavaScript intervention in individual components.

// Synchronizes body attribute for CSS selectors
useEffect(() => {
  document.body.setAttribute('theme-mode', settings.mode);
}, [settings.mode]);

Storage Persistence Effects

A separate useEffect handles the localStorage serialization, ensuring that every state update triggers a write operation to maintain session continuity.

Hook Abstraction and Component Consumption

To abstract the context import path and provide a clean API, DrawDB implements a custom hook in src/hooks/useSettings.js.

The useSettings Hook

This minimal wrapper returns the result of useContext(SettingsContext), exporting both the current settings object and the setSettings dispatch function.

// src/hooks/useSettings.js
import { useContext } from 'react';
import { SettingsContext } from '../context/SettingsContext';

export default function useSettings() {
  return useContext(SettingsContext);
}

Component Usage Patterns

Components throughout the application consume these preferences using the hook. In src/components/Workspace.jsx and various Canvas components like EditorCanvas/Table.jsx, developers destructure the context value to conditionally render UI elements or adjust behavior.

import useSettings from "../hooks/useSettings";

function GridToggle() {
  const { settings, setSettings } = useSettings();
  
  const toggleGrid = () => {
    setSettings(prev => ({
      ...prev,
      showGrid: !prev.showGrid,
    }));
  };

  return (
    <button onClick={toggleGrid}>
      {settings.showGrid ? "Hide Grid" : "Show Grid"}
    </button>
  );
}

Components also use the settings object directly for className decisions based on the current theme mode:

<div className={settings.mode === "dark" ? "bg-zinc-800" : "bg-zinc-100"}>
  {/* Content adapts to theme */}
</div>

Summary

  • Centralized State: The SettingsContextProvider in src/context/SettingsContext.jsx acts as the single source of truth for all editor preferences.
  • Default Configuration: Initial values are defined in the defaultSettings object, ensuring sensible defaults for new users.
  • Persistence Layer: Automatic synchronization with localStorage maintains user preferences across browser sessions without manual save actions.
  • URL Override: The system parses the theme query parameter to allow link-based theme sharing that supersedes stored preferences.
  • DOM Synchronization: Reactive effects update the theme-mode attribute on the document body, enabling CSS to react to theme changes.
  • Developer Experience: The useSettings hook in src/hooks/useSettings.js provides a clean abstraction for accessing or modifying preferences from any component in the tree.

Frequently Asked Questions

Where are the default editor preferences defined in DrawDB?

The default preferences are defined in the defaultSettings object located in src/context/SettingsContext.jsx. This object specifies initial values for strict mode, theme appearance, autosave intervals, and UI visibility toggles like grid and comment displays.

How does DrawDB persist settings between browser sessions?

Settings persistence is handled automatically by useEffect hooks within the SettingsContextProvider. When the provider mounts, it reads from localStorage and merges data with defaults. Subsequently, any call to setSettings triggers a side effect that serializes the entire settings object back to localStorage, ensuring changes survive page reloads.

Can I share a specific theme with someone using a URL?

Yes. DrawDB supports theme override via the URL query string. If you append ?theme=dark or ?theme=light to the application URL, the system parses this parameter during initialization and overrides both the default and stored mode values, opening the editor with your specified theme regardless of the recipient's local settings.

How do I access or modify editor preferences from a custom component?

Import and use the useSettings hook from src/hooks/useSettings.js. Destructure the returned object to access settings (current values) and setSettings (updater function). Use settings to read preference values and setSettings with the spread operator to update specific properties while preserving others.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →