Documentation

Everything the component exposes: parameters, methods, toolbars, modules and the Blazor Server specifics.

Installation

MudExRichTextEditor targets .NET 8, 9 and 10 and works in Blazor WebAssembly and Blazor Server. MudBlazor is pulled in transitively, but the component also renders in projects that do not use MudBlazor themselves.

dotnet add package MudExRichTextEditor

Then register the using once, in _Imports.razor:

@using MudExRichTextEditor

Everything else — Quill, the stylesheets and every bundled module — is loaded on demand from _content/MudExRichTextEditor. No CDN, no extra script tag in your host page.

Quick start

@page "/"

<MudExRichTextEdit @bind-Value="_html" Height="320" />

@code {
    private string _html = "<p>Hello <b>MudBlazor</b>!</p>";
}

The component derives from the MudBlazor.Extensions form base, so @bind-Value, Required, Label, Validation and Disabled behave the way they do on a MudTextField. An empty document reports no value, so Required fires again after the user deletes all text.

Presets

QuillPresets ships three ready-made combinations of toolbar and modules.

  • Minimal — bold, italic, underline, strike. No modules.
  • Standard — headings, lists, links and images, plus table, image-resize and compression modules.
  • Full — everything above plus colors, indentation, alignment, blockquote, code block, video and the table button.
@using MudExRichTextEditor.Types

<MudExRichTextEdit @bind-Value="_html"
                   Tools="@QuillPresets.Standard.Tools"
                   Modules="@QuillPresets.Standard.Modules"
                   Height="360"
                   Immediate="true" />

Parameters

The most relevant parameters. Everything inherited from the MudBlazor form base (Label, Required, Disabled, Class, Style, Validation, Culture) is available too.

ParameterTypeDescription
ValuestringSemanticHtml of the document
ValueHtmlBehaviorGetHtmlBehaviorWhich representation Value carries: SemanticHtml (default), InnerHtml, Text or Content (Delta JSON).
ImmediateboolRaise ValueChanged on every keystroke instead of on blur.
ToolsQuillTool[]Toolbar contents. Defaults to QuillTool.All() plus the file and microphone buttons.
ModulesIQuillModule[]Quill modules to load.
ReadOnlyboolLocks the document; the toolbar hides unless HideToolbarWhenReadOnly is false.
HideToolbarWhenReadOnlyboolDefault true, applies to the Snow theme.
HeightMudExSize<double>?Editor height; without it the editor grows with its content.
EnableResizeboolLets the user drag the bottom edge to resize.
PlaceholderstringPlaceholder text, localized through the MudBlazor.Extensions localizer.
ThemeQuillThemeSnow (toolbar above) or Bubble (floating toolbar).
DebugLevelQuillDebugLevelQuill console verbosity.
BackgroundColorMudExColor?Editor surface color.
ToolBarBackgroundColorMudExColor?Toolbar background color.
BorderColorMudExColor?Border color for editor and toolbar.
CustomUploadFuncFunc<UploadableFile, Task<string>>Called for pasted, dropped and attached files; return the URL to embed.
Files / OnGetFilesFuncIList<UploadableFile>Pre-fills the attachment dialog with files you already have.
ToolbarContentRenderFragmentReplaces the generated toolbar completely.
EditorContentRenderFragmentSeeds the editor with markup instead of Value.
DefaultToolHandlersDefaultToolHandler[]Overrides a built-in Quill handler (for example image) with your own callback.
UseCultureForSpeechRecognitionboolPasses the component culture to the speech recognizer.

Methods

Grab a reference with @ref to call these.

MethodDescription
GetHtml()The raw innerHTML Quill produced.
GetSemanticHTML()Cleaned-up semantic HTML — this is what Value holds by default.
GetText()Plain text without markup.
GetContent()The Quill Delta as a JSON string.
GetValue(GetHtmlBehavior)Any of the above, chosen at runtime.
SetHtml(string)Replaces the document; waits for initialization first.
InsertHtmlAsync(string)Pastes markup at the caret.
InsertImage(string url)Embeds an image at the caret.
InsertTableAsync(rows, columns)Inserts an empty table.
AttachFilesAsync()Opens the attachment dialog.
EnableEditor(bool)Enables or disables editing without re-rendering.
LoadContent(string delta)Loads a Quill Delta JSON document.
GetModule<T>()Returns a loaded module instance.

Toolbar & tools

