### Lexical Monorepo Development Setup Source: https://lexical.dev/docs/api Commands to set up the development environment for the Lexical monorepo. Includes installing dependencies, starting the dev server, and running tests. ```bash # Install dependencies pnpm install # Start playground dev server pnpm run start # Run tests pnpm run test-unit pnpm run test-e2e-chromium # Lint and type check pnpm run ci-check ``` -------------------------------- ### Install Playwright browsers Source: https://lexical.dev/docs/testing Required setup step to download the necessary browser binaries for E2E testing. ```bash pnpm exec playwright install ``` -------------------------------- ### Initialize Rich Text Editor Source: https://lexical.dev/docs/getting-started/quick-start Example setup for a rich text editor including history and dragon support. ```typescript /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */ import './styles.css'; import {registerDragonSupport} from '@lexical/dragon'; import {createEmptyHistoryState, registerHistory} from '@lexical/history'; import {HeadingNode, QuoteNode, registerRichText} from '@lexical/rich-text'; import {mergeRegister} from '@lexical/utils'; import {createEditor, HISTORY_MERGE_TAG} from 'lexical'; import prepopulatedRichText from './prepopulatedRichText'; document.querySelector('#app')!.innerHTML = ` ``` -------------------------------- ### Full Initial Setup Source: https://lexical.dev/docs/maintainers-guide Combines bootstrapping and trust configuration for a new monorepo setup. ```bash pnpm run setup-trusted-publishing --bootstrap --setup-trust ``` -------------------------------- ### Install @lexical/headless Source: https://lexical.dev/docs/packages/lexical-headless Install the @lexical/headless package using npm. ```bash npm install --save @lexical/headless ``` -------------------------------- ### Install Lexical and React Package Source: https://lexical.dev/docs/getting-started/react Install the necessary Lexical packages for React integration. This command installs the core lexical library and the React-specific wrapper. ```bash npm install --save lexical @lexical/react ``` -------------------------------- ### Install Lexical Table and React Packages Source: https://lexical.dev/docs/packages/lexical-table Install the necessary packages for Lexical tables and React integration. ```bash npm install @lexical/table @lexical/react ``` -------------------------------- ### selectStart() Source: https://lexical.dev/docs/api/modules/lexical Selects the start of the node. ```APIDOC ### selectStart() **Signature:** `selectStart(): RangeSelection` ``` -------------------------------- ### Start WebSocket Server for Collaboration Source: https://lexical.dev/docs/collaboration/react Run a WebSocket server to enable communication between different browser clients. This setup uses y-websocket and supports persistence to save Yjs documents between server restarts. ```bash $ HOST=localhost PORT=1234 YPERSISTENCE=./yjs-wss-db npx y-websocket ``` -------------------------------- ### Lexical React App with Plugins Source: https://lexical.dev/docs/extensions/migration This example shows a typical setup for a Lexical React application using individual plugins like ToolbarPlugin, HistoryPlugin, and AutoFocusPlugin. It imports each plugin separately and configures them within the LexicalComposer. ```jsx import {ContentEditable} from '@lexical/react/LexicalContentEditable'; import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin'; import {LexicalComposer} from '@lexical/react/LexicalComposer'; import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary'; import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin'; import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin'; import ExampleTheme from './ExampleTheme'; import ToolbarPlugin from './plugins/ToolbarPlugin'; import TreeViewPlugin from './plugins/TreeViewPlugin'; const placeholderText = 'Enter some rich text...'; const contentEditable = ( {placeholderText}} /> ); const editorConfig = { namespace: 'React.js Demo', nodes: [], // Handling of errors during update onError(error: Error) { throw error; }, // The editor theme theme: ExampleTheme, }; export default function App() { return (
); } ``` -------------------------------- ### Install @lexical/eslint-plugin Source: https://lexical.dev/docs/packages/lexical-eslint-plugin Install the ESLint plugin as a development dependency using npm. ```bash npm install @lexical/eslint-plugin --save-dev ``` -------------------------------- ### getStart() Source: https://lexical.dev/docs/api/modules/lexical_list Retrieves the starting index of the list node. ```APIDOC ## getStart() ### Description Returns the starting number of the list node. ### Returns - **number** - The start index. ``` -------------------------------- ### Full Document Traversal with CaretRange Source: https://lexical.dev/docs/concepts/traversals Demonstrates a full depth-first traversal of a Lexical document tree using CaretRange, starting from the root and covering all child nodes. This example shows how to define the start and end points of the traversal. ```javascript const carets = [...$getCaretRange( // Start with the arrow pointing towards the first child of root $getChildCaret($getRoot(), 'next'), // End when the arrow points away from root $getSiblingCaret($getRoot(), 'next'), )]; expect(carets).toEqual([ $getChildCaret(paragraphA, 'next'), // enter Paragraph A $getSiblingCaret(textA1, 'next'), $getChildCaret(linkA2, 'next'), // enter Link A2 $getSiblingCaret(textA3, 'next'), $getSiblingCaret(linkA2, 'next'), // leave Link A2 $getSiblingCaret(textA4, 'next'), $getSiblingCaret(paragraphA, 'next'), // leave Paragraph A $getChildCaret(paragraphB, 'next'), // enter Paragraph B $getSiblingCaret(textB1, 'next'), $getSiblingCaret(paragraphB, 'next'), // leave Paragraph B $getChildCaret(paragraphC, 'next'), // enter Paragraph C $getSiblingCaret(paragraphC, 'next'), // leave Paragraph C ]); ``` -------------------------------- ### Install Lexical Dependencies Source: https://lexical.dev/docs/api Installs the necessary Lexical packages for your project using npm. ```bash npm install lexical @lexical/react ``` -------------------------------- ### Example importJSON for HeadingNode Source: https://lexical.dev/docs/serialization This example demonstrates implementing `importJSON` for a `HeadingNode` using `$createHeadingNode` and `updateFromJSON` for simplified deserialization. ```typescript static importJSON(serializedNode: SerializedHeadingNode): HeadingNode { return $createHeadingNode().updateFromJSON(serializedNode); } updateFromJSON( serializedNode: LexicalUpdateJSON, ): this { return super.updateFromJSON(serializedNode).setTag(serializedNode.tag); } ``` -------------------------------- ### Initialize Editor with DOM Import Source: https://lexical.dev/docs/serialization/dom-import Demonstrates the minimal setup required to import HTML content using the extension pipeline. ```typescript import { buildEditorFromExtensions, getExtensionDependencyFromEditor, } from '@lexical/extension'; import { CoreImportExtension, DOMImportExtension, } from '@lexical/html'; import {$getRoot, defineExtension} from 'lexical'; const editor = buildEditorFromExtensions( defineExtension({ name: 'app', dependencies: [CoreImportExtension], }), ); const dep = getExtensionDependencyFromEditor(editor, DOMImportExtension); editor.update(() => { const dom = new DOMParser().parseFromString( '

