mrkeyoor.com_
Sat 08 Aug 21:01 UTC
npmWeb Frontendupdated 08 Aug 2026

@lexical/markdown

@lexical/markdown is the Markdown bridge for Meta's Lexical editor framework. It converts Markdown strings into Lexical nodes, serializes an editor or selection back to Markdown, and turns typed Markdown markers into rich-text formatting. You choose an ordered list of transformers, so the package can support the built-in headings, quotes, lists, fenced code, links, and text styles or an application-specific subset. It is an editor integration layer, not a general Markdown parser or renderer.

Verdict

Use it when Lexical is already the editor and Markdown is a required input, output, or typing syntax. Do not install it as a general Markdown parser, and test your chosen transformer set for round-trip losses before making Markdown the source of truth.

API stability3/5The package exposes a compact set of conversion helpers, named built-in transformers, and one shortcut registrar, but it is still on 0.x releases. Version 0.49.0 pins every Lexical dependency to the identical version, and current source includes newer details such as selection-only export, generated-node import, Enter-triggered transformers, and highlight syntax. Those changes are useful, but they make casual mixed-version upgrades unsafe.
Docs4/5The package README shows whole-document import, export, React initialization, React shortcuts, manual shortcuts, and the transformer groups. The generated API page documents every exported type and function. The weak spot is setup context: the README does not show the complete node registration needed by TRANSFORMERS, even though the shortcut source explicitly throws when a required node class is absent.
Maintenance5/5Version 0.49.0 was published on July 30, 2026, the facebook/lexical repository was pushed on August 8, 2026, and the package is developed in the main Lexical monorepo rather than a detached adapter. The repository currently reports 370 open issues and pull requests, a sizable queue, but active commits and coordinated package releases show that this module is under continuing development.
Ecosystem4/5The package recorded 4,341,865 downloads for July 31 through August 6, 2026, while the parent repository has 23,745 stars. It fits directly into Lexical's React plugin and node packages and can be used without React through `registerMarkdownShortcuts`. Its reach is strong inside Lexical, but its transformer model is framework-specific and does not participate directly in the larger unified Markdown syntax-tree ecosystem.

Use it if

  • Your application already uses Lexical and needs to load or save editor content as Markdown
  • You want React or framework-free typing shortcuts such as turning '# ' into a heading and '**text**' into bold text
  • You need control over the exact Markdown features an editor accepts through an explicit transformer list
  • You want to export only the current Lexical selection instead of serializing the whole document
Skip it if

Setup reality

Installation is not just `npm install @lexical/markdown`. Version 0.49.0 brings `lexical` plus eight @lexical runtime packages at the exact same version, and TypeScript 5.2 or newer is a peer requirement. In a real editor, every node class required by your chosen transformers must also appear in the Lexical editor configuration. Headings and quotes need the rich-text nodes, lists and check lists need `ListNode` and `ListItemNode`, links need `LinkNode`, and fenced code needs `CodeNode`. The shortcut registrar checks these dependencies and throws when a node is missing, so a transformer list copied from an example can fail at editor startup. React users install and render `MarkdownShortcutPlugin`; non-React users call `registerMarkdownShortcuts` and must run the returned cleanup function. Import and export helpers begin with `$`, which means they must execute inside `editor.update()`, an editor-state read callback, or the `editorState` initializer supplied to `LexicalComposer`. Import clears the target root by default and moves an existing selection to the start. The default transformer bundle is opinionated: it includes `==highlight==`, which is not standard Markdown, but excludes the separately exported `CHECK_LIST`. It also does not cover tables or images. Decide on one transformer array, register all of its node dependencies, and use that same array for import, shortcuts, and export or users can create formatting that does not survive saving.

Patterns

Install the Markdown package with Lexicalinstall-matching-version

npm install lexical@0.49.0 @lexical/markdown@0.49.0

Keep all Lexical packages on the same release; @lexical/markdown 0.49.0 uses exact 0.49.0 dependencies.

Replace editor content with Markdownimport-markdown

import {
  $convertFromMarkdownString,
  TRANSFORMERS,
} from '@lexical/markdown';

editor.update(() => {
  $convertFromMarkdownString(markdown, TRANSFORMERS);
});

The helper clears the target root before importing and must run in a Lexical update or initialization context.

Serialize the editor to Markdownexport-markdown

import {
  $convertToMarkdownString,
  TRANSFORMERS,
} from '@lexical/markdown';

const markdown = editor.getEditorState().read(() =>
  $convertToMarkdownString(TRANSFORMERS),
);

