WysiMd.Blazor 1.1.0

dotnet add package WysiMd.Blazor --version 1.1.0
                    
NuGet\Install-Package WysiMd.Blazor -Version 1.1.0
                    
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="WysiMd.Blazor" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WysiMd.Blazor" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="WysiMd.Blazor" />
                    
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 WysiMd.Blazor --version 1.1.0
                    
#r "nuget: WysiMd.Blazor, 1.1.0"
                    
#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 WysiMd.Blazor@1.1.0
                    
#: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=WysiMd.Blazor&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=WysiMd.Blazor&version=1.1.0
                    
Install as a Cake Tool

WysiMd.Blazor

CI NuGet NuGet Downloads MIT License .NET 10

What You See Is Markdown — a WYSIWYG Markdown editor Razor component for Blazor. Always produces clean Markdown. Minimal JavaScript. Mobile friendly. Dark & light themes.


Features

  • Visual (WYSIWYG) and Raw modes — switch instantly, content stays in sync
  • 20+ toolbar actions with keyboard shortcuts (Ctrl+B, Ctrl+I, Ctrl+Z, …)
  • Dark / light theming — two-way bindable, fully driven by CSS custom properties
  • GFM table editing — insert/delete rows and columns, auto-sum in Visual mode
  • Image uploads — embedded as base64 data URLs (max 5 MB)
  • Undo / redo — debounced history (1 s), up to 50 snapshots
  • Status bar — word, character, line count and reading time
  • Read-only mode — disables editing; export and theme actions still work
  • Responsive / mobile-first — 48 × 48 px touch targets, bottom-sheet dialogs on narrow screens
  • Configurable toolbar — hide, reorder, or override icons and labels per instance
  • Minimal JS — only cursor/selection tracking and DOM operations; all Markdown logic is C#

Installation

dotnet add package WysiMd.Blazor

Setup

Program.cs

using WysiMd.Blazor;

builder.Services.AddWysiMdBlazor();

App.razor / index.html — add before </body>:

<link rel="stylesheet" href="_content/WysiMd.Blazor/css/WysiMd.Blazor.css" />
<script src="_content/WysiMd.Blazor/WysiMd.Blazor.js"></script>

_Imports.razor (optional convenience):

@using WysiMd.Blazor
@using WysiMd.Blazor.Models

Usage

Basic two-way binding

<MarkdownEditor @bind-Value="content" />

@code {
    private string content = "# Hello, World!";
}

With options and callbacks

<MarkdownEditor @bind-Value="content"
                @bind-FileName="fileName"
                @bind-IsDarkTheme="isDark"
                Options="options"
                OnChange="OnChanged"
                OnPrint="OnPrint"
                OnDownloadPdf="OnDownloadPdf" />

@code {
    private string content = string.Empty;
    private string fileName = "my-document";
    private bool isDark = false;

    private readonly EditorOptions options = new()
    {
        Height            = "500px",
        DefaultMode       = EditorMode.Visual,
        ShowStatusBar     = true,
        SpellCheck        = true,
        MaxLength         = 50_000,
    };

    private void OnChanged(string md) { }
    private void OnPrint(string md) { }
    private void OnDownloadPdf(string md) { }
}

Dark mode

<MarkdownEditor @bind-Value="content" @bind-IsDarkTheme="isDark" />

Read-only preview

<MarkdownEditor Value="@source"
    Options="@(new EditorOptions { ReadOnly = true, ShowToolbar = false })" />

Minimal toolbar

<MarkdownEditor @bind-Value="content"
    Options="@(new EditorOptions
    {
        EnabledToolbarItems = new List<string>
        {
            "bold", "italic", "link", "ul", "ol", "toggle-mode"
        },
        ShowStatusBar = false,
        MinHeight = "160px",
    })" />

Inside an EditForm

<EditForm Model="model" OnValidSubmit="Submit">
    <DataAnnotationsValidator />
    <MarkdownEditor @bind-Value="model.Body"
        Options="@(new EditorOptions { MinHeight = "300px" })" />
    <ValidationMessage For="() => model.Body" />
    <button type="submit">Save</button>
</EditForm>

Editor Modes

Mode Description
EditorMode.Visual Contenteditable WYSIWYG — live inline editing with toolbar formatting
EditorMode.Raw Plain textarea showing raw Markdown source

Users switch modes via the toolbar toggle button. Default is EditorMode.Visual.


Parameters

Parameter Type Description
@bind-Value string Markdown content (two-way)
@bind-FileName string Document filename shown in the overflow panel
@bind-IsDarkTheme bool Dark mode toggle (two-way)
Options EditorOptions Editor configuration object
OnChange EventCallback<string> Fires on every content change
OnPrint EventCallback<string> Fires when the Print button is clicked
OnDownloadPdf EventCallback<string> Fires when the PDF button is clicked