Without a Tools value the editor renders QuillTool.All() plus a file-attachment and a microphone button. Pass your own array to control both contents and order — tools are grouped by their group number, and each group becomes one separator-delimited block.

@using MudExRichTextEditor.Types

<MudExRichTextEdit @bind-Value="_html" Tools="@_tools" />

@code {
    private QuillTool[] _tools = [
        QuillTools.Header(group: 1),
        QuillTools.Font(group: 1),                 // font family dropdown
        QuillTools.Size(group: 1),                 // font size dropdown
        QuillTools.Bold(group: 2),
        QuillTools.Italic(group: 2),
        QuillTools.Link(group: 3),
        QuillTools.Image(group: 3),
    ];
}

QuillTools is the factory for every built-in button: Bold, Italic, Underline, Strike, Header, Font, Size, Color, Background, OrderedList, BulletList, IndentDecrease, IndentIncrease, Align, Blockquote, CodeBlock, Link, Image, Video and TableButton.

Fonts and sizes: Quill only renders font names it knows. The defaults are serif and monospace next to the theme font; to offer more, pass your own list to QuillTools.Font(fonts: [...]) and add matching .ql-font-<name> CSS rules to your app stylesheet.

To replace the toolbar markup entirely, use the ToolbarContent render fragment instead.

Custom toolbar buttons

CustomTool renders a MudBlazor icon button and hands you the editor instance on click. Icon, tooltip and color can be static or computed per render, which is how the microphone button switches between “record” and “stop”.

@using MudExRichTextEditor.Types

<MudExRichTextEdit @ref="_editor" Tools="@_tools" />

@code {
    private MudExRichTextEdit _editor;

    private QuillTool[] _tools = QuillTool.All()
        .Append(new CustomTool(
            onClick: (_, editor) => editor.InsertHtmlAsync("<hr />"),
            icon: Icons.Material.Filled.HorizontalRule,
            tooltip: "Insert divider",
            group: 9))
        .ToArray();
}

To override a built-in Quill handler rather than adding a button, use DefaultToolHandlers with the Quill format name, for example new DefaultToolHandler("image", (editor, args) => ...).

Modules

@using MudExRichTextEditor.Extensibility

<MudExRichTextEdit @bind-Value="_html" Modules="@_modules" />

@code {
    private IQuillModule[] _modules = [
        new QuillBlotFormatterModule(),   // resize images by clicking them
        new QuillImageCompressorModule(), // shrink pasted/dropped images
        new QuillTableBetterModule(),     // tables (needs QuillTools.TableButton())
    ];
}
  • QuillBlotFormatterModule — resize handles on images and embeds. Part of the Standard and Full presets.
  • QuillImageCompressorModule — downscales images on paste and drop before they are stored.
  • QuillTableBetterModule — tables with a context menu. Add QuillTools.TableButton() to the toolbar to get the insert button.
  • QuillBetterTableModule — the older table implementation, with MudBlazor palette colors for cell backgrounds.
  • QuillMentionModule<T> — see below.

Write your own by deriving from QuillModule: declare JsFiles and CssFiles, and return your Quill configuration from OnModuleLoadedAsync.

Mentions

QuillMentionModule<T> is generic over your own item type and takes an async lookup that receives the trigger character and the current search term.

@using MudExRichTextEditor.Extensibility

<MudExRichTextEdit @bind-Value="_html" Modules="@_modules" />

@code {
    private IQuillModule[] _modules;

    protected override void OnInitialized() => _modules = [
        new QuillMentionModule<string>(
            (denotationChar, search) => Task.FromResult(
                new[] { "Alice", "Bob", "Carol" }.Where(n => n.Contains(search, StringComparison.OrdinalIgnoreCase))),
            '@', '#')
    ];
}

The module also exposes MentionClicked, MentionHovered, BeforeMentionSelect and AfterMentionSelect callbacks.

The suggestion list is positioned with quill-mention's fixed strategy, so it lands at the caret even inside a MudDialog or any scrolled or transformed parent.

Files & uploads

The attachment button opens a MudBlazor upload dialog; paste and drag & drop are handled as well. Without CustomUploadFunc every file becomes a data URL inside the document, which is convenient for demos and expensive for real content — point it at your storage instead.

<MudExRichTextEdit @bind-Value="_html" CustomUploadFunc="@UploadAsync" />

