WYSIWYG editing Image gallery upload Content templates
Comprehensive reference

Developer Documentation

Complete reference for the RichTextBox ASP.NET Core WYSIWYG editor — from installation and Tag Helper attributes to the JavaScript API, events, uploads, and deployment.

Quick start

Getting Started

Add a production-ready WYSIWYG HTML editor to any ASP.NET Core Razor Pages or MVC application in three steps: install the NuGet package, register the services, and drop in the tag helper.

1. Install the package

dotnet add package RichTextBox

2. Configure Program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRichTextBox();
var app = builder.Build();
app.UseStaticFiles();
app.MapRichTextBoxUploads();
app.MapRazorPages();
app.Run();

3. Place the editor

<richtextbox name="Body" toolbar="default" height="400px" />

That is the minimal setup. The sections below cover every option, attribute, and API method in detail.

Setup

Installation

NuGet Package

Install via the .NET CLI:

dotnet add package RichTextBox

Or via the Visual Studio Package Manager Console:

Install-Package RichTextBox

Or add directly to your .csproj:

<PackageReference Include="RichTextBox" Version="1.0.0-preview.1" />

Program.cs Configuration

Register services with AddRichTextBox() and map the upload endpoints with MapRichTextBoxUploads():

var builder = WebApplication.CreateBuilder(args);

// Register RichTextBox services (default options)
builder.Services.AddRichTextBox();

// Or with custom options:
builder.Services.AddRichTextBox(options =>
{
    options.UploadWebPath = "/uploads";
    options.MaxUploadBytes = 4 * 1024 * 1024; // 4 MB
    options.AllowedImageExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg" };
    options.AllowedFileExtensions = new[] { ".zip", ".pdf", ".doc", ".docx" };
});

var app = builder.Build();
app.UseStaticFiles();

// Map upload and gallery endpoints
app.MapRichTextBoxUploads();

app.MapRazorPages();
app.Run();

License File

Place your RichTextBox.lic file in the project content root (same folder as Program.cs):

YourProject/
  Program.cs
  RichTextBox.lic         <-- content root
  appsettings.json
  Pages/
    _ViewImports.cshtml
    Index.cshtml
  wwwroot/
    uploads/              <-- image upload target

Tag Helper Registration

Add the following line to Pages/_ViewImports.cshtml:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, RichTextBox

Tag Helper

<richtextbox> Attributes

All attributes available on the <richtextbox> tag helper element, organized by category.

Core

AttributeTypeDefaultDescription
idstringautoHTML element ID for the editor instance
namestringrequiredForm field name for the HTML content
asp-forexpression-Model expression for two-way binding
htmlstringemptyInitial HTML content for the editor
classstring-CSS class for the editor wrapper element

Toolbar

AttributeTypeDefaultDescription
toolbarstring"default"Toolbar preset: default, full, basic, ribbon, office, or a custom button string
toolbar-mobilestring"mobile"Toolbar layout for mobile-width screens

Appearance

AttributeTypeDefaultDescription
skinstring"default"Visual skin: default, gray, office2007blue, rounded-corner, blue
widthstring"100%"Editor width (CSS value: px, %, em)
heightstring"300px"Editor height (CSS value)
resize-modestring"both"Resize mode: both, height, none

Content Behavior

AttributeTypeDefaultDescription
paste-modestring"Auto"Paste handling: Auto, Text, Word, Disabled
enter-key-tagstring"p"Tag for Enter key: p, div, br
url-typestring"default"URL processing: default, relative, absolute
read-onlyboolfalseMakes the editor read-only

Context Menu

AttributeTypeDefaultDescription
enable-context-menuboolfalseEnable custom right-click context menu
context-menu-modestring"Default"Menu preset: Default, Simple, Minimal

Statistics & UI

AttributeTypeDefaultDescription
show-tag-listbooltrueShow tag path in the bottom bar
show-statisticsbooltrueShow character/word statistics in the bottom bar

Content Styling

AttributeTypeDefaultDescription
content-css-urlstringbuilt-inURL of a CSS file applied to the content area
content-css-textstring""Inline CSS text applied to the content area
preview-css-urlstringbuilt-inCSS file for the preview window
editor-body-css-classstring""CSS class applied to the editor body element
editor-body-css-textstring""Inline CSS for the editor body element

Dialog Data

AttributeTypeDefaultDescription
image-items-jsonstring[]JSON array of image URLs for the insert image dialog
gallery-images-jsonstring[]JSON array of {url, text} objects for the gallery
html-templates-jsonstring[]JSON array of [name, html] arrays for templates

Autosave

