Fumadocs Editor

MdxEditor

Props, ref and sync options

import { MdxEditor } from "@fumadocs-editor/ui";

Props

Prop

Type

sync

Prop

Type

Page variant

variant="page" makes the editor the page: no frame, the header row with the sync status and mode tabs stays at the top, and the document scrolls in a column centered on the viewport. header puts your own controls in that row. Studio is built on it.

page.tsx
import { useState, type CSSProperties } from "react";
import { MdxEditor } from "@fumadocs-editor/ui";

export function Page({ path }: { path: string }) {
  const [panel, setPanel] = useState(false);
  return (
    // the open panel takes a column of its own instead of covering the text
    <div style={{ "--fde-page-inset": panel ? "18rem" : "0px" } as CSSProperties}>
      <MdxEditor
        variant="page"
        header={{
          start: (
            <button type="button" onClick={() => setPanel(!panel)}>
              Files
            </button>
          ),
        }}
        sync={{ path }}
      />
      {panel && (
        <aside style={{ position: "fixed", top: "3.25rem", left: "0.75rem", width: "17rem" }} />
      )}
    </div>
  );
}

Two custom properties on an ancestor tune the column: --fde-page-width (default 52rem) and --fde-page-inset, the space at the start the column keeps clear of, for a panel floating there. The column stays centered while there is room and moves right of the inset when there is not.

Parts

MdxEditor is a preset: sync status, Visual / MDX tabs, one surface at a time. When that layout does not fit, compose the parts yourself.

import { MdxEditor, useSourceText } from "@fumadocs-editor/ui";

function Source() {
  const { value, onChange, error, readOnly } = useSourceText();
  return <CodeMirror value={value} onChange={onChange} readOnly={readOnly} />;
}

<MdxEditor.Root sync={{ path }} ref={ref}>
  <MdxEditor.Status />
  <div className="split">
    <MdxEditor.Visual />
    <Source />
  </div>
</MdxEditor.Root>;
PartWhat it is
MdxEditor.RootThe document: parse, sync, collab, the ref. Takes the props below minus the frame ones
MdxEditor.VisualThe WYSIWYG surface, static paint first
MdxEditor.SourceThe MDX in a textarea, parse error above it
MdxEditor.StatusSync dot and the keep-mine / take-disk chip; nothing without sync
MdxEditor.TabsVisual / MDX switch for hosts that show one surface at a time

Ref

Prop

Type

The ref satisfies SyncedDocument, so it can be handed to createFileSession directly.

Keyboard

The toolbar appears over selected text. A resting caret has no chrome, so formatting comes from the keyboard:

KeysDoes
⌘B ⌘I ⌘E ⌘⇧SToggle bold, italic, code or strikethrough; with no selection, for what you type next
at the line's endStop the marks in force, so the next words come out plain
⌘.Open the block's menu (attributes, inserts, move, delete)
⌥↑ ⌥↓Move the block
EscSelect the component around the caret, then its parent
⌘ASelect the region, then the component around it, then its parent; plain select-all after
Tab ⇧TabNext or previous region of a component
⌘EnterInsert the next sibling (a new Step, Tab, File) from anywhere inside one

With a mouse, double-click a component's chrome (its icon, rail or padded edge) to select it; its menu and joystick follow. Code blocks and tables have no chrome to double-click, so a joystick appears beside them while the caret is inside: click it to select the block, drag it to move it.

On a touch screen there are no shortcuts, so the toolbar stays up while you type, below the caret or the selection and clear of the system's copy menu. The block's joystick sits in the gutter beside the block.

EditorThemeProvider

Theme context for standalone hosts. Tracks the choice, persists it and sets .light or .dark on a wrapper so the --fde-* tokens resolve. Read or change it with useEditorTheme().

Prop

Type

Last updated on

On this page