BlazorEditorJs
A simple editorjs implementation as a blazor component - HTML / C# / CSS / JavaScript
Install / Use
/learn @kibblewhite/BlazorEditorJsREADME
blazor-editorjs
A Blazor component library wrapping EditorJS — a block-styled editor with clean JSON output. This package provides an <Editor /> component, a fluent builder API for tool and editor configuration, JavaScript interop for full editor lifecycle control, and provisioning state tracking for multi-editor pages.
Try the Live Demo
Packages
| Package | Description |
|---|---|
| EditorJs | The Blazor <Editor /> component, JS interop, provisioning monitor, and all bundled EditorJS plugins. References EditorJs.Configuration automatically. |
| EditorJs.Configuration | Standalone builder API (EditorToolsBuilder, EditorConfigBuilder, NamingScheme) and JsonObjectExtensions. Use this package independently when you only need to build tool/config JSON without the Blazor component — for example, in a shared library or server-side code. |
Installation
Install the EditorJs NuGet package (this also brings in EditorJs.Configuration as a dependency):
dotnet add package EditorJs
Register the required services in Program.cs:
builder.Services.AddScopedEditorJsInterop();
This registers EditorJsInterop (scoped) and ProvisioningMonitor (transient).
Include the bundled EditorJS scripts in your index.html (WebAssembly) or App.razor (Server):
<script src="_content/EditorJs/lib/editorjs-bundle.js"></script>
The bundle includes EditorJS core and all bundled plugins. Alternatively, load only the plugins you need:
<script src="_content/EditorJs/lib/editorjs/editorjs/dist/editorjs.umd.min.js"></script>
<script src="_content/EditorJs/lib/editorjs/header/dist/header.umd.min.js"></script>
<!-- ... additional plugins as needed -->
Quick Start
@using EditorJs
@using EditorJs.Builders
<Editor @ref="editor"
Id="my-editor"
Value="editorValue"
ValueChanged="OnValueChanged"
Tools="editorTools"
Configurations="editorConfig"
ProvisioningCallbacks="provisioningMonitor.EventsCallbacksHandlerAsync" />
@code {
[Inject] public required ProvisioningMonitor provisioningMonitor { get; init; }
private Editor? editor;
private JsonObject editorValue = Editor.CreateEmptyJsonObject();
private JsonObject editorTools = EditorToolsBuilder.Text().Build();
private JsonObject editorConfig = EditorConfigBuilder.Create()
.DefaultBlock("paragraph")
.Build();
private Task OnValueChanged(JsonObject value)
=> Task.FromResult(editorValue = value);
}
Editor Component Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| Id | string | Yes | Unique identifier for the editor instance. |
| Tools | JsonObject | Yes | Tool plugin configurations — use EditorToolsBuilder or raw JSON. |
| Configurations | JsonObject | Yes | Editor-level settings — use EditorConfigBuilder or raw JSON. |
| Value | JsonObject | No | Current editor content as an EditorJS data object. |
| ValueChanged | EventCallback<JsonObject> | No | Fires when editor content changes. |
| EnterKeyDownCapture | EventCallback<JsonObject> | No | Fires when Enter is pressed (for single-line editors using TextElement). |
| ProvisioningCallbacks | EventCallback<ProvisioningStatus> | No | Tracks editor initialisation lifecycle. |
| Name | string? | No | HTML name attribute on the holder div. |
| Style | string? | No | Inline CSS styles on the holder div. |
| Class | string? | No | CSS classes on the holder div. |
| Title | string? | No | HTML title attribute on the holder div. |
Building Tool Configurations
The EditorToolsBuilder provides a fluent API that handles the internal LoadActions and OptionsNamingScheme boilerplate for you.
Presets
// Common text editing tools
JsonObject tools = EditorToolsBuilder.Text().Build();
// All bundled plugins
JsonObject tools = EditorToolsBuilder.Rich().Build();
Text preset includes: Header, NestedList, Quote, Delimiter, Marker, InlineCode.
Rich preset includes: Header, NestedList, Checklist, CodeTool, Quote, Table, SimpleImage, Embed, Delimiter, Warning, RawTool, Marker, InlineCode, Underline.
Custom Composition
Pick only the tools you need:
JsonObject tools = EditorToolsBuilder.Create()
.Header()
.NestedList()
.Quote()
.Table()
.Marker()
.InlineCode()
.Build();
Configuring Individual Tools
Each tool method accepts an optional Action<JsonObject> to set tool-specific options:
JsonObject tools = EditorToolsBuilder.Create()
.Header(options =>
{
options["inlineToolbar"] = true;
options["config"] = new JsonObject
{
["placeholder"] = "Enter a header",
["levels"] = new JsonArray(1, 2, 3, 4),
["defaultLevel"] = 2
};
})
.Embed(options =>
{
options["config"] = new JsonObject
{
["services"] = new JsonObject
{
["youtube"] = true,
["vimeo"] = true
}
};
})
.Build();
Extending Presets
Calling a tool method on a preset overwrites that tool's entry, allowing selective customisation:
JsonObject tools = EditorToolsBuilder.Rich()
.Header(options =>
{
options["config"] = new JsonObject { ["defaultLevel"] = 3 };
})
.Build();
Single-Line Text Editor
Use TextElement with DisabledParagraph for constrained single-line inputs (titles, labels, chat fields):
JsonObject tools = EditorToolsBuilder.Create()
.DisabledParagraph()
.TextElement(options =>
{
options["inlineToolbar"] = true;
options["config"] = new JsonObject
{
["placeholder"] = "Enter title...",
["hideToolbar"] = true,
["hidePopoverItem"] = true,
["allowEnterKeyDown"] = false,
["wrapElement"] = "title"
};
})
.Build();
DisabledParagraph() registers a no-op replacement for the built-in EditorJS paragraph, preventing console warnings when the default paragraph is not loaded.
Available Tool Methods
| Method | EditorJS Plugin | Notes |
|---|---|---|
| Header() | @editorjs/header | Heading levels 1-6 |
| NestedList() | @editorjs/nested-list | Ordered/unordered with nesting |
| Checklist() | @editorjs/checklist | Checkbox items |
| CodeTool() | @editorjs/code | Monospace code blocks |
| Quote() | @editorjs/quote | Blockquote with caption and alignment |
| Table() | @editorjs/table | Tabular data with optional headings |
| SimpleImage() | @editorjs/simple-image | Image from URL with caption |
| Embed() | @editorjs/embed | YouTube, Vimeo, Instagram, Twitter, Facebook, Imgur |
| Delimiter() | @editorjs/delimiter | Visual separator between sections |
| Warning() | @editorjs/warning | Title + message callout |
| RawTool() | @editorjs/raw | Raw HTML block |
| Marker() | @editorjs/marker | Highlighted inline text |
| InlineCode() | @editorjs/inline-code | Monospace inline text |
| Underline() | @editorjs/underline | Underlined inline text |
| TextElement() | editorjs-text | Single-line text block |
| DisabledParagraph() | editorjs-text | No-op replacement for built-in paragraph |
| Tool(key, globalClassName) | Any | Register a custom/external plugin (see below) |
External Plugins
Third-party EditorJS plugins that are not bundled with the library can be loaded via a <script> tag and registered using the Tool() method.
1. Add the plugin script to your index.html:
<script src="_content/EditorJs/lib/editorjs-bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/editorjs-alert@latest/dist/bundle.js"></script>
2. Register the plugin with Tool():
JsonObject tools = EditorToolsBuilder.Text()
.Tool("Alert", "Alert", configure: options =>
{
options["config"] = new JsonObject
{
["defaultType"] = "info",
["messagePlaceholder"] = "Enter alert message..."
};
})
.Build();
The key parameter ("Alert") is automatically transformed to camelCase ("alert") for the EditorJS tool registration. Control this with the naming_scheme parameter:
// Tool key becomes "my_toggle_block" (snake_case)
.Tool("MyToggleBlock", "MyToggleBlock", naming_scheme: NamingScheme.SnakeCase)
// Tool key becomes "my-toggle-block" (kebab-case)
.Tool("MyToggleBlock", "MyToggleBlock", naming_scheme: NamingScheme.KebabCase)
// Tool key stays "MyToggleBlock" (PascalCase)
.Tool("MyToggleBlock", "MyToggleBlock", naming_scheme: NamingScheme.PascalCase)
If the plugin expects a specific key that doesn't follow any naming convention, use override_options_key:
.Tool("MyCustomTool", "MyLibrary.ToolClass", override_options_key: "custom-tool")
Th
