Skip to main content

Overview

The editor and all plugins are headless by default: they define structure and behavior but do not ship UI for how block elements look. You can keep them headless (e.g. for custom design) or attach styled UI from a theme package. Available themes:
  • @yoopta/themes-shadcn — Shadcn UI styled components (production ready)
  • @yoopta/themes-material — Material Design styled components (in progress)
Themes provide UI components for plugin elements (e.g. Callout, Code, Image). You can either apply a theme to all plugins at once with applyTheme(), or attach theme UI to individual plugins via plugin.extend({ elements: ThemeUI }).

Concept: headless + optional UI

Plugins define their block type, elements, and behavior; they do not require any specific look. Theme packages export:
  1. Per-plugin UI objects — e.g. CalloutUI from @yoopta/themes-shadcn/callout, which you pass into Callout.extend({ elements: CalloutUI }).
  2. applyTheme(plugins) — a helper that applies the theme’s UI to every supported plugin in the array in one go.
So you can:
  • Use plugins as-is (headless) and render elements yourself.
  • Use theme UI for a single plugin: Callout.extend({ elements: CalloutUI }).
  • Use theme UI for all plugins: applyTheme([Paragraph, Callout, ...]).

Apply theme to all plugins

Use applyTheme() from a theme package to wrap your plugin list; the theme will attach its styled elements to every plugin it supports.
npm install @yoopta/themes-shadcn
import { createYooptaEditor } from '@yoopta/editor';
import { applyTheme } from '@yoopta/themes-shadcn';
import Paragraph from '@yoopta/paragraph';
import Callout from '@yoopta/callout';
import Headings from '@yoopta/headings';

const plugins = applyTheme([
  Paragraph,
  Callout,
  Headings.HeadingOne,
  Headings.HeadingTwo,
  Headings.HeadingThree,
]);

const editor = createYooptaEditor({ plugins, marks: [] });
Then pass plugins (the result of applyTheme) into createYooptaEditor. The theme’s CSS is applied by the package; you don’t pass plugins or marks to <YooptaEditor>.

Apply theme UI to a single plugin

If you only want themed UI for specific plugins, import the theme’s UI object for that plugin and extend the plugin with elements. Example: Callout with Shadcn UI
import Callout from '@yoopta/callout';
import { CalloutUI } from '@yoopta/themes-shadcn/callout';

const CalloutWithUI = Callout.extend({
  elements: CalloutUI,
});
Use CalloutWithUI in your plugins array; other plugins can stay headless or use other themes. Subpaths are available per plugin, for example:
  • @yoopta/themes-shadcn/calloutCalloutUI
  • @yoopta/themes-shadcn/paragraphParagraphUI
  • @yoopta/themes-shadcn/embedEmbedUI
  • …and other plugins supported by the theme

@yoopta/themes-shadcn

Production-ready theme based on Shadcn UI. It provides styled elements for paragraph, headings, lists, blockquote, callout, code, image, video, embed, file, table, tabs, steps, accordion, carousel, divider, link, mention, and table-of-contents. Install
npm install @yoopta/themes-shadcn
Apply to all plugins
import { applyTheme } from '@yoopta/themes-shadcn';

const plugins = applyTheme([
  Paragraph,
  Headings.HeadingOne,
  Callout,
  // ...
]);
Apply to one plugin
import { CalloutUI } from '@yoopta/themes-shadcn/callout';

Callout.extend({ elements: CalloutUI });

CSS Variables

The theme reads from standard shadcn/ui CSS variables (--background, --foreground, --card, --border, etc.). If your app already uses shadcn/ui, the theme works out of the box — no extra setup needed. If you don’t have shadcn CSS variables defined, import the default variables provided by the package:
import '@yoopta/themes-shadcn/variables.css';
This gives you a complete set of light and dark mode color tokens.

Dark mode

The theme supports dark mode through CSS variables. When dark mode is active, the variable values change and the editor automatically picks up the new colors (background, text, borders, etc.). Dark mode activates when any ancestor of the editor has one of these selectors:
  • .dark class (default for shadcn/ui and Tailwind dark mode)
  • [data-theme="dark"] attribute
  • [data-yoopta-theme="dark"] attribute
Example: toggle dark mode
function App() {
  const [isDark, setIsDark] = useState(false);

  return (
    <div className={isDark ? 'dark' : ''}>
      <button onClick={() => setIsDark(!isDark)}>Toggle theme</button>
      <YooptaEditor editor={editor}>
        {/* ... */}
      </YooptaEditor>
    </div>
  );
}
Or using a data attribute:
<div data-theme={isDark ? 'dark' : 'light'}>
  <YooptaEditor editor={editor} />
</div>

Customizing variables

You can override any variable to match your brand. The theme uses HSL values (without the hsl() wrapper):
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --card: 0 0% 100%;
  --card-foreground: 222.2 84% 4.9%;
  --popover: 0 0% 100%;
  --popover-foreground: 222.2 84% 4.9%;
  --primary: 221.2 83.2% 53.3%;
  --primary-foreground: 210 40% 98%;
  --secondary: 210 40% 96.1%;
  --secondary-foreground: 222.2 47.4% 11.2%;
  --muted: 210 40% 96.1%;
  --muted-foreground: 215.4 16.3% 46.9%;
  --accent: 210 40% 96.1%;
  --accent-foreground: 222.2 47.4% 11.2%;
  --destructive: 0 84.2% 60.2%;
  --destructive-foreground: 210 40% 98%;
  --border: 214.3 31.8% 91.4%;
  --input: 214.3 31.8% 91.4%;
  --ring: 221.2 83.2% 53.3%;
  --radius: 0.5rem;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  /* ... your dark mode overrides */
}
You only need to define the variables you want to change — the rest will use defaults from variables.css (if imported) or your existing shadcn/ui setup. Full list of variables used by the theme:
VariableUsage
--backgroundEditor background
--foregroundEditor text color
--card / --card-foregroundCard-style blocks (callout, code, etc.)
--popover / --popover-foregroundDropdowns, menus, tooltips
--primary / --primary-foregroundPrimary actions, buttons
--secondary / --secondary-foregroundSecondary actions
--muted / --muted-foregroundMuted text, disabled states
--accent / --accent-foregroundHover highlights, accents
--destructive / --destructive-foregroundDelete actions, errors
--borderBorders
--inputInput field borders
--ringFocus rings
--radiusBorder radius base value

@yoopta/themes-material

Material Design styled components for Yoopta plugins. In progress — not all plugins may be covered yet. Install
npm install @yoopta/themes-material
Usage (same idea as Shadcn: applyTheme(plugins) or plugin.extend({ elements: MaterialUI }) per plugin when the package exposes it).

Summary

ApproachUse case
HeadlessCustom design; you implement all element UI.
plugin.extend({ elements: ThemeUI })Use theme UI only for specific plugins.
applyTheme(plugins)Use theme UI for all supported plugins at once.
The editor and plugins stay headless; themes only provide optional UI for their elements.