# How to Customize the Editor Theme and Dark Mode Using Shadcn UI in Reactjs-Tiptap-Editor

> Customize your reactjs-tiptap-editor theme and dark mode with Shadcn UI. Learn how to update CSS custom properties using themeActions for a personalized look.

- Repository: [Hung Hoang/reactjs-tiptap-editor](https://github.com/hunghg255/reactjs-tiptap-editor)
- Tags: how-to-guide
- Published: 2026-03-03

---

**You can customize the reactjs-tiptap-editor theme and dark mode by mounting the `ThemeColorReactive` component and calling `themeActions.setTheme()`, `setColor()`, or `setBorderRadius()` to update CSS custom properties that mirror Shadcn UI's design tokens.**

The **reactjs-tiptap-editor** library ships with a lightweight, Shadcn UI-compatible theming system that uses CSS custom properties for instant visual updates. All color palettes, border radii, and mode switches are managed through a reactive signal-based store that injects variables at runtime. This approach allows the editor to visually match any Shadcn UI component set without recompiling your application.

## How Theming Works Under the Hood

The theming architecture consists of three core layers: a static palette definition, a reactive state store, and a runtime CSS injector.

### The THEME Object and Signal Store

In [`src/theme/theme.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/theme/theme.ts), the library defines a `THEME` object containing **light** and **dark** HSL color palettes for multiple colorways (default, red, violet, etc.). A `themeSignal` store (using `reactjs-signal`) tracks the current `theme`, `color`, and `borderRadius` selections. The `useTheme()` hook provides read access to these values, while `themeActions` offers mutators: `setTheme()`, `setColor()`, and `setBorderRadius()`.

### Runtime CSS Injection

The `ThemeColorReactive` component (located in [`src/store/ThemeColorReactive.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/ThemeColorReactive.tsx)) watches the store via `useEffect`. When values change, it builds a CSS string from `THEME[theme][color]` and injects it into the document head using `updateCSS()` from [`src/utils/dynamicCSS.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/utils/dynamicCSS.ts). This creates a `<style data-id="richtext-theme">` tag with priority 50, ensuring the variables override defaults without conflicts. All editor styles in [`src/styles/editor.scss`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/styles/editor.scss) reference these variables (e.g., `background: hsl(var(--background))`), enabling instant theme switches.

## Setting Up Theme Reactivity

To enable theming in your application, import and render the `ThemeColorReactive` component at your root level. This component is already demonstrated in the library's playground at [`playground/src/App.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx).

```tsx
import { ThemeColorReactive } from 'reactjs-tiptap-editor/theme';
import { themeActions, useTheme } from 'reactjs-tiptap-editor/theme';

function App() {
  const { theme } = useTheme();

  return (
    <>
      {/* Injects CSS variables for the current theme */}
      <ThemeColorReactive />
      
      {/* Your editor components */}
      <button onClick={() => themeActions.setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Dark Mode
      </button>
    </>
  );
}

```

Mounting this component ensures that any subsequent calls to `themeActions` immediately update the editor's visual appearance.

## Switching Between Light and Dark Modes

Toggle between light and dark modes using the `setTheme()` action. The library defaults to `light` mode with a `0.65rem` border radius.

```tsx
import { themeActions } from 'reactjs-tiptap-editor/theme';

// Switch to dark mode
themeActions.setTheme('dark');

// Revert to light mode
themeActions.setTheme('light');

```

When `setTheme()` updates the `themeSignal` store, `ThemeColorReactive` re-renders and injects the corresponding dark or light palette from the `THEME` object into the CSS variables.

## Changing Color Palettes

Select from built-in Shadcn UI colorways using `setColor()`. Supported keys include `default`, `red`, `blue`, `green`, `orange`, `rose`, `violet`, and `yellow`, as defined in `THEME.light` and `THEME.dark`.

```tsx
// Apply the violet color palette
themeActions.setColor('violet');

```

These palettes contain HSL values identical to those generated by the Shadcn UI theme generator, ensuring visual consistency with Shadcn UI components.

## Adjusting Border Radius

Customize the global border radius independently of the color palette using `setBorderRadius()`. This updates the `--radius` CSS custom property, as implemented in `ThemeColorReactive` lines 28-30.

```tsx
// Set a tighter radius
themeActions.setBorderRadius('0.4rem');

// Or a rounder appearance
themeActions.setBorderRadius('0.75rem');

```

The `ThemeColorReactive` component specifically handles the `radius` token by substituting the user-provided value into the generated CSS block, overriding the static theme definition.

## Integrating Custom Shadcn UI Themes

To import a custom theme generated from [ui.shadcn.com/themes](https://ui.shadcn.com/themes), extend the `THEME` object in your application or modify [`src/theme/theme.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/theme/theme.ts) directly.

```ts
import { THEME } from 'reactjs-tiptap-editor/theme';

// Merge a custom palette from Shadcn UI generator
THEME.light.myCustom = {
  radius: '0.5rem',
  background: '0 0% 100%',
  foreground: '240 10% 3.9%',
  primary: '240 5.9% 10%',
  // ... additional tokens
};

// Now select it programmatically
themeActions.setColor('myCustom');

```

Because the editor uses standard CSS custom properties (`--background`, `--primary`, `--radius`, etc.), any descendant components—including your custom UI—automatically inherit the selected theme.

## Summary

- **Mount `ThemeColorReactive`** at your application root to enable runtime CSS variable injection from [`src/store/ThemeColorReactive.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/ThemeColorReactive.tsx).
- **Use `themeActions.setTheme()`** to toggle between `light` and `dark` modes via the signal-based store in [`src/theme/theme.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/theme/theme.ts).
- **Call `themeActions.setColor()`** to switch between Shadcn UI-compatible color palettes (violet, red, orange, etc.).
- **Adjust `themeActions.setBorderRadius()`** to modify the global `--radius` variable without touching CSS files.
- **Extend the `THEME` object** to import custom palettes from the Shadcn UI theme generator, maintaining full compatibility with Shadcn UI design tokens.

## Frequently Asked Questions

### How do I persist the user's theme selection across page reloads?

The `themeSignal` store in [`src/theme/theme.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/theme/theme.ts) manages state in memory only. To persist selections, subscribe to the `useTheme()` hook and save values to `localStorage`. On application mount, read these values and call `themeActions.setTheme()` and `setColor()` to restore the user's preferences before rendering the editor.

### Can I use the editor's theme variables in my own components outside the editor?

Yes. Because `ThemeColorReactive` injects variables into the document root, any component can reference them using standard CSS custom properties: `background: hsl(var(--primary))` or `border-radius: var(--radius)`. Ensure your components are descendants of the React tree where `ThemeColorReactive` is mounted.

### What happens if I don't include the `ThemeColorReactive` component?

Without `ThemeColorReactive`, the CSS custom properties (`--background`, `--foreground`, etc.) are never injected into the page. The editor will lack styling and appear unstyled or broken, as [`src/styles/editor.scss`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/styles/editor.scss) depends on these variables for all visual presentation.

### Does the library support system-level dark mode preferences?

The library does not automatically detect `prefers-color-scheme` in the default implementation. You must manually call `themeActions.setTheme('dark')` when the system preference changes. Implement a `useEffect` hook that listens to `window.matchMedia('(prefers-color-scheme: dark)')` events and updates the store accordingly to achieve automatic switching.