Skip to main content

Quick Start (Vanilla JS)

This section covers how to use Lexical, independently of any framework or library. For those intending to use Lexical in their React applications, it's advisable to check out the Getting Started with React page.

Creating an editor and using it

When you work with Lexical, you normally work with a single editor instance. An editor instance can be thought of as the one responsible for wiring up an EditorState with the DOM. The editor is also the place where you can register custom nodes, add listeners, and transforms.

An editor instance can be created from the lexical package and accepts an optional configuration object that allows for theming and other options:

import {createEditor} from 'lexical';

const config = {
namespace: 'MyEditor',
theme: {
onError: console.error

const editor = createEditor(config);

Once you have an editor instance, when ready, you can associate the editor instance with a content editable <div> element in your document:

const contentEditableElement = document.getElementById('editor');


If you want to clear the editor instance from the element, you can pass null. Alternatively, you can switch to another element if need be, just pass an alternative element reference to setRootElement().

Working with Editor States

With Lexical, the source of truth is not the DOM, but rather an underlying state model that Lexical maintains and associates with an editor instance. You can get the latest editor state from an editor by calling editor.getEditorState().

Editor states are serializable to JSON, and the editor instance provides a useful method to deserialize stringified editor states.

const stringifiedEditorState = JSON.stringify(editor.getEditorState().toJSON());

const newEditorState = editor.parseEditorState(stringifiedEditorState);

Updating an editor state

While it's not necessarily needed if using @lexical/rich-text or @lexical/plain-text helper packages, it's still relevant for programmatic content modification as well as in case of the custom editor fine tuning.

There are a few ways to update an editor instance:

  • Trigger an update with editor.update()
  • Setting the editor state via editor.setEditorState()
  • Applying a change as part of an existing update via editor.registerNodeTransform()
  • Using a command listener with editor.registerCommand(EXAMPLE_COMMAND, () => {...}, priority)

The most common way to update the editor is to use editor.update(). Calling this function requires a function to be passed in that will provide access to mutate the underlying editor state. When starting a fresh update, the current editor state is cloned and used as the starting point. From a technical perspective, this means that Lexical leverages a technique called double-buffering during updates. There's an editor state to represent what is current on the screen, and another work-in-progress editor state that represents future changes.

Creating an update is typically an async process that allows Lexical to batch multiple updates together in a single update – improving performance. When Lexical is ready to commit the update to the DOM, the underlying mutations and changes in the update will form a new immutable editor state. Calling editor.getEditorState() will then return the latest editor state based on the changes from the update.

Here's an example of how you can update an editor instance:

import {$getRoot, $getSelection, $createParagraphNode, $createTextNode} from 'lexical';

// Inside the `editor.update` you can use special $ prefixed helper functions.
// These functions cannot be used outside the closure, and will error if you try.
// (If you're familiar with React, you can imagine these to be a bit like using a hook
// outside of a React function component).
editor.update(() => {
// Get the RootNode from the EditorState
const root = $getRoot();

// Get the selection from the EditorState
const selection = $getSelection();

// Create a new ParagraphNode
const paragraphNode = $createParagraphNode();

// Create a new TextNode
const textNode = $createTextNode('Hello world');

// Append the text node to the paragraph

// Finally, append the paragraph to the root

It's important to note that the core library (the 'lexical' package) does not listen for any commands or perform any updates to the editor state in response to user events out-of-the-box. In order to see text and other content appear in the editor, you need to register command listeners and update the editor in the callback. Lexical provides a couple of helper packages to make it easy to wire up a lot of the basic commands you might want for plain text or rich text experiences.

If you want to know when the editor updates so you can react to the changes, you can add an update listener to the editor, as shown below:

editor.registerUpdateListener(({editorState}) => {
// The latest EditorState can be found as `editorState`.
// To read the contents of the EditorState, use the following API: => {
// Just like editor.update(), .read() expects a closure where you can use
// the $ prefixed helper functions.

Putting it together

Here we have simplest Lexical setup in rich text configuration (@lexical/rich-text) with history (@lexical/history) and accessibility (@lexical/dragon) features enabled.