@code {
    // Return the URL the editor should embed. Without this func the file is
    // inlined as a data URL, which bloats the stored HTML.
    private async Task<string> UploadAsync(UploadableFile file)
    {
        await file.EnsureDataLoadedAsync();
        var url = await _myStorage.SaveAsync(file.FileName, file.Data, file.ContentType);
        return url;
    }
}

Use Files or OnGetFilesFunc to pre-populate the dialog, and OnBeforeDialogOpen / OnDialogClosed to react around it.

Value & HTML behavior

ValueHtmlBehavior decides what the binding writes back.

@using MudExRichTextEditor.Types

<MudExRichTextEdit @bind-Value="_html"
                   ValueHtmlBehavior="GetHtmlBehavior.SemanticHtml" />

@code {
    // Read a different representation on demand, independent of the binding:
    private async Task ReadAll()
    {
        var semantic = await _editor.GetSemanticHTML();
        var inner    = await _editor.GetHtml();
        var text     = await _editor.GetText();
        var delta    = await _editor.GetContent(); // Quill Delta as JSON
    }
}
  • SemanticHtml (default) — Quill's cleaned output, best for storing and re-rendering elsewhere.
  • InnerHtml — the editor DOM verbatim, including Quill's own classes.
  • Text — plain text.
  • Content — the Delta document as JSON; load it back with LoadContent.

Quill keeps an empty document as <p><br></p>. The component treats markup without text or embeds as empty, so validation and ValueChanged behave correctly when the user clears the field.

Incoming markup runs through Quill's own converter, which is what keeps <pre>, <ul>/<li> and nested inline formatting intact. Anything Quill has no blot for is still dropped — register a custom blot if you need it.

Themes & appearance

@using MudExRichTextEditor.Types

<MudExRichTextEdit @bind-Value="_html"
                   Theme="QuillTheme.Bubble"
                   BackgroundColor="MudExColor.Surface"
                   ToolBarBackgroundColor="MudExColor.Primary"
                   BorderColor="MudExColor.Secondary"
                   EnableResize="true"
                   Height="300" />

Colors accept the full MudExColor range: palette entries, CSS variables and literal values. Read-only mode hides the toolbar by default:

<MudExRichTextEdit Value="@_html" ReadOnly="true" HideToolbarWhenReadOnly="true" />

Blazor Server

The editor needs an interactive render mode; under a static or prerendered-only mode the container renders but Quill never initializes. Make sure the MudBlazor providers and blazor.web.js are present:

<!-- App.razor / _Host.cshtml -->
<head>
    ...
    <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
</head>
<body>
    <MudDialogProvider @rendermode="InteractiveServer" />
    <MudSnackbarProvider @rendermode="InteractiveServer" />
    <MudPopoverProvider @rendermode="InteractiveServer" />
    <Routes @rendermode="InteractiveServer" />
    <script src="_framework/blazor.web.js"></script>
    <script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>

If the initial value shows up late, check that the component really is interactive (@rendermode InteractiveServer) — SetHtml waits for initialization before writing.

Self-hosting & offline

All assets ship inside the package and are served from _content/MudExRichTextEditor: Quill itself, the themes, the mention module, the image compressor, the blot formatter and — since 9.5.1 — the table module. Nothing is fetched from a CDN, so the component works behind a proxy, in an air-gapped network, and under a strict Content-Security-Policy.

FAQ

Chrome warns about preloaded CSS from AuralizeBlazor or Nextended.Blazor

That warning comes from the .NET SDK, not from this component. When your app references any Razor class library with scoped CSS, the SDK adds a Link: <…bundle.scp.css>; rel="preload" header to your own {App}.styles.css. Chrome logs a warning whenever such a preload is not consumed quickly enough. It is cosmetic — nothing is broken and nothing is loaded twice.

Can I use it without MudBlazor?

Yes. The package brings its own MudBlazor dependency; you do not have to build the rest of your app with it.

How do I get true HTML instead of Quill markup?

Use the default GetHtmlBehavior.SemanticHtml, or call GetSemanticHTML().

Why does my markup come back changed?

Markup you assign to Value is parsed by Quill, so it is normalized to what Quill can actually represent: <b> becomes <strong>, <pre> becomes a code block, list items get Quill's own attributes. The semantic HTML you read back is equivalent, not byte-identical.

Drag & drop and paste

The component handles both itself and inserts the file exactly once, at the drop position. Do not add your own drop or paste handler on the editor root for files — that is what causes duplicate images.