Lexical Extensions
Extensions are a new convention to add configuration and behavior to a Lexical editor in a modular and framework-independent manner. It is easier to use and more powerful than the React plug-in scheme that came before, and is framework agnostic.
See Included Extensions for the list of extensions that come with Lexical and its associated packages.
Stabilityโ
๐งช This API is experimental, and may evolve with no deprecation period. Be prepared to track its development closely if you adopt it early.
Use Caseโ
Lexical Extensions allow complex features to be added to your editor
in a single place. If you want to have an editor that supports check
lists, you simply add the CheckListExtension
to your dependencies.
All of its dependent configuration, registration, and dependencies on
other extensions is encapsulated. Extensions also work the same way
with or without React, so you don't generally need separate
documentation and exports to support both.
All of the complexity for how and when things need to happen is
moved to buildEditorFromExtensions
and the definitions of the
extensions themselves.
In contrast, using checklists without Lexical Extensions requires
ListNode
and ListItemNode
to be configured, and both the ListPlugin
and
CheckListPlugin
to be registered exactly one time each (or if not using
React: registerList
and registerCheckList
).
Customizing a Lexical editor without Lexical Extensions is done in two phases:
- Configuration before the editor is constructed (registered nodes, node replacement, theme, etc.)
- Registration after the editor is constructed (listeners, transforms, commands, react plug-ins, etc.)
In many cases, adding a feature to the editor requires both configuration and registration. These typically happen at very different places in the code, possibly even different files, and there are no transitive dependencies. It's up to you to make sure that every required snippet of configuration and registration code is included exactly once in the right place. When some dependent configuration or registration is missing (or runs twice!), at best you get a runtime error, but often it will partially work which is tricky to debug.
Another compelling use case for extensions is to provide functionality from one extension to another extension (e.g. collaboration with yjs), or have one extension enhance the capabilities of another (e.g. markdown import/export, adding options to menus or toolbars, etc.). This is very awkward and error-prone with legacy plug-ins since there is no established pattern for them to depend on each other, and providing this sort of functionality with React requires a strict hierarchy of context providers. Any extension can provide output, which is equivalent to having extension-specific context scoped to an editor.
Core API Overviewโ
The primary use case for extensions is to build an editor from a collection of extensions. Any configuration or behavior specific to your editor should be provided as an extension with defineExtension or as an override of some dependency's configuration with configExtension. This is done with either:
- buildEditorFromExtensions - when not using React
- LexicalExtensionComposer - when using React
See Defining Extensions for a detailed guide for
defineExtension
.
The following functions are included in the core lexical module to ensure that defining extensions is lightweight and requires no dependency on the runtime features of @lexical/extension. These functions are primarily for type inference with very minimal implementations.
- defineExtension - define an extension
- configExtension - provide configuration overrides for an extension
- declarePeerDependency - declare an indirect dependency on another extension by name
- safeCast - a convenience for TypeScript casting, with more safety than
as
. Similar to using a variable with a declared type. - shallowMergeConfig - the default configuration merge strategy
Advanced featuresโ
- Signals - Can be used for extensions to change their configuration or behavior at runtime, such as dynamically enabling or disabling an extension. This is also used to facilitate some performance optimizations around subscriptions and listeners.
- Peer Dependencies - Used to declare indirect or optional dependencies between extensions, this can be used to enable some functionality or configuration only when some other extension is available. For example, an Emoji extension may declare a specific Markdown serialization for EmojiNode, but only if the Markdown extension is present.