Skip to main content

Documentation Index

Fetch the complete documentation index at: https://react.email/docs/llms.txt

Use this file to discover all available pages before exploring further.

Everything is accessed through a single import:
import {
  EditorFocusScope,
} from '@react-email/editor/ui';
The FocusScopes extension, included by default in StarterKit, keeps the editor’s focus and blur behavior in sync across registered surfaces. Wrap each custom portaled surface that should still count as being inside the editor UI with EditorFocusScope.
Inspector.Root, built-in bubble menus, and slash commands already register themselves. Inside custom portaled UI, you usually only add EditorFocusScope around the portaled content.

EditorFocusScopeProvider

EditorFocusScopeProvider is deprecated. Focus tracking moved to the FocusScopes extension, which is included by default in StarterKit, but the provider still installs the same tracking plugin for editors that do not use StarterKit.

Example

import { StarterKit } from '@react-email/editor/extensions';
import { EditorFocusScope } from '@react-email/editor/ui';
import { EditorContent, EditorContext, useEditor } from '@tiptap/react';
import * as Popover from '@radix-ui/react-popover';

export function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit.configure()],
    content,
  });

  return (
    <EditorContext.Provider value={{ editor }}>
      <EditorContent editor={editor} />

      <Popover.Root>
        <Popover.Trigger type="button">Theme presets</Popover.Trigger>

        <Popover.Portal>
          <EditorFocusScope>
            <Popover.Content sideOffset={8}>
              <button type="button">Apply preset</button>
            </Popover.Content>
          </EditorFocusScope>
        </Popover.Portal>
      </Popover.Root>
    </EditorContext.Provider>
  );
}
Use EditorFocusScope for each portaled surface that should still count as being inside the editor UI.

Props

children
ReactNode
Rendered unchanged.
clearSelectionOnBlur
boolean
default:"true"
Deprecated. Used only by the provider’s fallback plugin. Prefer configuring FocusScopes in StarterKit instead.

EditorFocusScope

Use EditorFocusScope around portaled UI like Radix Select.Content, Popover.Content, or dialog content so moving focus into that portal is still treated as staying inside the editor UI. EditorFocusScope registers with the editor’s FocusScopes extension. If the extension is not present, it renders its children unchanged.

Example

import { EditorFocusScope, Inspector } from '@react-email/editor/ui';
import * as Select from '@radix-ui/react-select';

<Inspector.Node>
  {({ getAttr, setAttr }) => (
    <Select.Root
      value={String(getAttr('alignment') ?? 'left')}
      onValueChange={(value) => setAttr('alignment', value)}
    >
      <Select.Trigger>
        <Select.Value />
      </Select.Trigger>

      <Select.Portal>
        <EditorFocusScope>
          <Select.Content>
            <Select.Viewport>
              <Select.Item value="left">
                <Select.ItemText>Left</Select.ItemText>
              </Select.Item>
              <Select.Item value="center">
                <Select.ItemText>Center</Select.ItemText>
              </Select.Item>
              <Select.Item value="right">
                <Select.ItemText>Right</Select.ItemText>
              </Select.Item>
            </Select.Viewport>
          </Select.Content>
        </EditorFocusScope>
      </Select.Portal>
    </Select.Root>
  )}
</Inspector.Node>
If you are building a focus-aware editor shell without StarterKit, add the FocusScopes extension and then use EditorFocusScope for each portaled surface.

Props

children
ReactNode
The element or subtree to register as an additional editor focus scope. Wrap the portaled container that receives focus.