Use the same transformer array for import and export; otherwise some imported node types may serialize as plain text or disappear.

Initialize LexicalComposer from Markdowninitialize-react-editor

import {LexicalComposer} from '@lexical/react/LexicalComposer';
import {$convertFromMarkdownString, TRANSFORMERS} from '@lexical/markdown';

const initialConfig = {
  namespace: 'ArticleEditor',
  nodes: [HeadingNode, QuoteNode, ListNode, ListItemNode, LinkNode, CodeNode],
  onError(error) { throw error; },
  editorState: () => $convertFromMarkdownString(markdown, TRANSFORMERS),
};

<LexicalComposer initialConfig={initialConfig}>
  <RichTextPlugin contentEditable={<ContentEditable />} ErrorBoundary={LexicalErrorBoundary} />
</LexicalComposer>

Import runs during editor creation; register every node class required by the transformer array before it runs.

Enable Markdown typing shortcuts in Reactenable-react-shortcuts

import {TRANSFORMERS} from '@lexical/markdown';
import {MarkdownShortcutPlugin} from '@lexical/react/LexicalMarkdownShortcutPlugin';

<MarkdownShortcutPlugin transformers={TRANSFORMERS} />

This changes typed markers into rich nodes; it does not make the editor store Markdown internally.

Register shortcuts without Reactenable-vanilla-shortcuts

import {
  registerMarkdownShortcuts,
  TRANSFORMERS,
} from '@lexical/markdown';

const unregister = registerMarkdownShortcuts(editor, TRANSFORMERS);

// When the editor integration is disposed:
unregister();

Keep and call the returned cleanup function or update listeners and command handlers remain registered.

Allow only a curated Markdown subsetchoose-transformer-subset

import {
  BOLD_STAR,
  HEADING,
  ITALIC_STAR,
  LINK,
  QUOTE,
} from '@lexical/markdown';

const ARTICLE_TRANSFORMERS = [
  HEADING,
  QUOTE,
  BOLD_STAR,
  ITALIC_STAR,
  LINK,
];

Transformer order matters, especially for overlapping text markers; preserve the built-in ordering when selecting related formats.

Add task-list Markdown explicitlysupport-check-lists

import {CHECK_LIST, TRANSFORMERS} from '@lexical/markdown';
import {ListItemNode, ListNode} from '@lexical/list';

const nodes = [ListNode, ListItemNode];
const transformers = [CHECK_LIST, ...TRANSFORMERS];

CHECK_LIST is exported separately but is not included in TRANSFORMERS; ListNode and ListItemNode must be registered.

Preserve source newlines during importpreserve-line-breaks

editor.update(() => {
  $convertFromMarkdownString(
    markdown,
    TRANSFORMERS,
    undefined,
    true,
  );
});

The fourth argument preserves newlines; it disables adjacent-line merging and can produce different paragraph structure from normalized CommonMark.

Merge adjacent non-empty lines on importmerge-adjacent-lines

editor.update(() => {
  $convertFromMarkdownString(
    markdown,
    TRANSFORMERS,
    undefined,
    false,
    true,
  );
});

The fifth argument applies CommonMark-style line merging only when newline preservation is false.

Parse Markdown nodes without clearing the documentinsert-markdown-at-selection

import {$getSelection} from 'lexical';
import {$generateNodesFromMarkdownString, TRANSFORMERS} from '@lexical/markdown';

editor.update(() => {
  const selection = $getSelection();
  const nodes = $generateNodesFromMarkdownString(markdown, TRANSFORMERS);
  selection?.insertNodes(nodes);
});

$generateNodesFromMarkdownString returns detached nodes and leaves the document untouched until you insert them.

Export only selected contentexport-selection

import {$getSelection} from 'lexical';
import {
  $convertSelectionToMarkdownString,
  TRANSFORMERS,
} from '@lexical/markdown';

const selectedMarkdown = editor.getEditorState().read(() =>
  $convertSelectionToMarkdownString(TRANSFORMERS, $getSelection()),
);

A missing selection or collapsed range returns an empty string; it does not fall back to the whole document.

Alternatives

PackageRegistryPick it when
markdown-itnpmYou need a standalone, extensible Markdown-to-HTML parser and are not using Lexical
markednpmYou want a direct Markdown lexer and renderer with a small API outside an editor framework
remark-parsenpmYou need a Markdown syntax tree that can pass through the unified plugin ecosystem
@lexical/htmlnpmYour Lexical persistence or interoperability format is HTML rather than Markdown