Skip to content
Development
Skill

/web-editor-lexical

Extensible text editor framework by Meta. Use when building a rich text editor on Lexical — editor setup, custom nodes, commands, transforms, and serialization.

From plugin
agents-inc-skills
24200 skills
Install
$ npx -y skills add agents-inc/skills --skill web-editor-lexical --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/web-editor-lexical

Context preview

The summary Claude sees to decide when to auto-load this skill.

Extensible text editor framework by Meta. Use when building a rich text editor on Lexical — editor setup, custom nodes, commands, transforms, and serialization.

SKILL.md

web-editor-lexical.SKILL.md
name: web-editor-lexical
description: Extensible text editor framework by Meta. Use when building a rich text editor on Lexical — editor setup, custom nodes, commands, transforms, and serialization.

Lexical Editor Patterns

> **Quick Guide:** Lexical is an editor framework rather than an editor: the core gives you a node > tree, a selection model, a reconciler, a command bus and an update lifecycle, and everything else > is a plugin. EditorState is immutable, so every read and every mutation happens inside an > `editor.update()` or `editor.read()` closure, and the `$`-prefixed functions are the ones that > require that context. Extend the tree through `ElementNode`, `TextNode` or `DecoratorNode`, and > react to content through transforms rather than listeners. **Current: v0.42.x, pre-1.0 — APIs > still move between minors.**

**Detailed Resources:**

  • [examples/core.md](examples/core.md) — editor setup, plugins with cleanup, toolbars, transforms, persistence
  • [examples/custom-nodes.md](examples/custom-nodes.md) — ElementNode, TextNode and DecoratorNode classes, the NodeState and `$config` APIs
  • [examples/serialization.md](examples/serialization.md) — JSON and HTML round trips, `exportDOM`/`importDOM`, headless editors
  • [reference.md](reference.md) — package map, command priorities, built-in commands, node hierarchy, plugin list, custom-node checklist

---

Which path applies

  • **A React app** — `LexicalComposer` owns the editor and every plugin is a child component

reaching it through `useLexicalComposerContext()`. Start at [examples/core.md](examples/core.md).

  • **A server or a build step, with no DOM** — `createHeadlessEditor` from `@lexical/headless` runs

the same node classes for search indexing, email rendering and content transforms. Register the same nodes as the client. See [examples/serialization.md](examples/serialization.md).

  • **Adding a content type** — the work is a node class plus its registration, and the branch that

matters is which base node it extends. See the decision framework below and [examples/custom-nodes.md](examples/custom-nodes.md).

---

<critical_requirements>

Before writing Lexical code

**Call `$`-prefixed functions inside an `editor.update()` or `editor.read()` closure.** `$getRoot`, `$getSelection`, `$createTextNode` and their siblings read the active editor state from a context that only exists inside those closures; outside one they throw at runtime, and nothing catches it at compile time.

**Register every custom node in `initialConfig.nodes`.** An unregistered node throws or silently drops content the moment the editor meets it, including on deserialization of previously saved documents.

**Return the unsubscribe function from the `useEffect` that registers a command, transform or listener.** Every `register*` method hands one back, and dropping it leaks a listener per render.

**Open every transform with a precondition that the mutation makes false.** A transform that mutates its target unconditionally marks the node dirty, which re-triggers the transform and freezes the editor.

</critical_requirements>

---

**Auto-detection:** Lexical, `@lexical/react`, `@lexical/rich-text`, `@lexical/list`, `@lexical/code`, `@lexical/link`, `@lexical/html`, `@lexical/headless`, LexicalComposer, EditorState, LexicalNode, ElementNode, TextNode, DecoratorNode, createCommand, dispatchCommand, registerCommand, COMMAND_PRIORITY, `$getRoot`, `$getSelection`, `$createParagraphNode`, `$createTextNode`, RichTextPlugin, OnChangePlugin, HistoryPlugin, useLexicalComposerContext, editor.update, editor.read, registerNodeTransform, exportJSON, importJSON, exportDOM, importDOM, NodeState, createState

**Applies to:**

  • Rich text editing with custom formatting and embedded content
  • Custom content types — mentions, embeds, callouts, polls — as node classes
  • Structured document output rather than an HTML string
  • Server-side or build-time processing of editor content
  • Collaborative editing, where Lexical supplies the binding point

**Handled elsewhere:**

  • Visual design of the editor — the theme maps class names onto nodes, and what those classes

contain is settled by whatever owns styling

  • Where the serialized document goes — the editor hands back JSON, and the transport and store are

not its concern

  • Real-time sync between clients — Lexical exposes the state to bind, and the sync layer itself is

a separate concern

  • Sanitizing HTML entering or leaving the editor — `$generateNodesFromDOM` trusts what it is given

---

<philosophy>

Lexical ships a core and no editor. The tree, selection, reconciler, command bus and update lifecycle are the product; toolbars, lists, links, embeds and formatting are all plugins, including the ones Meta writes.

**EditorState is immutable.** The editor holds a frozen snapshot. `editor.update()` clones it, applies the closure's changes, and reconciles the difference to the DOM — which is why a stale read outside a closure has no state to read and throws.

**A plugin is a React component.** It renders as a child of `<LexicalComposer>`, reaches the editor through `useLexicalComposerContext()`, and registers its commands, transforms and listeners in a `useEffect` that returns their unsubscribes. Many plugins render `null`.

**Commands are the bus.** Typed commands with priority-ordered listeners let one plugin intercept or augment another's behaviour without either knowing about the other.

**Content is typed nodes.** A new kind of content is a new node class, not a new attribute.

Lexical is pre-1.0, so a project that needs a frozen API surface should weigh that before adopting it. Weigh the node model too: a `DecoratorNode` is a real node in the tree, so it serializes and moves with the content around it. An editor whose requirement is a purely visual overlay — highlights or annotations that must never enter the saved document — is asking for something Lexical's decorators do not do.

</philosophy>

---

Read more
Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin

Other skills on agents-inc-skills.