AttributeTypeDefaultDescription
auto-saveboolfalseEnable localStorage draft saving
auto-save-keystringautolocalStorage key for draft isolation
auto-save-delayint600Delay in milliseconds between saves

Validation

AttributeTypeDefaultDescription
max-html-lengthint0 (unlimited)Maximum total HTML characters
max-text-lengthint0 (unlimited)Maximum visible text characters

Services

Configuration (RichTextBoxOptions)

Configure the RichTextBox services in Program.cs via the AddRichTextBox(options => { ... }) delegate.

builder.Services.AddRichTextBox(options =>
{
    options.UploadWebPath = "/uploads";
    options.MaxUploadBytes = 8 * 1024 * 1024; // 8 MB
    options.AllowedImageExtensions = new[] { ".jpg", ".png", ".gif", ".webp", ".svg" };
    options.AllowedFileExtensions = new[] { ".zip", ".pdf", ".doc", ".docx", ".rtf", ".txt" };
});
PropertyTypeDefaultDescription
UploadWebPathstring"/uploads"Web-accessible path for uploaded files
MaxUploadByteslong4194304Maximum upload file size (4 MB default)
AllowedImageExtensionsstring[].jpg .png .gif .webp .svgAllowed image file extensions
AllowedFileExtensionsstring[].zip .pdf .doc .docx .rtf .txtAllowed non-image file extensions
RequireValidLicensebooltrueRequire valid license file to render editors and accept uploads
LicenseNumberstring"30076785"Expected license number

Toolbars

Toolbar Presets

Use a preset name for quick setup or provide a custom toolbar string with specific buttons.

Built-in Presets

ValueDescription
defaultStandard toolbar with common formatting, links, images, and table commands
fullExtended toolbar with every available command
basicMinimal toolbar: bold, italic, underline, lists, and links
ribbonRibbon-style grouped toolbar (Office-like tabs)
officeOffice-inspired toolbar with grouped sections
mobileCompact toolbar optimized for small screens

Using a Preset

<richtextbox name="Body" toolbar="full" />

<richtextbox name="Body" toolbar="basic" toolbar-mobile="mobile" />

Custom Toolbar String

Build a custom toolbar by listing button names in curly braces, separated by pipe characters for grouping:

<richtextbox name="Body"
    toolbar="{bold,italic,underline}|{fontname,fontsize}|{forecolor,backcolor}|{insertlink,insertimage}|{undo,redo}" />

Use {group1}|{group2} to visually separate button groups. Use # to force a new toolbar row.

Appearance

Skin Reference

Set the visual theme of the editor with the skin attribute.

default

Clean, modern look with a light toolbar background. Works well with any layout.

gray

Subtle gray toolbar that blends with neutral-themed interfaces.

office2007blue

Blue gradient toolbar inspired by the classic Office 2007 ribbon style.

rounded-corner

Soft rounded corners on the toolbar and editor frame.

blue

Blue-toned toolbar with a professional, corporate appearance.

<richtextbox name="Body" skin="office2007blue" toolbar="full" />

Client-side

JavaScript API

Every editor instance is accessible from JavaScript via the global window.richTextBoxEditors dictionary or directly as a window property using the editor ID.

Accessing the Editor Instance

// By dictionary lookup
var editor = window.richTextBoxEditors["myEditor"];

// Or directly by ID (when the id matches a valid JS identifier)
var editor = window.myEditor;

Methods

MethodDescription
getHTMLCode()Returns the current HTML content as a string
setHTMLCode(html)Replaces the editor content with the given HTML string
getPlainText()Returns the visible text content without HTML tags
insertHTML(html)Inserts HTML at the current caret position
execCommand(cmd)Executes a toolbar command programmatically (e.g. "bold", "insertimage")
focus()Sets focus to the editor content area
getDocument()Returns the iframe document object for low-level DOM access
getEditable()Returns the editable body element inside the iframe
attachEvent(name, handler)Subscribes to an editor event
detachEvent(name, handler)Unsubscribes from an editor event

Example: Get and Set Content

// Read the current content
var html = editor.getHTMLCode();
console.log("HTML length:", html.length);

// Replace content
editor.setHTMLCode("<h2>Hello World</h2><p>New content here.</p>");

// Insert at caret
editor.insertHTML("<img src='/uploads/photo.jpg' />");

// Execute a command
editor.execCommand("bold");

Client-side

Events

Subscribe to editor events using attachEvent and unsubscribe with detachEvent.

Event NameDescription
changeFired when the editor content changes (keystroke, paste, command, etc.)
exec_commandFired when a toolbar command is executed
selectionchangeFired when the text selection or caret position changes

