Fumadocs Editor

Studio

Edit a folder of MDX files without writing a host app

Studio is the editor as a command. Run it in your Fumadocs app:

npx @fumadocs-editor/studio

It opens a browser with your documents and a file list:

  • edits save to disk, and changes on disk merge in (Sync)
  • the file tree creates, deletes and reorders pages
  • ?collab in the URL turns on collaboration for that tab
Studio needs Node 24 or later.

Keyboard

KeysDoes
⌘Ksearch files and actions (theme, collab, MDX source)
⌘Ssave now
Escclose the file list, when focused

The file list has its own shortcuts.

Flags

FlagDefault
--root <dir>content/docs, content, then the cwd
--config <file>fumadocs-studio.config.ts
--port <n>5180
--hostlisten on all addresses
--no-opendo not open the browser

Configuration

Create a fumadocs-studio.config.ts where you run Studio. Flags override it.

fumadocs-studio.config.ts
import { defineConfig } from "@fumadocs-editor/studio";
import { admonitionSpec, fumadocsUiComponents } from "@fumadocs-editor/ui";

export default defineConfig({
  root: "content/docs",
  port: 5180,
  components: [...fumadocsUiComponents, admonitionSpec],
  syntax: { math: true },
  theme: "system",
  // stylesheets for your own renderers
  styles: ["./studio.css"],
  // node-only options live in their own module
  server: "./studio.server.ts",
});

Prop

Type

components, syntax, theme and media work like the MdxEditor props. Your own components follow the authoring guide, and their styles load through styles.

Server options

The config also runs in the browser, so anything Node-only (a JWT library, a Vite plugin) goes in a separate file referenced by server:

studio.server.ts
import { defineServerConfig } from "@fumadocs-editor/studio";
import tailwindcss from "@tailwindcss/vite";

export default defineServerConfig({
  // same hook as the sync server: ?token=… reaches `payload`
  authenticate: ({ payload }) => (payload === process.env.STUDIO_TOKEN ? { write: true } : null),
  upload: { maxBytes: 5 * 1024 * 1024 },
  vite: { plugins: [tailwindcss()] },
});

Prop

Type

Auth

Without authenticate, anyone who can reach the port can edit. With it, the credential comes from ?token= in the URL by default; override it with auth. See Auth for writing the hook.

Last updated on

On this page