Blazor.Lexical 0.48.0-preview.7

This is a prerelease version of Blazor.Lexical.
dotnet add package Blazor.Lexical --version 0.48.0-preview.7
                    
NuGet\Install-Package Blazor.Lexical -Version 0.48.0-preview.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Blazor.Lexical" Version="0.48.0-preview.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Blazor.Lexical" Version="0.48.0-preview.7" />
                    
Directory.Packages.props
<PackageReference Include="Blazor.Lexical" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Blazor.Lexical --version 0.48.0-preview.7
                    
#r "nuget: Blazor.Lexical, 0.48.0-preview.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Blazor.Lexical@0.48.0-preview.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Blazor.Lexical&version=0.48.0-preview.7&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Blazor.Lexical&version=0.48.0-preview.7&prerelease
                    
Install as a Cake Tool

Blazor.Lexical

A Blazor Razor Class Library that wraps the Lexical rich-text editor behind a single <LexicalEditor> component. It ships the JS glue as a self-contained RCL asset, so there is no separate npm step for consumers.

Works with both Blazor Server (InteractiveServer) and Blazor WebAssembly.

Features

  • Rich text editing with history (undo/redo)
  • Bulleted / numbered lists and links
  • Get/set content as plain text, HTML, Markdown, or canonical editor-state JSON
  • Preload a document with InitialContent — no @ref, no empty first paint
  • Debounced OnContentChanged callback, in the format you ask for (or a dirty signal)
  • Read-only mode, placeholder text, and per-instance or app-wide themes
  • Tables, mentions/hashtags, a live table of contents, marks (app-owned highlight ids), document statistics, and a per-block hover gutter you can put your own buttons in — each an opt-in child component that costs nothing when you don't declare it

Install

dotnet add package Blazor.Lexical

Usage

Register the services in Program.cs:

using Blazor.Lexical;

builder.Services.AddLexicalBlazor();

Reference the stylesheet from your host page (App.razor / index.html):

<link rel="stylesheet" href="_content/Blazor.Lexical/blazor-lexical.css" />

Then use the component:

@using Blazor.Lexical

<LexicalEditor @ref="_editor"
               Placeholder="Start typing…" />

@code {
    private LexicalEditor _editor = default!;

    private async Task SaveAsync()
    {
        var json = await _editor.GetEditorStateJsonAsync();
        // persist json…
    }
}

Loading initial content

Pass the document — and the format it is written in — as InitialContent. It is applied while the editor is created, so it is on screen in the first frame and undo can't erase it:

<LexicalEditor InitialContent="@LexicalContent.FromHtml(_doc.Body)"
               OnContentChanged="OnChanged" />

LexicalContent.FromText / FromHtml / FromMarkdown / FromEditorStateJson all build one, so content loaded from a database or an API can choose its format at runtime. It is read once, at creation; call SetHtmlAsync (or a sibling Set*Async) to replace the content later.

With InitialContent in for content, OnContentChanged for live changes, and LastContentText for cheap reads, a typical editor needs no @ref at all.

Why there is no @bind-Value

The editor is a document, not a value input: its source of truth is Lexical's editor state, and a C# string is only a projection of it in one of four formats. Two-way binding would have to pick one, re-parse the entire document on every external set (losing selection, IME composition, and undo history), and stream the document across the JS boundary continuously. Instead the two halves are separate and each is opt-in: InitialContent in, OnContentChanged out, Get*Async/Set*Async on demand.

Parameters

Parameter Type Description
Placeholder string? Text shown while the editor is empty.
ReadOnly bool Renders the editor read-only.
CssClass string? Extra CSS class(es) on the outer wrapper.
Theme object? Lexical EditorThemeClasses for this instance.
EnableHistory bool Enables undo/redo (default true).
Id string Stable element id; auto-generated when omitted.
InitialContent LexicalContent? Document to load as the editor is created.
OnContentChanged EventCallback<LexicalContent> Fires (debounced) with the document, in the format ContentPayload names.
ContentPayload LexicalContentPayload Text (default), Html, Markdown, EditorStateJson, or SignalOnly.

Watching for changes

The push channel carries the document in the format you declare, so a handler that wants HTML doesn't get plain text and then have to ask again:

<LexicalEditor ContentPayload="LexicalContentPayload.EditorStateJson"
               OnContentChanged="OnChanged" />

@code {
    private async Task OnChanged(LexicalContent content)
    {
        // content.Format is EditorStateJson; content.Text is ready to persist.
        await _store.SaveAsync(content.Text);
    }
}

The document is serialized in JS inside the debounce, so this stays one interop crossing per change. If you only need to know that something changed — an autosave timer, an enabled Save button — use LexicalContentPayload.SignalOnly and nothing but the signal crosses.

Properties

IsReady (true once the underlying editor exists — every method no-ops before that) and LastContent (the most recent pushed document, with no interop call).

Content methods

GetTextAsync / SetTextAsync, GetHtmlAsync / SetHtmlAsync, GetMarkdownAsync / SetMarkdownAsync, GetEditorStateJsonAsync / SetEditorStateJsonAsync, plus InsertUnorderedListAsync, InsertOrderedListAsync, RemoveListAsync, SetLinkAsync, and RemoveLinkAsync.

When the format is a value rather than something known at the call site, use GetContentAsync(LexicalContentFormat) / SetContentAsync(LexicalContent) — the same LexicalContent that InitialContent takes, so a document round-trips without a switch.

Persist GetEditorStateJsonAsync() — it is the highest-fidelity format.

Every Set*Async takes an optional silent: true for content the app supplied rather than content the user typed (a remote revision, a server refresh): the apply adds no undo step and raises no OnContentChanged.

License

MIT


This package bundles Lexical 0.48.0 (facebook/lexical). The same version is exposed at runtime as LexicalPackage.LexicalVersion and in the assembly metadata key LexicalVersion.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.48.0-preview.7 143 7/28/2026
0.48.0-preview.6 66 7/24/2026
0.48.0-preview.5 67 7/23/2026
0.48.0-preview.4 70 7/22/2026
0.48.0-preview.3 61 7/22/2026
0.48.0-preview.2 67 7/21/2026
0.48.0-preview.1 64 7/21/2026

Bundles Lexical 0.48.0.