Hello world

', 'text/html', ); const nodes = dep.output.$generateNodesFromDOM(dom); $getRoot().clear().splice(0, 0, nodes); }); ``` -------------------------------- ### Install Dragon support for iframes Source: https://lexical.dev/docs/packages/lexical-dragon Pass the iframe's window object to install the listener within the specific iframe context. ```javascript installDragonSupport(iframe.contentWindow); ``` -------------------------------- ### Lexical Composer with Initial Configuration Source: https://lexical.dev/docs/react/create_plugin Example of initializing LexicalComposer with a specific configuration, including custom nodes. ```jsx ``` -------------------------------- ### Install walk-wide overlays via preprocessor Source: https://lexical.dev/docs/serialization/dom-import Use a preprocessor to conditionally inject overlays into the session for the entire import walk. ```typescript import { defineOverlayRules, type DOMPreprocessFn, ImportOverlays, } from '@lexical/html'; const WordPasteOverlay = defineOverlayRules([ WordOPRule, WordListParagraphRule, // … ]); const $installWordOverlay: DOMPreprocessFn = (dom, ctx, $next) => { const meta = dom.querySelector('meta[name="Generator"]'); if (meta && /Microsoft Word/i.test(meta.getAttribute('content') || '')) { ctx.session.update(ImportOverlays, (prev) => [...prev, WordPasteOverlay]); } $next(); }; ``` -------------------------------- ### Build Lexical Editor with Extensions Source: https://lexical.dev/docs/api/modules/lexical_extension Examples demonstrating how to construct a Lexical editor using extensions, including dependency management and configuration overrides. ```typescript const editor = buildEditorFromExtensions( defineExtension({ name: "[root]", dependencies: [ RichTextExtension, configExtension(EmojiExtension, { emojiBaseUrl: "/assets/emoji" }), ], register: (editor: LexicalEditor) => { console.log("Editor Created"); return () => console.log("Editor Disposed"); }, }), ); ``` ```typescript const editor = buildEditorFromExtensions( RichTextExtension, configExtension(EmojiExtension, { emojiBaseUrl: "/assets/emoji" }), ); ``` -------------------------------- ### installDragonSupport() Source: https://lexical.dev/docs/api/modules/lexical_dragon Installs the shared window listener that handles Dragon NaturallySpeaking's web extension messages for registered editors. ```APIDOC ## installDragonSupport(targetWindow?) ### Description Installs the shared window listener that handles Dragon NaturallySpeaking's web extension messages for every registered editor in the given window. This should be called synchronously from entrypoints that may render an editor lazily to ensure the listener is registered before the extension's own listener. ### Parameters - **targetWindow** (Window | undefined) - Optional - The window to install the listener on. Defaults to the current window. ### Returns - **Function** - A teardown function to remove the listener. ``` -------------------------------- ### setStart() Source: https://lexical.dev/docs/api/modules/lexical_list Sets the starting index of the list node. ```APIDOC ## setStart(start) ### Description Sets the starting number for the list node. ### Parameters - **start** (number) - Required - The start index. ### Returns - **this** - The current node instance. ``` -------------------------------- ### Install Lexical React and Yjs Dependencies Source: https://lexical.dev/docs/collaboration/react Install the necessary packages for Lexical React and Yjs collaboration. This includes the core Lexical packages, React, and Yjs with its WebSocket provider. ```bash $ npm i -S @lexical/react @lexical/yjs lexical react react-dom y-websocket yjs ``` -------------------------------- ### Run E2E tests Source: https://lexical.dev/docs/testing Starts the playground application and executes the E2E test suite for specific browsers. ```bash pnpm start & pnpm test-e2e-chromium # or -firefox, -webkit ``` -------------------------------- ### Basic Lexical Editor Setup Source: https://lexical.dev/docs/api Sets up a basic Lexical editor with a plain text plugin and history plugin. This configuration is suitable for simple text input. ```jsx import { $getRoot, $getSelection } from 'lexical'; import { LexicalComposer } from '@lexical/react/LexicalComposer'; import { PlainTextPlugin } from '@lexical/react/LexicalPlainTextPlugin'; import { ContentEditable } from '@lexical/react/LexicalContentEditable'; import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'; import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'; const initialConfig = { namespace: 'MyEditor', onError: (error) => console.error(error), }; function Editor() { return ( } ErrorBoundary={LexicalErrorBoundary} /> ); } ``` -------------------------------- ### Dispatch Command from Toolbar Source: https://lexical.dev/docs/concepts/commands Example of triggering a list insertion command from a UI component. ```typescript const formatBulletList = () => { editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND); }; ``` -------------------------------- ### Dispatch Examples from LexicalEvents Source: https://lexical.dev/docs/concepts/commands Common patterns for dispatching commands using events or formatting strings. ```typescript editor.dispatchCommand(KEY_ARROW_LEFT_COMMAND, event); // ... editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic'); ``` -------------------------------- ### Example exportJSON for HeadingNode Source: https://lexical.dev/docs/serialization This example shows how to implement `exportJSON` for a `HeadingNode`, including its specific `tag` property along with the superclass properties. ```typescript export type SerializedHeadingNode = Spread< { tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; }, SerializedElementNode >; exportJSON(): SerializedHeadingNode { return { ...super.exportJSON(), tag: this.getTag(), }; } ``` -------------------------------- ### Basic Lexical Composer Setup Source: https://lexical.dev/docs/react/create_plugin A basic structure for initializing Lexical with a custom plugin. Ensure any new nodes are registered in initialConfig.nodes. ```jsx ``` -------------------------------- ### Lexical ListPlugin Setup Source: https://lexical.dev/docs/react/plugins Integrates the ListPlugin to provide support for creating and managing ordered and unordered lists within the editor. ```jsx ``` -------------------------------- ### $createListNode(listType?, start?) Source: https://lexical.dev/docs/api/modules/lexical_list Creates a ListNode of a specified type. ```APIDOC ## $createListNode(listType?, start?) ### Description Creates a ListNode of listType. ### Parameters - **listType** (ListType) - Optional - The type of list to be created. Can be 'number', 'bullet', or 'check'. Default: 'number'. - **start** (number) - Optional - Where an ordered list starts its count. Default: 1. ### Returns - **ListNode** - The new ListNode. ``` -------------------------------- ### Implement LexicalAutoEmbedPlugin Source: https://lexical.dev/docs/api/modules/lexical_react_LexicalAutoEmbedPlugin Example showing how to define a custom embed configuration and integrate the LexicalAutoEmbedPlugin into a React component. ```typescript interface CustomEmbedConfig extends EmbedConfig<{ domain: string; oid?: string; }> { // Icon for display. icon?: JSX.Element; // Embed a Figma Project. description?: string; }; return ( embedConfigs={EmbedConfigs} getMenuOptions={getMenuOptions} /> ); ``` -------------------------------- ### Configure Flat State Serialization Source: https://lexical.dev/docs/concepts/node-state Example of using the $config method to define a flat state configuration. ```typescript $config() { return this.config('colored', { extends: TextNode, stateConfigs: [{flat: true, stateConfig: colorState}], }); } ``` -------------------------------- ### Migrate from LexicalComposer to LexicalExtensionComposer Source: https://lexical.dev/docs/extensions/react Switch from using `LexicalComposer` with an `initialConfig` to `LexicalExtensionComposer` with a defined `extension`. This simplifies setup and allows for easier integration of extensions. ```jsx } ErrorBoundary={LexicalErrorBoundary} /> {/* other legacy React plugins */} ``` ```jsx } ErrorBoundary={LexicalErrorBoundary} /> {/* other legacy React plugins */} ``` -------------------------------- ### CSS for Editor Placeholders and Paragraphs Source: https://lexical.dev/docs/getting-started/theming Example CSS rules for the 'editor-placeholder' and 'editor-paragraph' classes used in Lexical theming. ```css .editor-placeholder { color: #999; overflow: hidden; position: absolute; top: 15px; left: 15px; user-select: none; pointer-events: none; } .editor-paragraph { margin: 0 0 15px 0; position: relative; } ``` -------------------------------- ### Implement an Import Rule with InlineSchema Source: https://lexical.dev/docs/serialization/dom-import Example of using an import rule to create a HeadingNode and import children using the InlineSchema. ```typescript const HeadingRule = defineImportRule({ match: sel.tag('h1', 'h2', 'h3', 'h4', 'h5', 'h6'), $import: (ctx, el) => { const node = $createHeadingNode(el.nodeName.toLowerCase() as HeadingTagType); node.splice(0, 0, ctx.$importChildren(el, {schema: InlineSchema})); return [node]; }, }); ``` -------------------------------- ### Initialize package source directory Source: https://lexical.dev/docs/maintainers-guide Creates the source directory and entrypoint file for the new package. ```bash mkdir -p packages/lexical-eslint-plugin/src code packages/lexical-eslint-plugin/src/index.ts ``` -------------------------------- ### Get Sibling Carets from a Specific Node Source: https://lexical.dev/docs/concepts/traversals Shows how to get sibling carets starting from a specific node. The iteration begins at the node where the 'arrow' head points. ```javascript const carets = [...$getSiblingCaret(paragraphB, 'next')]; expect(carets).toEqual([ $getSiblingCaret(paragraphC, 'next'), ]); const prevCarets = [...$getSiblingCaret(paragraphB, 'previous')]; expect(prevCarets).toEqual([ $getSiblingCaret(paragraphA, 'previous'), ]); ``` -------------------------------- ### Get Child Carets from Root Source: https://lexical.dev/docs/concepts/traversals Illustrates how to get the first and last child carets from the root node. The traversal starts at the first child for 'next' and the last child for 'previous'. ```javascript const carets = [...$getChildCaret($getRoot(), 'next')]; // next starts at the first child expect(carets).toEqual([ $getSiblingCaret(paragraphA, 'next'), $getSiblingCaret(paragraphB, 'next'), $getSiblingCaret(paragraphC, 'next'), ]); // previous starts at the last child const prevCarets = [...$getChildCaret($getRoot(), 'previous')]; expect(prevCarets).toEqual([ $getSiblingCaret(paragraphC, 'previous'), $getSiblingCaret(paragraphB, 'previous'), $getSiblingCaret(paragraphA, 'previous'), ]); ``` -------------------------------- ### Initialize unit test directory Source: https://lexical.dev/docs/maintainers-guide Creates the directory and file for unit testing the new package. ```bash mkdir -p packages/lexical-eslint-plugin/src/__tests__/unit code packages/lexical-eslint-plugin/src/__tests__/unit/LexicalEslintPlugin.test.ts ``` -------------------------------- ### Iterating Siblings with NodeCaret Source: https://lexical.dev/docs/concepts/traversals Demonstrates how to iterate through all sibling nodes starting from a given NodeCaret. Note that NodeCaret itself is iterable, making this function a conceptual example. ```typescript function *$iterSiblings( startCaret: NodeCaret ): Iterable> { // Note that we start at the adjacent caret. The start caret // points away from the origin node, so we do not want to // trick ourselves into thinking that that origin is included. for ( let caret = startCaret.getAdjacentCaret(); caret !== null; caret = caret.getAdjacentCaret() ) { yield caret; } } ``` -------------------------------- ### Integrate OnChangePlugin in React Editor Source: https://lexical.dev/docs/getting-started/react Integrate the custom `MyOnChangePlugin` into a React Lexical editor setup. This example shows how to use the plugin to update a React state variable with the latest editor state. ```javascript function MyOnChangePlugin({ onChange }) { const [editor] = useLexicalComposerContext(); useEffect(() => { return editor.registerUpdateListener(({editorState}) => { onChange(editorState); }); }, [editor, onChange]); return null; } function Editor() { // ... const [editorState, setEditorState] = useState(); function onChange(editorState) { setEditorState(editorState); } return ( Enter some text...} /> } ErrorBoundary={LexicalErrorBoundary} /> ); } ``` -------------------------------- ### Configure HistoryExtension with maxDepth Source: https://lexical.dev/docs/concepts/history Set a finite `maxDepth` to cap the undo stack at a fixed length. This example sets the maximum depth to 100, which is a common starting point for interactive editing sessions. ```typescript import {configExtension, defineExtension} from 'lexical'; import {HistoryExtension} from '@lexical/history'; const editorExtension = defineExtension({ // ... dependencies: [ // 100 events is a reasonable starting point — it matches the // ProseMirror history plugin's default depth and supports a deep // enough undo stack for almost any interactive editing session. configExtension(HistoryExtension, {maxDepth: 100}), ], }); ``` -------------------------------- ### Lexical CheckListPlugin Setup Source: https://lexical.dev/docs/react/plugins Adds the CheckListPlugin for creating and managing check lists. Note that custom CSS is required for rendering check/uncheck marks. ```jsx ``` -------------------------------- ### Bootstrap New Packages Source: https://lexical.dev/docs/maintainers-guide Publishes a placeholder version to the registry to claim a package name before configuring trust. ```bash npm login --registry https://registry.npmjs.org pnpm run setup-trusted-publishing --bootstrap ``` -------------------------------- ### connect() Source: https://lexical.dev/docs/api/modules/lexical_yjs Establishes the connection for the provider. ```APIDOC ## connect() ### Description Establishes the connection for the provider. ### Returns - **void | Promise** ``` -------------------------------- ### Build Editor with Basic Rich Text Extension Source: https://lexical.dev/docs/extensions/defining-extensions This example demonstrates building a Lexical editor with a basic rich text configuration. It sets a namespace, includes a dependency, and defines register and cleanup functions for the editor's lifecycle. ```javascript const editor = buildEditorFromExtensions( defineExtension({ name: "@example/basic-rich-text-editor", namespace: "basic-rich-text-editor", dependencies: [RichTextExtension], register: (editor: LexicalEditor) => { console.log("Editor Created"); return () => console.log("Editor Disposed"); }, }), ); ``` -------------------------------- ### Install and Build Lexical, Link to Downstream App Source: https://lexical.dev/docs/maintainers-guide-link Build Lexical artifacts in the checkout and then add them to your downstream project using `pnpm`'s `link:` protocol. This allows immediate visibility of Lexical rebuilds. ```bash # in the Lexical checkout pnpm install pnpm run build-release # produces dist/.{dev,prod,node}.{js,mjs} + .d.ts + .js.flow # in your downstream app pnpm add link:/path/to/lexical/packages/lexical pnpm add link:/path/to/lexical/packages/lexical-react # etc. ``` -------------------------------- ### Raw HTML Markdown Example Source: https://lexical.dev/docs/packages/lexical-mdast An example of a Markdown structure using HTML tags like details and summary. ```markdown
The *summary* line The **body** blocks
``` -------------------------------- ### Initialize Editor with Initial State and Persist Source: https://lexical.dev/docs/concepts/editor-state Demonstrates how to load an initial editor state from a backend, initialize the editor, and then save the current editor state. Assumes helper functions `loadContent`, `createEditor`, `registerRichText`, and `saveContent` are defined. ```javascript // Get editor initial state (e.g. loaded from backend) const loadContent = async () => { // 'empty' editor const value = '{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'; return value; } const initialEditorState = await loadContent(); const editor = createEditor(...); registerRichText(editor, initialEditorState); ... // Handler to store content (e.g. when user submits a form) const onSubmit = () => { await saveContent(JSON.stringify(editor.getEditorState())); } ``` -------------------------------- ### Vanilla JS App - After Minimal Migration Source: https://lexical.dev/docs/extensions/migration This snippet demonstrates migrating to Lexical Builder with minimal changes, using `buildEditorFromExtensions` for editor creation while still manually registering some plugins. ```javascript import {registerDragonSupport} from '@lexical/dragon'; import {createEmptyHistoryState, registerHistory} from '@lexical/history'; import {HeadingNode, QuoteNode, registerRichText} from '@lexical/rich-text'; import {mergeRegister} from '@lexical/utils'; import {buildEditorFromExtensions} from '@lexical/extension'; import $prepopulatedRichText from './$prepopulatedRichText'; const editorRef = document.getElementById('lexical-editor'); const stateRef = document.getElementById( 'lexical-state', ) as HTMLTextAreaElement; const editor = buildEditorFromExtensions({ // Any string is suitable as long as it uniquely defines the Extension in the editor name: '[root]', namespace: 'Vanilla JS Demo (with Lexical Extension)', // Register nodes specific for @lexical/rich-text nodes: [HeadingNode, QuoteNode], // onError boilerplate removed theme: {quote: 'PlaygroundEditorTheme__quote'}, }); editor.setRootElement(editorRef); // Registering Plugins mergeRegister( registerRichText(editor), registerDragonSupport(editor), registerHistory(editor, createEmptyHistoryState(), 300), ); editor.update($prepopulatedRichText, {tag: 'history-merge'}); ``` -------------------------------- ### $getTextNodeOffset() Source: https://lexical.dev/docs/api/modules/lexical Gets a normalized offset into a TextNode. ```APIDOC ## $getTextNodeOffset(origin, offset, mode?) ### Description Get a normalized offset into a TextNode given a numeric offset or a direction. Throws in dev if the offset is out of bounds. ### Parameters - **origin** (TextNode) - Required - A TextNode - **offset** (number | CaretDirection) - Required - An absolute offset or a direction - **mode** ("error" | "clamp") - Optional - Defaults to "error". Determines behavior for out of bounds offsets. ### Returns - **number** - An absolute offset into the TextNode string. ``` -------------------------------- ### Initialize Editor with MdastTableExtension Source: https://lexical.dev/docs/api/modules/lexical_mdast Demonstrates how to configure an editor instance using the MdastTableExtension and MdastShortcutsExtension. ```typescript import {MdastShortcutsExtension, MdastTableExtension} from '@lexical/mdast'; import {buildEditorFromExtensions} from '@lexical/extension'; import {defineExtension} from 'lexical'; const editor = buildEditorFromExtensions( defineExtension({ dependencies: [MdastShortcutsExtension, MdastTableExtension], name: '[root]', }), ); ``` -------------------------------- ### Initialize Editor with Markdown Extensions Source: https://lexical.dev/docs/api/modules/lexical_mdast Demonstrates how to build an editor instance using MdastCommonMarkExtension to enable Markdown conversion. ```typescript import {$convertFromMarkdownString, MdastCommonMarkExtension} from '@lexical/mdast'; import {buildEditorFromExtensions} from '@lexical/extension'; import {defineExtension} from 'lexical'; const editor = buildEditorFromExtensions( defineExtension({dependencies: [MdastCommonMarkExtension], name: '[root]'}), ); editor.update(() => $convertFromMarkdownString('# Hi')); ``` -------------------------------- ### Run package update script Source: https://lexical.dev/docs/maintainers-guide Generates boilerplate documentation and configuration files for the new package. ```bash pnpm run update-packages ``` -------------------------------- ### $isAtStartOfNode Source: https://lexical.dev/docs/api/modules/lexical_utils Checks if the point is at the start of the node's content. ```APIDOC ## $isAtStartOfNode(point, node) ### Description Whether the collapsed point sits at the very start of node's content — on its first descendant (or on the empty node itself) at offset 0. ### Parameters - **point** (PointType) - Required - **node** (ElementNode) - Required ### Returns - **boolean** ``` -------------------------------- ### Create package workspace directory Source: https://lexical.dev/docs/maintainers-guide Initializes the directory structure for a new package. ```bash mkdir -p packages/lexical-eslint-plugin ``` -------------------------------- ### getElementByKey Source: https://lexical.dev/docs/api/modules/lexical Gets the underlying HTMLElement associated with a specific LexicalNode key. ```APIDOC ## getElementByKey(key) ### Description Gets the underlying HTMLElement associated with the LexicalNode for the given key. ### Parameters - **key** (string) - Required - The key of the LexicalNode. ### Returns - **HTMLElement | null** - The HTMLElement rendered by the LexicalNode associated with the key. ``` -------------------------------- ### $generateJSONFromSelectedNodes() Source: https://lexical.dev/docs/api/modules/lexical_clipboard Gets the Lexical JSON of the nodes inside the provided Selection. ```APIDOC ## $generateJSONFromSelectedNodes(editor, selection) ### Description Gets the Lexical JSON of the nodes inside the provided Selection. ### Parameters - **editor** (LexicalEditor) - Required - LexicalEditor to get the JSON content from. - **selection** (BaseSelection | null) - Required - Selection to get the JSON content from. ### Returns - **object** - An object containing the editor namespace and a list of serializable nodes. ``` -------------------------------- ### splice(start, deleteCount, nodesToInsert) Source: https://lexical.dev/docs/api/modules/lexical Splices nodes into the element node. ```APIDOC ## splice(start, deleteCount, nodesToInsert) ### Description Modifies the contents of the element node by removing or replacing existing nodes and/or adding new nodes. ### Parameters - **start** (number) - Required - The starting index. - **deleteCount** (number) - Required - The number of nodes to delete. - **nodesToInsert** (LexicalNode[]) - Required - The nodes to insert. ### Returns - **this** - The current node instance. ``` -------------------------------- ### $getRenderContextValue Source: https://lexical.dev/docs/api/modules/lexical_html Get a render context value during a DOM render or export operation. ```APIDOC ## $getRenderContextValue(cfg, editor?) ### Description Get a render context value during a DOM render or export operation. ### Parameters - **cfg** (RenderStateConfig) - Required - The configuration for the render state. - **editor** (LexicalEditor) - Optional - The editor instance. ### Returns - **V** - The value from the render context. ``` -------------------------------- ### Create Lexical Editor Instance Source: https://lexical.dev/docs/packages/lexical Instantiate a Lexical editor with a namespace and theme configuration. This is the core editor engine. ```javascript import {createEditor} from 'lexical'; const config = { namespace: 'MyEditor', theme: { ... }, }; const editor = createEditor(config); ``` -------------------------------- ### Get Root Sibling Carets Source: https://lexical.dev/docs/concepts/traversals Demonstrates that the root node does not have any sibling nodes. ```javascript const carets = [...$getSiblingCaret($getRoot(), 'next')]; expect(carets).toEqual([]); ``` -------------------------------- ### Create Editor with Theme in Vanilla JS Source: https://lexical.dev/docs/getting-started/theming Initialize a Lexical editor with a theme object using vanilla JavaScript. Pass the theme to the createEditor function. ```javascript import {createEditor} from 'lexical'; const editor = createEditor({ namespace: 'MyEditor', theme: exampleTheme, }); ``` -------------------------------- ### $convertFromMarkdownString() Source: https://lexical.dev/docs/api/modules/lexical_markdown Renders markdown from a string into the editor. The selection is moved to the start after the operation. ```APIDOC ## $convertFromMarkdownString(markdown, transformers?, node?, shouldPreserveNewLines?, shouldMergeAdjacentLines?) ### Description Renders markdown from a string. The selection is moved to the start after the operation. ### Parameters - **markdown** (string) - Required - **transformers** (Transformer[]) - Optional - Default: TRANSFORMERS - **node** (ElementNode) - Optional - **shouldPreserveNewLines** (boolean) - Optional - Default: false - **shouldMergeAdjacentLines** (boolean) - Optional - Default: false ### Returns - **void** ``` -------------------------------- ### Basic Lexical Editor Configuration with Plugins Source: https://lexical.dev/docs/react/plugins Sets up a basic Lexical editor using LexicalComposer and integrates PlainTextPlugin, HistoryPlugin, and OnChangePlugin. ```jsx const initialConfig = { namespace: 'MyEditor', theme, onError, }; } placeholder={
Enter some text...
} /> ...
; ``` -------------------------------- ### Default NodeState Serialization Source: https://lexical.dev/docs/concepts/node-state Example of the default JSON structure where state is nested under the '$' key. ```json { "type": "poll", "$": { "question": "Are you planning to use NodeState?" } } ``` -------------------------------- ### $getChildCaret Source: https://lexical.dev/docs/api/modules/lexical Gets a caret that points at the first or last child of the given origin node. ```APIDOC ## $getChildCaret(origin, direction) ### Parameters - **origin** (ElementNode) - Required - The origin ElementNode - **direction** (D) - Required - 'next' for first child or 'previous' for last child ### Returns - **ChildCaret** ``` -------------------------------- ### Initialize Lexical Editor Source: https://lexical.dev/docs/getting-started/quick-start Configures the editor instance, registers plugins, and sets up an update listener to sync the editor state to a textarea. ```typescript ; const editorRef = document.getElementById('lexical-editor'); const stateRef = document.getElementById( 'lexical-state', ) as HTMLTextAreaElement; const initialConfig = { namespace: 'Vanilla JS Demo', // Register nodes specific for @lexical/rich-text nodes: [HeadingNode, QuoteNode], onError: (error: Error) => { throw error; }, theme: { // Adding styling to Quote node, see styles.css quote: 'PlaygroundEditorTheme__quote', }, }; const editor = createEditor(initialConfig); editor.setRootElement(editorRef); // Registering Plugins mergeRegister( registerRichText(editor), registerDragonSupport(editor), registerHistory(editor, createEmptyHistoryState(), 300), ); editor.update(prepopulatedRichText, {tag: HISTORY_MERGE_TAG}); editor.registerUpdateListener(({editorState}) => { stateRef!.value = JSON.stringify(editorState.toJSON(), undefined, 2); }); ``` -------------------------------- ### Generate JSON from EditorState Source: https://lexical.dev/docs/serialization Use the `toJSON()` method on an `EditorState` object to get its JSON representation. ```typescript const editorState = editor.getEditorState(); const json = editorState.toJSON(); ``` -------------------------------- ### Initialize editor in jsdom Source: https://lexical.dev/docs/testing Setup a Lexical editor instance within a jsdom environment for unit testing. ```typescript import {buildEditorFromExtensions} from '@lexical/extension'; import {RichTextExtension} from '@lexical/rich-text'; import { $createParagraphNode, $createTextNode, $getRoot, $getSelection, $isRangeSelection, CONTROLLED_TEXT_INSERTION_COMMAND, } from 'lexical'; const editor = buildEditorFromExtensions({ $initialEditorState: () => { $getRoot().append( $createParagraphNode().append($createTextNode('hello')), ); }, dependencies: [RichTextExtension], name: 'test', }); const root = document.createElement('div'); root.contentEditable = 'true'; document.body.appendChild(root); editor.setRootElement(root); ``` -------------------------------- ### $config() Source: https://lexical.dev/docs/api/modules/lexical Implements the static node configuration protocol. This method is called on the prototype and should be a trivial implementation. ```APIDOC ## $config() ### Description Implements the static node configuration protocol. This method is called directly on the prototype and must not depend on anything initialized in the constructor. ### Returns - **BaseStaticNodeConfig** - The configuration object for the node. ``` -------------------------------- ### Lexical HistoryPlugin Setup Source: https://lexical.dev/docs/react/plugins Includes the HistoryPlugin to enable undo and redo functionality within the Lexical editor. ```jsx ``` -------------------------------- ### Vanilla JS App - After Full Migration (Lexical Builder) Source: https://lexical.dev/docs/extensions/migration This snippet shows a fully migrated vanilla JavaScript application using Lexical Builder, where extensions handle node and plugin registration, and the initial state is set via `$initialEditorState`. ```javascript import {buildEditorFromExtensions} from '@lexical/extension'; import {HistoryExtension} from '@lexical/history'; import {RichTextExtension} from '@lexical/rich-text'; import prepopulatedRichText from './prepopulatedRichText'; const editor = buildEditorFromExtensions({ // This works similarly to LexicalComposer editorState $initialEditorState: $prepopulatedRichText, name: '[root]', namespace: 'Vanilla JS Demo (all-in with Lexical Builder)', // RichTextExtension has a nodes property to add QuoteNode and HeadingNode // DragonExtension is a dependency of RichTextExtension // All three extensions have register properties to add behavior to the editor, // with defaults for all of the History configuration dependencies: [RichTextExtension, HistoryExtension], theme: {quote: 'PlaygroundEditorTheme__quote'}, }); editor.setRootElement(editorRef); ``` -------------------------------- ### Project Dependency Lockfile Source: https://lexical.dev/docs/concepts/node-replacement The package-lock.json file defining project dependencies and versions for the node replacement example. ```json { "name": "@lexical/node-replacement-example", "version": "0.48.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@lexical/node-replacement-example", "version": "0.48.0", "dependencies": { "@lexical/react": "0.48.0", "@lexical/utils": "0.48.0", "lexical": "0.48.0", "react": "^19.2.5", "react-dom": "^19.2.5" }, "devDependencies": { "@types/react": "^19.2.14", "@types/react-dom": "^19.1.9", "@vitejs/plugin-react": "^5.0.2", "cross-env": "^7.0.3", "typescript": "^5.9.2", "vite": "^7.3.2" }, "optionalDependencies": { "@rollup/rollup-darwin-arm64": "4.52.0", "@rollup/rollup-linux-x64-gnu": "4.52.0", "@rollup/rollup-win32-x64-msvc": "4.52.0", "@rollup/wasm-node": "4.52.0" } }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", ``` -------------------------------- ### Using ExtensionComponent Source: https://lexical.dev/docs/api/modules/lexical_react_ExtensionComponent Demonstrates how to use the ExtensionComponent to render an extension's output component with custom props. This is the primary way to integrate extensions using this utility. ```jsx return ( ); ``` -------------------------------- ### Legacy Node Serialization Implementation Source: https://lexical.dev/docs/concepts/node-state Example of a legacy node implementation requiring manual serialization methods. ```typescript export type SerializedColoredNode = Spread< {color?: string}, SerializedTextNode >; export class ColoredNode extends TextNode { __color: string; constructor(text: string = '', color: string = DEFAULT_COLOR, key?: NodeKey) { super(text, key); this.__color = color; } static getType(): string { return 'colored'; } static clone(node: ColoredNode): ColoredNode { return new ColoredNode(node.__text, node.__color, node.__key); } static importJSON(serializedNode: SerializedColoredNode) { return new ColoredNode().updateFromJSON(serializedNode); } updateFromJSON(serializedNode: SerializedColoredNode) { const self = super.updateFromJSON(serializedNode); self.__color = typeof serializedNode.color === 'string' ? serializedNode.color : DEFAULT_COLOR; return self; } exportJSON(): SerializedColoredNode { return { ...super.exportJSON(), color: this.__color === DEFAULT_COLOR ? undefined : this.__color, }; } } ``` -------------------------------- ### Initialize LexicalExtensionComposer with useMemo Source: https://lexical.dev/docs/api/modules/lexical_react_LexicalExtensionComposer Use useMemo to create a stable extension when dependencies rely on dynamic props. ```typescript function MyEditor({ emojiBaseUrl, children }) { const extension = useMemo(() => { return defineExtension({ name: "[root]", dependencies: [ RichTextExtension, HistoryExtension, configExtension(EmojiExtension, { emojiBaseUrl }), ], }); }, [emojiBaseUrl]); return ({children}); } ``` -------------------------------- ### Configure LexicalAutoEmbedPlugin for YouTube Source: https://lexical.dev/docs/react/plugins Handles pasted links that match embed configurations, offering to replace them with embedded nodes. This example configures it for YouTube links. Requires LinkNode and AutoLinkNode. ```javascript import {LexicalAutoEmbedPlugin, EmbedConfig, AutoEmbedOption} from '@lexical/react/LexicalAutoEmbedPlugin'; const YouTubeEmbedConfig = { type: 'youtube', parseUrl: (url) => { const match = url.match(/youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)/); return match ? {url, id: match[1]} : null; }, insertNode: (editor, result) => { // Insert your custom embed node here }, }; {/* open modal */}} getMenuOptions={(config, embedFn, dismissFn) => [ new AutoEmbedOption('Embed', {onSelect: embedFn}), new AutoEmbedOption('Dismiss', {onSelect: dismissFn}), ]} /> ``` -------------------------------- ### $getAdjacentChildCaret Source: https://lexical.dev/docs/api/modules/lexical Gets the adjacent caret, returning a ChildCaret if the origin is an ElementNode. Useful for DFS-style tree traversal. ```APIDOC ## $getAdjacentChildCaret(caret) ### Description Gets the adjacent caret, if not-null and if the origin of the adjacent caret is an ElementNode, then return the ChildCaret. This can be used along with the getParentAdjacentCaret method to perform a full DFS style traversal of the tree. ### Parameters - **caret** (NodeCaret | null) - Required - The caret to start at ### Returns - **NodeCaret | null** ``` -------------------------------- ### Lexical PlainTextPlugin Setup Source: https://lexical.dev/docs/react/plugins Configures the PlainTextPlugin for basic text editing, including accessibility attributes and placeholder text. ```jsx Enter some text...} /> } ErrorBoundary={LexicalErrorBoundary} /> ``` -------------------------------- ### Run unit tests Source: https://lexical.dev/docs/testing Execute the unit test suite using the configured test runner. ```bash pnpm test-unit ``` -------------------------------- ### Configure Custom MIME-Type Handler Source: https://lexical.dev/docs/serialization/dom-import Example of registering a custom handler for a specific MIME type with defined priority. ```typescript configExtension(ClipboardImportExtension, { $importMimeType: { 'application/vnd.myapp+json': [ (data, selection, editor) => { const nodes = parseMyAppFormat(data); $insertGeneratedNodes(editor, nodes, selection); return true; }, ], }, // Slot between 'application/x-lexical-editor' (0) and 'text/html' (10). priority: {'application/vnd.myapp+json': 5}, }) ```