EditorOptions

Property Type Default Description
DefaultMode EditorMode Visual Starting editor mode
ShowToolbar bool true Show or hide the toolbar
ShowStatusBar bool true Show word / char / line count and reading time
ReadOnly bool false Disable editing; export and theme buttons still work
SpellCheck bool true Browser spell-check
Placeholder string "Start writing..." Placeholder in Raw mode
MinHeight string "400px" CSS min-height
Height string? null Fixed height (overrides MinHeight)
IsDarkTheme bool false Dark mode
Background string? null Custom background color (light mode)
DarkBackground string? null Custom background color (dark mode)
MaxLength int? null Maximum character count
AllowFileNameEditing bool true Show filename input in overflow panel
EnabledToolbarItems List<string> all items Ordered list of toolbar item IDs to display
OverflowItems List<string> secondary items Item IDs shown in the ··· overflow dropdown
ToolbarItemOverrides Dictionary<string, ToolbarItemOptions> {} Per-item overrides: Hidden, Icon, Tooltip, CssClass
DebounceDelay int 500 Ms of inactivity before ValueChanged fires. Set to 0 for per-keystroke (Blazor WASM default behaviour). Recommended for Blazor Server.

Toolbar Items

ID Action Shortcut
undo Undo Ctrl+Z
redo Redo Ctrl+Y
bold Bold Ctrl+B
italic Italic Ctrl+I
strikethrough Strikethrough Ctrl+Shift+X
code Inline code Ctrl+`
heading Heading dropdown (H1–H6)
ul Unordered list
ol Ordered list
task Task list
blockquote Blockquote Ctrl+Shift+B
hr Horizontal rule
link Insert link dialog Ctrl+L
image Upload image (base64) Ctrl+K
table Insert table dialog
code-block Fenced code block
insert-row Insert table row (Visual only)
delete-row Delete table row (Visual only)
insert-col Insert table column (Visual only)
delete-col Delete table column (Visual only)
auto-sum Sum column numbers (Visual only)
download Download .md file Ctrl+S
print Print / export Ctrl+P
pdf Download PDF (callback)
toggle-mode Switch Visual ↔ Raw
toggle-theme Switch dark ↔ light
overflow Overflow panel

Hiding or overriding items

<MarkdownEditor @bind-Value="content"
    Options="@(new EditorOptions
    {
        ToolbarItemOverrides = new Dictionary<string, ToolbarItemOptions>
        {
            ["pdf"]   = new ToolbarItemOptions { Hidden = true },
            ["bold"]  = new ToolbarItemOptions { Tooltip = "Make it bold" },
        }
    })" />

MudBlazor

WysiMd.Blazor works with MudBlazor without any adapter package. Drop <MarkdownEditor> inside MudCard, MudDialog, or EditForm exactly as you would any other input component.

<MudCard>
    <MudCardContent Class="pa-0">
        <MarkdownEditor @bind-Value="content"
            Options="@(new EditorOptions { MinHeight = "300px" })" />
    </MudCardContent>
</MudCard>

Sync dark mode by forwarding MudThemeProvider's state to @bind-IsDarkTheme, and match your palette via CSS custom properties on .wysimd-editor. See docs/mudblazor.md for full examples (dialogs, forms, theme syncing, border removal).


Theming

Dark mode

<MarkdownEditor @bind-Value="content" @bind-IsDarkTheme="isDark" />

CSS custom properties

Override any --wysimd-* variable on a parent element:

.my-wrapper {
    --wysimd-accent:     #7c3aed;   /* brand color */
    --wysimd-font:       'Georgia', serif;
    --wysimd-radius:     2px;
    --wysimd-bg-toolbar: #faf5ff;
}

Key variables (full list in docs/theming.md):

Variable Light default Dark default
--wysimd-bg #ffffff #1e1e2e
--wysimd-bg-toolbar #f8f9fa #181825
--wysimd-text #212529 #cdd6f4
--wysimd-accent #0d6efd #89b4fa
--wysimd-border #dee2e6 #313244
--wysimd-code-bg #f1f3f4 #181825

Mobile Support

WysiMd.Blazor is designed mobile-first:

  • 48 × 48 px touch targets on screens ≤ 640 px (exceeds WCAG 2.5.5)
  • Toolbar scrolls horizontally — no buttons hidden at small widths
  • Link and Table dialogs render as full-width bottom sheets on mobile
  • Viewport-relative height for full-screen mobile editing:
<MarkdownEditor @bind-Value="content"
    Options="@(new EditorOptions { Height = "calc(100dvh - 120px)" })" />

See docs/mobile.md for the full testing checklist.


MarkdownService C# API

Inject MarkdownService for standalone use:

// Registration (done by AddWysiMdBlazor)
builder.Services.AddSingleton<MarkdownService>();
// Rendering
string html = svc.ToHtml("# Hello");

// Inline formatting — returns (newMarkdown, newStart, newEnd)
var (md, s, e) = svc.ToggleBold(markdown, selStart, selEnd);
var (md, s, e) = svc.ToggleItalic(markdown, selStart, selEnd);
var (md, s, e) = svc.ToggleStrikethrough(markdown, selStart, selEnd);
var (md, s, e) = svc.ToggleInlineCode(markdown, selStart, selEnd);

// Block formatting
var (md, cursor) = svc.SetHeading(markdown, cursorPos, level: 2);
var (md, s, e)   = svc.ToggleUnorderedList(markdown, selStart, selEnd);
var (md, s, e)   = svc.ToggleOrderedList(markdown, selStart, selEnd);
var (md, s, e)   = svc.ToggleTaskList(markdown, selStart, selEnd);
var (md, s, e)   = svc.ToggleBlockquote(markdown, selStart, selEnd);
var (md, cursor) = svc.InsertHorizontalRule(markdown, cursorPos);
var (md, s, e)   = svc.InsertLink(markdown, selStart, selEnd, url, text);
var (md, cursor) = svc.InsertCodeBlock(markdown, selStart, selEnd, "csharp");

// Table generation
string table = svc.GenerateTable(rows: 3, cols: 4);
string table = svc.GenerateTable(rows: 3, cols: 2, data: new[] {
    new[] { "Name", "Score" },
    new[] { "Alice", "95" },
});

// Statistics
EditorStats stats = svc.GetStats(markdown);
// stats.WordCount / CharCount / LineCount / ReadingTimeDisplay

Repository Structure

WysiMd.Blazor/
├── src/
│   └── WysiMd.Blazor/             # Library — ships to NuGet
│       ├── WysiMd.Blazor.csproj
│       ├── Models/                # EditorOptions, MarkdownDocument, EditorStats
│       ├── Services/              # MarkdownService, DI registration
│       ├── Components/            # MarkdownEditor, EditorToolbar, LinkDialog, TableDialog
│       └── wwwroot/               # WysiMd.Blazor.js + WysiMd.Blazor.css
│
├── tests/
│   ├── WysiMd.Blazor.UnitTests/   # MSTest + bUnit
│   ├── WysiMd.Blazor.IntegrationTests/ # MSTest + Playwright for .NET
│   └── WysiMd.Blazor.JsTests/    # Vitest (jsdom) — vanilla JS functions
│
├── samples/
│   ├── WysiMd.Blazor.Sample/      # Blazor WASM standalone — 7 demo pages
│
└── docs/                          # Markdown documentation
    ├── getting-started.md
    ├── configuration.md
    ├── toolbar-customization.md
    ├── theming.md
    ├── mudblazor.md
    ├── mobile.md
    └── api-reference.md

Running the Samples

dotnet run --project samples/WysiMd.Blazor.Sample

Running Tests

# Unit tests
dotnet test tests/WysiMd.Blazor.UnitTests

# JS tests (requires Node 18+)
cd tests/WysiMd.Blazor.JsTests
npm ci && npm test

# Integration tests (start the sample first)
dotnet run --project samples/WysiMd.Blazor.Sample --urls http://localhost:5100 &
dotnet test tests/WysiMd.Blazor.IntegrationTests

Dependencies

Package Version Purpose
Markdig 1.1.2 Markdown → HTML (CommonMark + GFM)
Microsoft.AspNetCore.Components.Web 10.0.5 Blazor

No JavaScript dependencies — all JS is vanilla.


Documentation

Topic File
Getting Started docs/getting-started.md
Configuration reference docs/configuration.md
Toolbar customization docs/toolbar-customization.md
Theming & CSS variables docs/theming.md
MudBlazor integration docs/mudblazor.md
Mobile support docs/mobile.md
C# API reference docs/api-reference.md
Roadmap ROADMAP.md
Changelog CHANGELOG.md
Contributing CONTRIBUTING.md

Contributing

Contributions are welcome! Please read CONTRIBUTING.md before opening a PR.

  • Check ROADMAP.md for planned features — open an issue to claim one
  • Branch off dev, use conventional commits (feat:, fix:, docs:, test:)
  • All new MarkdownService methods need unit tests
  • UI changes must be tested at 375 px viewport width
  • Update CHANGELOG.md in your PR

License

MIT © 2025–2026 Autorior

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
1.1.0 115 5/15/2026
1.0.3 108 5/11/2026
1.0.2 98 5/4/2026
1.0.1 93 5/4/2026
1.0.0 94 5/4/2026