Example: Listen for Changes

var editor = window.richTextBoxEditors["myEditor"];

function onContentChange() {
    var html = editor.getHTMLCode();
    console.log("Content changed. Length:", html.length);
    document.getElementById("preview").innerHTML = html;
}

// Subscribe
editor.attachEvent("change", onContentChange);

// Unsubscribe later
// editor.detachEvent("change", onContentChange);

Example: Track Commands

editor.attachEvent("exec_command", function() {
    console.log("A command was executed");
});

Server-side

Upload & File Management

MapRichTextBoxUploads() registers two endpoints that handle all file operations for the editor.

Mapped Endpoints

EndpointMethodDescription
/richtextbox/uploadPOSTAccepts image and document file uploads. Returns the public URL of the uploaded file.
/richtextbox/galleryGET / POSTBrowse folders, list images, and create subfolders within the upload directory.

How Uploads Work

1

The user clicks the image or file toolbar button and selects a file in the dialog.

2

The editor POSTs the file to /richtextbox/upload. The server validates the extension and size, saves the file to wwwroot/uploads/, and returns the public URL.

3

The returned URL is inserted into the editor content as an <img> tag or a download link.

Gallery Browsing

The /richtextbox/gallery endpoint powers the gallery dialog. Users can browse previously uploaded images, navigate subfolders, and create new folders directly from the editor UI. The gallery reads from and writes to the same wwwroot/uploads/ directory.

Configuring Upload Options

builder.Services.AddRichTextBox(options =>
{
    // Change the upload directory (relative to wwwroot)
    options.UploadWebPath = "/media/uploads";

    // Increase max upload size to 10 MB
    options.MaxUploadBytes = 10 * 1024 * 1024;

    // Restrict allowed image types
    options.AllowedImageExtensions = new[] { ".jpg", ".jpeg", ".png", ".webp" };

    // Restrict allowed document types
    options.AllowedFileExtensions = new[] { ".pdf", ".docx" };
});

Data binding

Form Integration

The editor integrates with standard HTML forms, ASP.NET Core model binding, and AJAX submissions.

Standard Form POST

Wrap the editor in a <form> with a name attribute. The HTML content is submitted as a form field.

<form method="post">
    <richtextbox name="Body" toolbar="default" height="400px" />
    <button type="submit">Submit</button>
</form>

In your page model, bind the value:

[BindProperty]
public string Body { get; set; }

public void OnPost()
{
    // Body contains the submitted HTML
    var html = Body;
}

Model Binding with asp-for

Use asp-for for strong-typed model binding. The editor reads the initial value from the model and writes back on form submission.

// PageModel
public class EditModel : PageModel
{
    [BindProperty]
    public ArticleModel Article { get; set; }
}

public class ArticleModel
{
    public string Title { get; set; }
    public string Content { get; set; }
}
<!-- Razor Page -->
<form method="post">
    <input asp-for="Article.Title" />
    <richtextbox asp-for="Article.Content" toolbar="full" height="500px" />
    <button type="submit">Save</button>
</form>

AJAX Submission

Read the editor content with JavaScript and send it via fetch:

async function submitContent() {
    var editor = window.richTextBoxEditors["myEditor"];
    var html = editor.getHTMLCode();

    var response = await fetch("/api/articles", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "RequestVerificationToken": document.querySelector(
                'input[name="__RequestVerificationToken"]'
            ).value
        },
        body: JSON.stringify({ content: html })
    });

    if (response.ok) {
        alert("Saved successfully!");
    }
}

Production

Deployment

Key considerations when deploying RichTextBox to production environments.

Pre-deploy checklist

  • RichTextBox.lic is included in the publish output (content root)
  • wwwroot/uploads directory exists and is writable by the app pool identity
  • app.UseStaticFiles() is called before app.MapRichTextBoxUploads()
  • ✓ Maximum request body size is configured for large file uploads

IIS

Install the ASP.NET Core Hosting Bundle. Set maxAllowedContentLength in web.config for large uploads. Ensure the app pool identity has write access to the uploads folder.

Azure App Service

Deploy via Visual Studio Publish, Azure CLI, or GitHub Actions. For persistent upload storage, consider Azure Blob Storage instead of the default local file system.

Docker

Mount a persistent volume for /app/wwwroot/uploads so uploaded files survive container restarts. Include RichTextBox.lic in the Docker image or mount it as a secret.

Linux with nginx

Run Kestrel behind an nginx reverse proxy. Set client_max_body_size in your nginx config for large uploads.

Kestrel request body size

builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 50 * 1024 * 1024; // 50 MB
});