
Syncfusion Blazor Blockeditor
- 244 installs
- 4 repo stars
- Updated July 28, 2026
- syncfusion/blazor-ui-components-skills
Use syncfusion-blazor-blockeditor for development tasks
About
syncfusion-blazor-blockeditor: A skill for development. This provides functionality for development workflows.
- syncfusion-blazor-blockeditor
Syncfusion Blazor Blockeditor by the numbers
- 244 all-time installs (skills.sh)
- +13 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,593 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/blazor-ui-components-skills --skill syncfusion-blazor-blockeditorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 244 |
|---|---|
| repo stars | ★ 4 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/blazor-ui-components-skills ↗ |
What it does
Use syncfusion-blazor-blockeditor for development tasks
Files
Syncfusion Blazor Block Editor
Component Overview
The Syncfusion Blazor Block Editor is a powerful, modular content creation component that enables users to build rich, structured documents using customizable blocks. Each block represents a specific content type—headings, paragraphs, lists, tables, images, code blocks, and more—providing a clean, organized editing experience.
Key Features
- Block-Based Architecture: Create structured content using distinct block types (heading, paragraph, list, table, image, code, quote, callout, divider, toggle)
- Intuitive Menus: Slash command menu (
/), context menu (right-click), block action menu (hover), and inline toolbar (text selection) - Drag-and-Drop Reordering: Rearrange blocks intuitively by dragging
- Undo/Redo Support: Full history management with configurable stack depth
- Keyboard Shortcuts: Comprehensive shortcuts for formatting, block creation, and editor operations
- Paste Cleanup: Advanced HTML sanitization, style filtering, and tag removal for safe content pasting
- Event System: Created, BlockChanged, SelectionChanged, Focus, Blur, and paste lifecycle events
- Responsive Design: Adaptive to different screen sizes and viewports
- Read-Only Mode: Display-only content without editing capabilities
- Custom Styling: CSS class customization and theme integration
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation via NuGet (Visual Studio, VS Code, .NET CLI)
- Blazor Web App project setup
- Service registration and configuration
- Import namespaces and add theme resources
- Render modes (Server, WebAssembly, Auto)
- Basic component initialization
- Block configuration and data binding
Appearance and Styling
📄 Read: references/appearance-and-styling.md
- Set component width and height
- Read-only mode configuration
- Custom CSS class application
- Responsive design patterns
- Theme customization and styling approaches
- Style examples and best practices
Editor Menus
📄 Read: references/editor-menus.md
- Slash command menu (built-in items, customization, events)
- Context menu (right-click actions, customization, events)
- Block action menu (drag handle actions, customization, events)
- Inline toolbar (text formatting options, customization, events)
- Menu event handling and filtering
- Custom command and menu item creation
Built-In Blocks
📄 Read: references/built-in-blocks.md
- Heading blocks (levels 1-4)
- Paragraph blocks
- List types (bullet, numbered, checklist)
- Table blocks
- Code blocks
- Image/media blocks
- Quote and callout blocks
- Toggle (collapsible) blocks
- Divider blocks
- Block nesting and hierarchy
Drag-Drop and Undo-Redo
📄 Read: references/drag-drop-and-undo.md
- Enable/disable drag-and-drop
- Single and multiple block dragging
- Undo/redo keyboard shortcuts
- Configure undo/redo stack depth
- History management and state tracking
- Drag operation visual feedback
Events and Interactions
📄 Read: references/events-and-interactions.md
- Created event (initialization)
- BlockChanged event (structural changes)
- SelectionChanged event (text selection)
- Focus and Blur events (editor state)
- Keyboard shortcuts (content editing, block creation, block management, general operations)
- Custom keyboard shortcut configuration (KeyConfig)
- Event handler patterns and examples
Content Handling
📄 Read: references/content-handling.md
- Getting and setting block content
- Content model structure (BlockModel, ContentModel)
- Paste cleanup configuration (AllowedStyles, DeniedTags, KeepFormat, PlainText)
- PasteCleanupStarting and PasteCleanupCompleted events
- Security best practices (XSS prevention)
- Content validation patterns
Labels and Mentions
📄 Read: references/labels-and-mentions.md
- UserModel for mentioning users (@mentions)
- LabelItemModel for tagging content (#labels)
- BlockEditorLabel component configuration
- MentionContentSettings and LabelContentSettings
- Implementing collaborative mentions and labels
- Practical workflow examples and patterns
Advanced Methods
📄 Read: references/advanced-methods.md
- GetSelectedBlocksAsync() - Retrieve selected blocks
- SelectAllBlocksAsync() - Select all blocks programmatically
- FocusInAsync() / FocusOutAsync() - Manage editor focus
- PrintAsync() - Print editor content
- EnableToolbarItemsAsync() / DisableToolbarItemsAsync() - Control toolbar availability
- Advanced workflow patterns and use cases
Advanced Features
📄 Read: references/advanced-features.md
- WebAssembly integration and considerations
- Performance optimization strategies
- Custom block type implementation
- Accessibility features (WCAG compliance)
- Auto-save and state persistence patterns
- Common troubleshooting scenarios
Quick Start Example
@using Syncfusion.Blazor.BlockEditor
@rendermode InteractiveAuto
<div id="container" style="height: 500px; width: 100%;">
<SfBlockEditor @bind-Blocks="blockData" EnableDragAndDrop="true">
<BlockEditorCommandMenu></BlockEditorCommandMenu>
<BlockEditorContextMenu Enable="true"></BlockEditorContextMenu>
<BlockEditorActionMenu Enable="true"></BlockEditorActionMenu>
<BlockEditorInlineToolbar Enable="true"></BlockEditorInlineToolbar>
<BlockEditorPasteCleanup AllowedStyles="@(new string[] { "font-weight", "font-style", "text-decoration" })"
DeniedTags="@(new string[] { "script", "iframe" })">
</BlockEditorPasteCleanup>
</SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData = new List<BlockModel>
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new List<ContentModel>
{
new ContentModel { ContentType = ContentType.Text, Content = "Welcome to Block Editor" }
}
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new List<ContentModel>
{
new ContentModel { ContentType = ContentType.Text, Content = "Start typing or use / to add new blocks..." }
}
}
};
}Common Patterns
Pattern 1: Event-Driven Content Tracking
Monitor document changes in real-time for auto-save or logging:
<SfBlockEditor BlockChanged="@OnBlockChanged"
SelectionChanged="@OnSelectionChanged">
</SfBlockEditor>
@code {
private void OnBlockChanged(BlockChangedEventArgs args)
{
// Auto-save, track changes, update UI
}
private void OnSelectionChanged(SelectionChangedEventArgs args)
{
// Update toolbar state based on selection
}
}Pattern 2: Read-Only Preview Mode
Display finalized content without editing:
<SfBlockEditor @bind-Blocks="blockData" ReadOnly="true">
</SfBlockEditor>Pattern 3: Custom Keyboard Shortcuts
Override default shortcuts for application-specific commands:
<SfBlockEditor KeyConfig="@customShortcuts">
</SfBlockEditor>
@code {
private Dictionary<string, string> customShortcuts = new()
{
{ "Bold", "alt+b" },
{ "Italic", "alt+i" }
};
}Pattern 4: Secure Paste with Content Filtering
Control paste behavior for safety and consistency:
<BlockEditorPasteCleanup AllowedStyles="@allowedStyles"
DeniedTags="@deniedTags"
PlainText="false">
</BlockEditorPasteCleanup>
@code {
private string[] allowedStyles = { "font-weight", "font-style" };
private string[] deniedTags = { "script", "iframe", "object" };
}Key Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
Blocks | List<BlockModel> | Empty | The content blocks bound to the editor |
Width | string | "100%" | Editor container width |
Height | string | "auto" | Editor container height |
ReadOnly | bool | false | Enable/disable read-only mode |
EnableDragAndDrop | bool | true | Enable/disable block dragging |
UndoRedoStack | int | 30 | Maximum undo/redo history depth |
CssClass | string | "" | Custom CSS classes for styling |
KeyConfig | Dictionary<string, string> | Default shortcuts | Custom keyboard shortcuts |
Common Use Cases
1. Blog Platform: Create a rich blog editor with headings, images, and formatting 2. Document Management: Build document creators for articles and reports 3. Knowledge Base: Implement structured content editors for documentation 5. Content Preview: Display finalized documents in read-only mode 6. Form Builder: Use blocks to create dynamic form templates 7. Email Templates: Create email templates with customizable blocks 8. Proposal Builder: Generate professional proposals with block-based structure
Advanced Features in Syncfusion Blazor Block Editor
WebAssembly Integration
The Block Editor works seamlessly with WebAssembly render modes for client-side performance.
WebAssembly Render Mode
Configure your component to run in the browser:
@page "/editor"
@rendermode InteractiveWebAssembly
<SfBlockEditor @bind-Blocks="blockData"></SfBlockEditor>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
}Auto Render Mode (Recommended)
Start on server, switch to WebAssembly:
@rendermode InteractiveAuto
<SfBlockEditor @bind-Blocks="blockData"></SfBlockEditor>Benefits:
- Fast initial page load (server-rendered)
- Rich interactivity (WebAssembly runtime)
- Smooth transition between environments
Performance Optimization
1. Lazy Load Block Content
@rendermode InteractiveAuto
<SfBlockEditor BlockChanged="@OnBlockChanged"></SfBlockEditor>
@code {
private List<BlockModel> blockData = new();
protected override async Task OnInitializedAsync()
{
// Load only first 10 blocks initially
blockData = await LoadInitialBlocks(10);
}
private void OnBlockChanged(BlockChangedEventArgs args)
{
// Load more blocks as needed
if (ShouldLoadMore())
{
_ = LoadMoreBlocks();
}
}
private async Task<List<BlockModel>> LoadInitialBlocks(int count)
{
// Simulate loading from API
await Task.Delay(100);
return GetDummyBlocks(count);
}
private async Task LoadMoreBlocks()
{
await Task.Delay(100);
// Add more blocks to blockData
}
private bool ShouldLoadMore()
{
return blockData.Count < 100;
}
private List<BlockModel> GetDummyBlocks(int count)
{
return Enumerable.Range(0, count)
.Select(i => new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = $"Block {i}" } }
})
.ToList();
}
}2. Debounce Change Events
Reduce excessive state updates:
@rendermode InteractiveAuto
<SfBlockEditor BlockChanged="@OnBlockChanged"></SfBlockEditor>
@code {
private System.Timers.Timer debounceTimer;
private const int DebounceDelay = 500; // milliseconds
protected override void OnInitialized()
{
debounceTimer = new System.Timers.Timer(DebounceDelay);
debounceTimer.Elapsed += async (s, e) => await ProcessChanges();
debounceTimer.AutoReset = false;
}
private void OnBlockChanged(BlockChangedEventArgs args)
{
debounceTimer.Stop();
debounceTimer.Start();
}
private async Task ProcessChanges()
{
// Perform auto-save or other operations
await SaveContent();
}
private async Task SaveContent()
{
// Save to server
await Task.Delay(100);
Console.WriteLine("Content saved");
}
public void Dispose()
{
debounceTimer?.Dispose();
}
}3. Virtual Scrolling for Large Documents
@rendermode InteractiveAuto
<div style="height: 600px; overflow-y: auto; display: flex; flex-direction: column;">
<SfBlockEditor @bind-Blocks="visibleBlocks" Height="600px"></SfBlockEditor>
</div>
@code {
private List<BlockModel> allBlocks = new();
private List<BlockModel> visibleBlocks = new();
private const int ItemsPerPage = 20;
protected override void OnInitialized()
{
// Load all blocks, but display only visible ones
allBlocks = LoadAllBlocks();
visibleBlocks = allBlocks.Take(ItemsPerPage).ToList();
}
private List<BlockModel> LoadAllBlocks()
{
return Enumerable.Range(0, 1000)
.Select(i => new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = $"Block {i}" } }
})
.ToList();
}
private void OnScroll(WheelEventArgs e)
{
// Update visible blocks based on scroll position
}
}Custom Block Types
Extend the editor with custom blocks:
@rendermode InteractiveAuto
<SfBlockEditor @bind-Blocks="blockData">
<BlockEditorCommandMenu Commands="@customCommands" ItemSelect="@OnCommandSelect">
</BlockEditorCommandMenu>
</SfBlockEditor>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private List<CommandItemModel> customCommands = new()
{
new CommandItemModel
{
ID = "survey-block",
GroupBy = "Custom",
Label = "Survey Question",
IconCss = "e-icons e-comment"
},
new CommandItemModel
{
ID = "rating-block",
GroupBy = "Custom",
Label = "Rating Widget",
IconCss = "e-icons e-star"
}
};
private void OnCommandSelect(CommandItemSelectEventArgs args)
{
if (args.Item.ID == "survey-block")
{
InsertSurveyBlock();
}
else if (args.Item.ID == "rating-block")
{
InsertRatingBlock();
}
}
private void InsertSurveyBlock()
{
// Create custom survey block
var surveyBlock = new BlockModel
{
BlockType = BlockType.Paragraph, // Use as container
Content = new()
{
new ContentModel
{
ContentType = ContentType.Text,
Content = "Survey Question: [Your question here]"
}
}
};
blockData.Add(surveyBlock);
}
private void InsertRatingBlock()
{
// Create custom rating block
var ratingBlock = new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new()
{
new ContentModel
{
ContentType = ContentType.Text,
Content = "Rate this: ⭐⭐⭐⭐⭐"
}
}
};
blockData.Add(ratingBlock);
}
}Accessibility Features
WCAG Compliance
@rendermode InteractiveAuto
<div>
<label for="editor-title">Document Title</label>
<SfBlockEditor @bind-Blocks="blockData"
id="editor-title"
role="textbox"
aria-label="Rich text editor"
aria-describedby="editor-help">
</SfBlockEditor>
<div id="editor-help" style="font-size: 12px; color: #666;">
Use keyboard shortcuts: Ctrl+B for bold, Ctrl+I for italic, / for commands
</div>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
}Keyboard Navigation
<!-- Editor is fully keyboard accessible by default -->
<!-- Users can navigate without mouse using:
- Tab: Move between UI elements
- Arrow Keys: Navigate within blocks
- Ctrl+Z/Y: Undo/Redo
- Ctrl+B/I/U: Format text
- /: Open command menu
-->Auto-Save Implementation
Auto-Save with Interval
@page "/auto-save-editor"
@rendermode InteractiveAuto
@implements IAsyncDisposable
<div style="max-width: 900px; margin: 20px auto;">
<div style="padding: 15px; background-color: #f5f5f5; margin-bottom: 15px;">
<h2>Auto-Save Editor</h2>
<p>Status: <span style="color: @(autoSaveStatus == "Saved" ? "green" : "orange");">@autoSaveStatus</span></p>
<p>Last Saved: @lastSaveTime</p>
</div>
<SfBlockEditor @bind-Blocks="blockData"
Height="500px"
BlockChanged="@OnBlockChanged">
</SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private string autoSaveStatus = "Ready";
private string lastSaveTime = "Never";
private System.Timers.Timer autoSaveTimer;
private bool hasUnsavedChanges = false;
protected override void OnInitialized()
{
autoSaveTimer = new System.Timers.Timer(10000); // 10 seconds
autoSaveTimer.Elapsed += async (s, e) => await PerformAutoSave();
autoSaveTimer.Start();
}
private void OnBlockChanged(BlockChangedEventArgs args)
{
hasUnsavedChanges = true;
autoSaveStatus = "Unsaved changes...";
}
private async Task PerformAutoSave()
{
if (hasUnsavedChanges)
{
autoSaveStatus = "Saving...";
try
{
// Save to server
await SaveToServer();
hasUnsavedChanges = false;
autoSaveStatus = "Saved";
lastSaveTime = DateTime.Now.ToString("HH:mm:ss");
}
catch (Exception ex)
{
autoSaveStatus = "Save failed";
Console.WriteLine($"Save error: {ex.Message}");
}
await InvokeAsync(StateHasChanged);
}
}
private async Task SaveToServer()
{
// Simulate API call
await Task.Delay(500);
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
autoSaveTimer?.Dispose();
}
}Error Handling
Try-Catch Patterns
@rendermode InteractiveAuto
<div style="max-width: 900px; margin: 20px auto;">
@if (!string.IsNullOrEmpty(errorMessage))
{
<div style="padding: 15px; background-color: #f8d7da; color: #721c24; border-radius: 4px; margin-bottom: 15px;">
<strong>Error:</strong> @errorMessage
</div>
}
<SfBlockEditor @bind-Blocks="blockData" BlockChanged="@OnBlockChanged"></SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData = new();
private string errorMessage = "";
protected override async Task OnInitializedAsync()
{
try
{
blockData = await LoadContent();
}
catch (Exception ex)
{
errorMessage = $"Failed to load content: {ex.Message}";
}
}
private void OnBlockChanged(BlockChangedEventArgs args)
{
try
{
ValidateContent();
}
catch (Exception ex)
{
errorMessage = $"Validation error: {ex.Message}";
}
}
private async Task<List<BlockModel>> LoadContent()
{
// Load from API with error handling
await Task.Delay(100);
return new() { new BlockModel { BlockType = BlockType.Paragraph } };
}
private void ValidateContent()
{
if (blockData == null)
throw new InvalidOperationException("Block data is null");
}
}State Persistence
Save to LocalStorage
@rendermode InteractiveAuto
<SfBlockEditor @bind-Blocks="blockData" BlockChanged="@OnBlockChanged"></SfBlockEditor>
@code {
private List<BlockModel> blockData = new();
protected override async Task OnInitializedAsync()
{
// Load from localStorage
var savedContent = await JS.InvokeAsync<string>("localStorage.getItem", "blockEditorContent");
if (!string.IsNullOrEmpty(savedContent))
{
blockData = System.Text.Json.JsonSerializer.Deserialize<List<BlockModel>>(savedContent);
}
}
private void OnBlockChanged(BlockChangedEventArgs args)
{
// Save to localStorage
var json = System.Text.Json.JsonSerializer.Serialize(blockData);
_ = JS.InvokeVoidAsync("localStorage.setItem", "blockEditorContent", json);
}
[Inject]
private IJSRuntime JS { get; set; }
}Common Troubleshooting
| Issue | Solution |
|---|---|
| Content not updating | Ensure @bind-Blocks is used correctly |
| Events not firing | Verify render mode is interactive |
| Performance lag | Implement debouncing for BlockChanged |
| Paste not working | Check paste cleanup permissions |
| Shortcuts not working | Verify KeyConfig syntax and render mode |
| WebAssembly issues | Check browser console for JS errors |
Advanced Methods in Syncfusion Blazor Block Editor
This guide covers advanced programmatic methods for controlling the Block Editor component. These methods enable custom workflows, automation, and fine-grained control over editor behavior.
Method Overview
| Method | Purpose | Return Type | Async |
|---|---|---|---|
GetSelectedBlocksAsync() | Get currently selected blocks | Task<List<BlockModel>> | ✅ |
SelectAllBlocksAsync() | Select all blocks in editor | Task | ✅ |
FocusInAsync() | Set focus to editor | Task | ✅ |
FocusOutAsync() | Remove focus from editor | Task | ✅ |
PrintAsync() | Print editor content | Task | ✅ |
EnableToolbarItemsAsync(List<int>) | Enable toolbar items by index | Task | ✅ |
DisableToolbarItemsAsync(List<int>) | Disable toolbar items by index | Task | ✅ |
---
GetSelectedBlocksAsync
Retrieves all currently selected blocks in the editor. This method returns a list of BlockModel objects that are currently selected by the user.
Syntax
public async Task<List<BlockModel>> GetSelectedBlocksAsync()Return Value
Returns a Task<List<BlockModel>> containing the selected block models. If no blocks are selected, returns an empty list.
Example: Get Selected Blocks
@page "/get-selected"
@rendermode InteractiveAuto
@using Syncfusion.Blazor.BlockEditor
<div style="margin-bottom: 20px;">
<button class="btn btn-primary" @onclick="GetSelected">Get Selected Blocks</button>
<button class="btn btn-secondary" @onclick="ClearSelection">Clear Selection</button>
</div>
<div id="result" style="padding: 10px; background-color: #f5f5f5; margin-bottom: 20px; border-radius: 4px;">
<strong>Selected:</strong> @selectedCount blocks
</div>
<div style="height: 400px; border: 1px solid #ddd; border-radius: 4px;">
<SfBlockEditor @ref="editorRef" @bind-Blocks="blockData">
<BlockEditorCommandMenu></BlockEditorCommandMenu>
</SfBlockEditor>
</div>
@code {
private SfBlockEditor editorRef;
private List<BlockModel> blockData = new();
private int selectedCount = 0;
protected override void OnInitialized()
{
blockData = new()
{
new BlockModel { BlockType = BlockType.Heading, Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 1" } } },
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 2" } } },
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 3" } } },
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 4" } } }
};
}
private async Task GetSelected()
{
var selected = await editorRef.GetSelectedBlocksAsync();
selectedCount = selected.Count;
}
private async Task ClearSelection()
{
selectedCount = 0;
}
}Use Cases
1. Batch Operations: Get selected blocks to perform bulk actions (delete, duplicate, format) 2. Selection Analysis: Determine which blocks are currently selected for UI updates 3. Content Export: Export only selected blocks to a different format 4. Conditional Actions: Enable/disable features based on selection
---
SelectAllBlocksAsync
Selects all blocks in the editor programmatically. This is useful for implementing "Select All" functionality or preparing for bulk operations.
Syntax
public async Task SelectAllBlocksAsync()Example: Select All Blocks
@page "/select-all"
@rendermode InteractiveAuto
@using Syncfusion.Blazor.BlockEditor
<div style="margin-bottom: 20px;">
<button class="btn btn-primary" @onclick="SelectAll">Select All Blocks</button>
<button class="btn btn-warning" @onclick="DeleteSelected">Delete Selected</button>
</div>
<div style="height: 400px; border: 1px solid #ddd; border-radius: 4px;">
<SfBlockEditor @ref="editorRef" @bind-Blocks="blockData">
<BlockEditorCommandMenu></BlockEditorCommandMenu>
</SfBlockEditor>
</div>
@code {
private SfBlockEditor editorRef;
private List<BlockModel> blockData = new();
protected override void OnInitialized()
{
blockData = new()
{
new BlockModel { BlockType = BlockType.Heading, Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Title" } } },
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Paragraph 1" } } },
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Paragraph 2" } } }
};
}
private async Task SelectAll()
{
await editorRef.SelectAllBlocksAsync();
}
private async Task DeleteSelected()
{
var selected = await editorRef.GetSelectedBlocksAsync();
foreach (var block in selected)
{
await editorRef.RemoveBlockAsync(block.ID);
}
}
}Use Cases
1. Select All Feature: Implement keyboard shortcut (Ctrl+A) handling 2. Bulk Operations: Prepare all blocks for formatting or export 3. Copy All: Copy entire document content to clipboard 4. Statistics: Calculate metrics across all blocks
---
FocusInAsync
Sets programmatic focus to the Block Editor component. This method ensures the editor receives focus and is ready for user input.
Syntax
public async Task FocusInAsync()Example: Set Focus to Editor
@page "/focus-control"
@rendermode InteractiveAuto
@using Syncfusion.Blazor.BlockEditor
<div style="margin-bottom: 20px;">
<button class="btn btn-info" @onclick="FocusEditor">Focus Editor</button>
<button class="btn btn-warning" @onclick="BlurEditor">Remove Focus</button>
</div>
<input type="text" placeholder="Click here first" style="margin-bottom: 10px; padding: 8px; width: 100%;" />
<div style="height: 400px; border: 1px solid #ddd; border-radius: 4px;">
<SfBlockEditor @ref="editorRef" @bind-Blocks="blockData"
Focus="@OnFocus" Blur="@OnBlur">
<BlockEditorCommandMenu></BlockEditorCommandMenu>
</SfBlockEditor>
</div>
<div style="margin-top: 10px; padding: 10px; background-color: #f0f0f0; border-radius: 4px;">
<strong>Status:</strong> @focusStatus
</div>
@code {
private SfBlockEditor editorRef;
private List<BlockModel> blockData = new();
private string focusStatus = "Unfocused";
protected override void OnInitialized()
{
blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Start typing here..." } } }
};
}
private async Task FocusEditor()
{
await editorRef.FocusInAsync();
}
private async Task BlurEditor()
{
await editorRef.FocusOutAsync();
}
private void OnFocus(FocusEventArgs args)
{
focusStatus = "Focused";
}
private void OnBlur(BlurEventArgs args)
{
focusStatus = "Unfocused";
}
}Use Cases
1. Auto-Focus: Focus editor when page loads 2. Modal Dialog: Focus editor when modal opens 3. Dynamic Transitions: Move focus between editor and other controls 4. Accessibility: Ensure focus management for keyboard navigation
---
FocusOutAsync
Removes focus from the Block Editor component. This method is useful for moving focus to other UI elements or handling focus transitions.
Syntax
public async Task FocusOutAsync()Example: Remove Focus (see FocusInAsync example above)
Use Cases
1. Blur on Save: Remove focus after saving 2. Form Navigation: Move focus to next form field 3. Dialog Dismissal: Remove focus when closing overlay 4. State Management: Track focus state in application
---
PrintAsync
Prints the entire Block Editor content. This method opens the browser's print dialog to print the document.
Syntax
public async Task PrintAsync()Example: Print Editor Content
@page "/print-document"
@rendermode InteractiveAuto
@using Syncfusion.Blazor.BlockEditor
<div style="margin-bottom: 20px;">
<button class="btn btn-success" @onclick="PrintContent">Print Document</button>
<button class="btn btn-info" @onclick="PrintAsHtml">Export as HTML</button>
</div>
<div style="height: 500px; border: 1px solid #ddd; border-radius: 4px;">
<SfBlockEditor @ref="editorRef" @bind-Blocks="blockData">
<BlockEditorCommandMenu></BlockEditorCommandMenu>
</SfBlockEditor>
</div>
@code {
private SfBlockEditor editorRef;
private List<BlockModel> blockData = new();
protected override void OnInitialized()
{
blockData = new()
{
new BlockModel { BlockType = BlockType.Heading, Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Document Title" } } },
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "This is a sample document that can be printed." } } },
new BlockModel { BlockType = BlockType.BulletList,
Content = new() {
new ContentModel { ContentType = ContentType.Text, Content = "Point 1" },
new ContentModel { ContentType = ContentType.Text, Content = "Point 2" },
new ContentModel { ContentType = ContentType.Text, Content = "Point 3" }
}
}
};
}
private async Task PrintContent()
{
await editorRef.PrintAsync();
}
private async Task PrintAsHtml()
{
// Export as HTML instead of printing
var html = await editorRef.GetDataAsHtml(null);
Console.WriteLine($"HTML Content: {html}");
}
}Use Cases
1. Document Printing: Print final document to PDF or paper 2. Print Preview: Allow users to preview before printing 3. Export Workflow: Generate printable version of content 4. Report Generation: Print reports created in editor
---
EnableToolbarItemsAsync
Enables one or more toolbar items by their indices. This allows dynamic control of toolbar availability based on application state.
Syntax
public async Task EnableToolbarItemsAsync(List<int> items)Parameters
- items (
List<int>): List of toolbar item indices to enable
Example: Enable/Disable Toolbar Items
@page "/toolbar-control"
@rendermode InteractiveAuto
@using Syncfusion.Blazor.BlockEditor
<div style="margin-bottom: 20px;">
<div class="btn-group" role="group">
<button class="btn btn-outline-primary" @onclick="@(() => ToggleFormatting(true))">
Enable Formatting
</button>
<button class="btn btn-outline-danger" @onclick="@(() => ToggleFormatting(false))">
Disable Formatting
</button>
</div>
</div>
<div style="margin-bottom: 10px; padding: 10px; background-color: #f0f0f0; border-radius: 4px;">
<strong>Status:</strong> @statusMessage
</div>
<div style="height: 400px; border: 1px solid #ddd; border-radius: 4px;">
<SfBlockEditor @ref="editorRef" @bind-Blocks="blockData">
<BlockEditorInlineToolbar Enable="true"></BlockEditorInlineToolbar>
</SfBlockEditor>
</div>
@code {
private SfBlockEditor editorRef;
private List<BlockModel> blockData = new();
private string statusMessage = "All formatting enabled";
protected override void OnInitialized()
{
blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Select text to see toolbar options" } } }
};
}
private async Task ToggleFormatting(bool enable)
{
// Toolbar item indices: 0=Bold, 1=Italic, 2=Underline, etc.
var formattingItems = new List<int> { 0, 1, 2, 3, 4, 5 };
if (enable)
{
await editorRef.EnableToolbarItemsAsync(formattingItems);
statusMessage = "All formatting enabled";
}
else
{
await editorRef.DisableToolbarItemsAsync(formattingItems);
statusMessage = "Formatting disabled";
}
}
}Common Toolbar Item Indices
| Index | Action | Description |
|---|---|---|
| 0 | Bold | Bold text formatting |
| 1 | Italic | Italic text formatting |
| 2 | Underline | Underline text formatting |
| 3 | StrikeThrough | Strikethrough text |
| 4 | TextColor | Change text color |
| 5 | BackgroundColor | Change background color |
Use Cases
1. Role-Based Permissions: Enable/disable features based on user role 2. Workflow Stages: Disable editing in finalized documents 3. Conditional Features: Enable features only when appropriate 4. User Level Control: Restrict advanced formatting for basic users
---
DisableToolbarItemsAsync
Disables one or more toolbar items by their indices. This prevents users from using specific formatting or editing tools.
Syntax
public async Task DisableToolbarItemsAsync(List<int> items)Parameters
- items (
List<int>): List of toolbar item indices to disable
Example: Disable Specific Toolbar Items
@page "/restrict-formatting"
@rendermode InteractiveAuto
@using Syncfusion.Blazor.BlockEditor
<div style="margin-bottom: 20px;">
<button class="btn btn-warning" @onclick="RestrictFormatting">Restrict to Basic Formatting</button>
</div>
<div style="height: 400px; border: 1px solid #ddd; border-radius: 4px;">
<SfBlockEditor @ref="editorRef" @bind-Blocks="blockData">
<BlockEditorInlineToolbar Enable="true"></BlockEditorInlineToolbar>
</SfBlockEditor>
</div>
@code {
private SfBlockEditor editorRef;
private List<BlockModel> blockData = new();
protected override void OnInitialized()
{
blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Content with restricted formatting" } } }
};
}
private async Task RestrictFormatting()
{
// Disable advanced formatting items (indices 3-5)
var advancedItems = new List<int> { 3, 4, 5 }; // StrikeThrough, TextColor, BackgroundColor
await editorRef.DisableToolbarItemsAsync(advancedItems);
}
}Use Cases
1. Content Templates: Disable features not needed for template 2. Compliance: Remove formatting options for compliance requirements 3. Simple Editors: Restrict to basic formatting for casual users 4. Document Types: Disable features based on document type
---
Complete Advanced Workflow Example
@page "/advanced-workflow"
@rendermode InteractiveAuto
@using Syncfusion.Blazor.BlockEditor
<div class="card" style="margin-bottom: 20px;">
<div class="card-header">
<h5>Advanced Editor Control Panel</h5>
</div>
<div class="card-body">
<div class="btn-group mb-2" role="group">
<button class="btn btn-primary" @onclick="SelectAllBlocks">Select All</button>
<button class="btn btn-info" @onclick="GetSelection">Get Selection</button>
<button class="btn btn-secondary" @onclick="FocusEditor">Focus</button>
<button class="btn btn-success" @onclick="PrintDocument">Print</button>
</div>
<div style="padding: 10px; background-color: #f5f5f5; border-radius: 4px;">
<strong>Info:</strong> @infoMessage
</div>
</div>
</div>
<div style="height: 500px; border: 1px solid #ddd; border-radius: 4px;">
<SfBlockEditor @ref="editorRef" @bind-Blocks="blockData">
<BlockEditorCommandMenu></BlockEditorCommandMenu>
</SfBlockEditor>
</div>
@code {
private SfBlockEditor editorRef;
private List<BlockModel> blockData = new();
private string infoMessage = "Ready to use advanced methods";
protected override void OnInitialized()
{
blockData = new()
{
new BlockModel { BlockType = BlockType.Heading, Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Advanced Methods Demo" } } },
new BlockModel { BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Use the buttons above to control the editor programmatically." } } }
};
}
private async Task SelectAllBlocks()
{
await editorRef.SelectAllBlocksAsync();
infoMessage = "All blocks selected";
}
private async Task GetSelection()
{
var selected = await editorRef.GetSelectedBlocksAsync();
infoMessage = $"{selected.Count} block(s) selected";
}
private async Task FocusEditor()
{
await editorRef.FocusInAsync();
infoMessage = "Editor focused - start typing";
}
private async Task PrintDocument()
{
await editorRef.PrintAsync();
infoMessage = "Print dialog opened";
}
}---
Best Practices
1. Async Handling: Always await these methods to ensure completion 2. State Tracking: Track editor state when using programmatic methods 3. User Feedback: Show loading indicators for operations 4. Error Handling: Wrap in try-catch for production code 5. Performance: Batch operations when possible to avoid frequent updates
---
Common Patterns
Pattern 1: Batch Block Operations
// Get selected, modify, and update
var selected = await editor.GetSelectedBlocksAsync();
foreach (var block in selected)
{
// Modify block properties
await editor.UpdateBlockAsync(block.ID, block);
}Pattern 2: Keyboard Shortcut Handling
// Intercept Ctrl+A for custom handling
if (keyCode == "CtrlA")
{
await editor.SelectAllBlocksAsync();
await editor.FocusInAsync();
}Pattern 3: Dynamic Toolbar Control
// Enable/disable based on user role
if (userRole == "Viewer")
{
await editor.DisableToolbarItemsAsync(new List<int> { 0, 1, 2, 3, 4, 5 });
}---
Last Updated: March 24, 2026 Component Version: 1.0.0+ Blazor: .NET 8.0+
Appearance and Styling in Syncfusion Blazor Block Editor
Setting Width and Height
Control the editor's display dimensions using Width and Height properties. These can be specified in pixels, percentages, viewport units, or relative units.
Examples of Size Configuration
Percentage-based (responsive):
<SfBlockEditor Width="100%" Height="80vh"></SfBlockEditor>Pixel-based (fixed):
<SfBlockEditor Width="900px" Height="600px"></SfBlockEditor>Mixed units:
<SfBlockEditor Width="100%" Height="400px"></SfBlockEditor>Container-relative:
<div style="display: flex; height: 100vh;">
<SfBlockEditor Width="100%" Height="100%"></SfBlockEditor>
</div>Responsive Design Pattern
Create an editor that adapts to screen size:
@page "/responsive-editor"
@rendermode InteractiveAuto
<div style="display: flex; flex-direction: column; height: 100vh; padding: 20px;">
<header style="flex-shrink: 0; margin-bottom: 15px;">
<h1>Responsive Document Editor</h1>
</header>
<main style="flex-grow: 1; overflow: hidden;">
<SfBlockEditor @bind-Blocks="blockData"
Width="100%"
Height="100%"
EnableDragAndDrop="true">
</SfBlockEditor>
</main>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Responsive editor content..." } }
}
};
}Read-Only Mode
Use the ReadOnly property to make the editor display-only. This is ideal for document preview, approval workflows, or archived content viewing.
Enable Read-Only Mode
<SfBlockEditor @bind-Blocks="blockData" ReadOnly="true"></SfBlockEditor>When ReadOnly="true":
- Users can view and select content
- Editing, pasting, and block manipulation are disabled
- Menus and toolbars are hidden
- Copy, cut operations work; paste operations are blocked
Toggle Read-Only Dynamically
@page "/editor-with-toggle"
@rendermode InteractiveAuto
<div style="padding: 20px;">
<div style="margin-bottom: 15px;">
<label>
<input type="checkbox" @onchange="@OnToggleReadOnly" checked="@isReadOnly" />
Read-Only Mode
</label>
</div>
<SfBlockEditor @bind-Blocks="blockData" ReadOnly="@isReadOnly"></SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Document Preview" }
}
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Toggle read-only mode using the checkbox above." }
}
}
};
private bool isReadOnly = false;
private void OnToggleReadOnly(ChangeEventArgs e)
{
isReadOnly = (bool)e.Value;
}
}Read-Only Use Cases
| Use Case | Configuration |
|---|---|
| Document Preview | ReadOnly="true" |
| Published Content | ReadOnly="true" |
| Approval Review | Dynamic toggle on approval |
| Draft vs. Final | Toggle based on status |
| Template Display | ReadOnly="true" with initial content |
Custom CSS Classes
Apply custom styling using the CssClass property. This allows you to add one or more CSS classes for targeted styling.
Single CSS Class
<SfBlockEditor @bind-Blocks="blockData" CssClass="dark-theme"></SfBlockEditor>
<style>
.dark-theme {
background-color: #1e1e1e;
color: #ffffff;
}
.dark-theme .e-block {
border-color: #333333;
}
.dark-theme .e-block-content {
color: #e0e0e0;
}
</style>Multiple CSS Classes
<SfBlockEditor @bind-Blocks="blockData" CssClass="modern-editor compact-view"></SfBlockEditor>
<style>
.modern-editor {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.compact-view .e-block {
margin-bottom: 8px;
padding: 10px;
}
.compact-view .e-block-content {
font-size: 14px;
line-height: 1.5;
}
</style>Theme Customization
Available Built-In Themes
Include one theme CSS file in App.razor:
<!-- Bootstrap 5 (Default) -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<!-- Material Design -->
<link href="_content/Syncfusion.Blazor.Themes/material.css" rel="stylesheet" />
<!-- Material Dark -->
<link href="_content/Syncfusion.Blazor.Themes/material-dark.css" rel="stylesheet" />
<!-- Tailwind CSS -->
<link href="_content/Syncfusion.Blazor.Themes/tailwind.css" rel="stylesheet" />
<!-- Fluent Design -->
<link href="_content/Syncfusion.Blazor.Themes/fluent.css" rel="stylesheet" />Custom Theme with CSS Variables
Override theme variables using CSS:
<style>
:root {
--e-block-editor-primary: #007bff;
--e-block-editor-primary-light: #e7f1ff;
--e-block-editor-border: #dee2e6;
--e-block-editor-text: #212529;
--e-block-editor-bg: #ffffff;
}
.custom-theme-editor {
background-color: var(--e-block-editor-bg);
color: var(--e-block-editor-text);
border: 1px solid var(--e-block-editor-border);
}
.custom-theme-editor .e-block {
background-color: var(--e-block-editor-bg);
border-left: 3px solid var(--e-block-editor-primary);
padding: 12px;
margin-bottom: 8px;
}
.custom-theme-editor .e-block:hover {
background-color: var(--e-block-editor-primary-light);
}
</style>Complete Styling Example
@page "/styled-editor"
@rendermode InteractiveAuto
<div class="editor-wrapper">
<header class="editor-header">
<h1>Professional Document Editor</h1>
<p>Create and edit structured content with full styling control</p>
</header>
<div class="editor-controls">
<label>
<input type="checkbox" @onchange="@OnToggleReadOnly" checked="@isReadOnly" />
Preview Mode (Read-Only)
</label>
<button class="btn" @onclick="@OnChangeTheme">Change Theme</button>
</div>
<SfBlockEditor @bind-Blocks="blockData"
Width="100%"
Height="600px"
ReadOnly="@isReadOnly"
CssClass="@cssClasses"
EnableDragAndDrop="true">
</SfBlockEditor>
<footer class="editor-footer">
<p>Block Count: @blockData.Count | Theme: @currentTheme</p>
</footer>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Document Title" }
}
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Professional styled content here..." }
}
}
};
private bool isReadOnly = false;
private string currentTheme = "light";
private string cssClasses = "light-theme professional-styling";
private void OnToggleReadOnly(ChangeEventArgs e)
{
isReadOnly = (bool)e.Value;
}
private void OnChangeTheme()
{
currentTheme = currentTheme == "light" ? "dark" : "light";
cssClasses = $"{currentTheme}-theme professional-styling";
}
}
<style>
.editor-wrapper {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f8f9fa;
}
.editor-header {
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
}
.editor-header h1 {
margin: 0 0 8px 0;
font-size: 28px;
}
.editor-header p {
margin: 0;
font-size: 14px;
opacity: 0.9;
}
.editor-controls {
padding: 15px 20px;
background-color: white;
border-bottom: 1px solid #dee2e6;
display: flex;
gap: 15px;
align-items: center;
}
.editor-controls label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.btn {
padding: 8px 16px;
background-color: #667eea;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.btn:hover {
background-color: #764ba2;
}
.editor-footer {
padding: 12px 20px;
background-color: white;
border-top: 1px solid #dee2e6;
text-align: center;
font-size: 12px;
color: #6c757d;
}
/* Light Theme */
.light-theme {
background-color: #ffffff;
color: #212529;
}
.light-theme .e-block {
border: 1px solid #dee2e6;
background-color: #ffffff;
}
/* Dark Theme */
.dark-theme {
background-color: #1e1e1e;
color: #e0e0e0;
}
.dark-theme .e-block {
border: 1px solid #333333;
background-color: #2d2d2d;
}
.dark-theme .e-block-content {
color: #e0e0e0;
}
/* Professional Styling */
.professional-styling .e-block {
border-radius: 6px;
margin-bottom: 12px;
padding: 12px;
transition: all 0.2s ease;
}
.professional-styling .e-block:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.professional-styling .e-block-content {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
}
</style>Responsive Grid Example
Build an editor that adapts to different screen sizes:
<style>
@media (max-width: 768px) {
.editor-wrapper {
padding: 10px;
}
.editor-header {
padding: 15px;
}
.editor-header h1 {
font-size: 20px;
}
.editor-controls {
flex-direction: column;
align-items: flex-start;
}
.light-theme .e-block {
padding: 8px;
margin-bottom: 8px;
}
.light-theme .e-block-content {
font-size: 14px;
}
}
</style>Built-In Blocks in Syncfusion Blazor Block Editor
Overview of Block Types
The Block Editor provides a comprehensive set of block types for creating structured, modular content. Each block type serves a specific purpose and can be combined to build complex documents.
Available Block Types
| Block Type | Purpose | Properties | Example Use |
|---|---|---|---|
| Paragraph | Standard text content | None | Regular body text |
| Heading | Section/subsection titles | Level (1-4) | Document structure |
| BulletList | Unordered items | None | Lists, bullet points |
| NumberedList | Ordered items | None | Numbered steps, sequences |
| Checklist | Checkable items | None | Tasks, to-do lists |
| Quote | Citation/emphasis blocks | None | Testimonials, quotes |
| CodeBlock | Formatted code | Language | Code samples |
| Table | Data in rows/columns | Rows, Columns | Data presentation |
| Image | Media content | Source URL, Alt text | Illustrations, photos |
| Divider | Horizontal separator | None | Visual separation |
| Callout | Highlighted information | Type (info, warning, success, error) | Important notices |
| Toggle | Collapsible content | None | Expandable sections |
Heading Blocks
Create document structure with hierarchical headings.
Heading Levels
@rendermode InteractiveAuto
<SfBlockEditor @bind-Blocks="headingData"></SfBlockEditor>
@code {
private List<BlockModel> headingData = new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Heading Level 1" } }
},
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 2 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Heading Level 2" } }
},
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 3 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Heading Level 3" } }
},
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 4 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Heading Level 4" } }
}
};
}Paragraph Blocks
Basic text content blocks for body text and descriptions.
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "This is a paragraph with basic text content." },
new ContentModel { ContentType = ContentType.Text, Content = " Additional text in same paragraph." }
}
}List Types
Bullet Lists (Unordered)
new BlockModel
{
BlockType = BlockType.BulletList,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "First bullet point" },
new ContentModel { ContentType = ContentType.Text, Content = "Second bullet point" },
new ContentModel { ContentType = ContentType.Text, Content = "Third bullet point" }
}
}Numbered Lists (Ordered)
new BlockModel
{
BlockType = BlockType.NumberedList,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "First step" },
new ContentModel { ContentType = ContentType.Text, Content = "Second step" },
new ContentModel { ContentType = ContentType.Text, Content = "Third step" }
}
}Checklist
Interactive checkable items:
new BlockModel
{
BlockType = BlockType.Checklist,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Task one" },
new ContentModel { ContentType = ContentType.Text, Content = "Task two" },
new ContentModel { ContentType = ContentType.Text, Content = "Task three" }
}
}Quote Blocks
Styled blocks for citations and emphasizing important text:
new BlockModel
{
BlockType = BlockType.Quote,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "\"The only way to do great work is to love what you do.\" - Steve Jobs" }
}
}Code Blocks
Display formatted code with syntax highlighting:
new BlockModel
{
BlockType = BlockType.CodeBlock,
Properties = new CodeBlockSettings { Language = "csharp" },
Content = new()
{
new ContentModel
{
ContentType = ContentType.Text,
Content = "public class Example {\n public static void Main() {\n Console.WriteLine(\"Hello, World!\");\n }\n}"
}
}
}Supported Languages
- csharp
- javascript
- typescript
- python
- java
- html
- css
- sql
- json
- xml
- bash
- etc.
Table Blocks
Display tabular data:
new BlockModel
{
BlockType = BlockType.Table,
Properties = new TableBlockSettings
{
Rows = 3,
Columns = 3,
HeaderRow = true
},
Content = new()
{
// Row 1: Headers
new ContentModel { ContentType = ContentType.Text, Content = "Name" },
new ContentModel { ContentType = ContentType.Text, Content = "Email" },
new ContentModel { ContentType = ContentType.Text, Content = "Status" },
// Row 2: Data
new ContentModel { ContentType = ContentType.Text, Content = "John Doe" },
new ContentModel { ContentType = ContentType.Text, Content = "john@example.com" },
new ContentModel { ContentType = ContentType.Text, Content = "Active" },
// Row 3: Data
new ContentModel { ContentType = ContentType.Text, Content = "Jane Smith" },
new ContentModel { ContentType = ContentType.Text, Content = "jane@example.com" },
new ContentModel { ContentType = ContentType.Text, Content = "Active" }
}
}Image Blocks
Embed media content:
new BlockModel
{
BlockType = BlockType.Image,
Properties = new ImageBlockSettings
{
Src = "https://example.com/image.jpg",
Alt = "Example Image",
Caption = "Image caption text"
}
}Image Sizing Configuration
Control image dimensions and resizing behavior with advanced sizing properties:
new BlockModel
{
BlockType = BlockType.Image,
Properties = new ImageBlockSettings
{
Src = "https://example.com/image.jpg",
Alt = "Example Image",
Caption = "Image caption text",
EnableResize = true, // Allow users to resize images
Width = "100%", // Display width (pixels, percent, auto)
Height = "auto", // Display height (pixels, percent, auto)
MinWidth = "200px", // Minimum width constraint
MaxWidth = "800px", // Maximum width constraint
MinHeight = "150px", // Minimum height constraint
MaxHeight = "600px" // Maximum height constraint
}
}Image Sizing Properties
| Property | Type | Default | Description |
|---|---|---|---|
EnableResize | bool | false | Enable/disable user image resizing |
Width | string | "auto" | Display width (px, %, auto, inherit) |
Height | string | "auto" | Display height (px, %, auto, inherit) |
MinWidth | string | null | Minimum width when resizing |
MaxWidth | string | null | Maximum width when resizing |
MinHeight | string | null | Minimum height when resizing |
MaxHeight | string | null | Maximum height when resizing |
Sizing Examples
Responsive Full-Width Image:
new BlockModel
{
BlockType = BlockType.Image,
Properties = new ImageBlockSettings
{
Src = "https://example.com/banner.jpg",
Width = "100%",
Height = "auto", // Maintains aspect ratio
MaxWidth = "1200px" // Limit maximum width
}
}Fixed Size Image:
new BlockModel
{
BlockType = BlockType.Image,
Properties = new ImageBlockSettings
{
Src = "https://example.com/thumbnail.jpg",
Width = "300px",
Height = "300px",
EnableResize = false // Prevent resizing
}
}Resizable Image with Constraints:
new BlockModel
{
BlockType = BlockType.Image,
Properties = new ImageBlockSettings
{
Src = "https://example.com/photo.jpg",
Width = "500px",
Height = "auto",
EnableResize = true,
MinWidth = "200px",
MaxWidth = "800px",
MinHeight = "150px",
MaxHeight = "600px"
}
}Best Practices for Image Sizing
1. Maintain Aspect Ratio: Set height to "auto" to preserve image proportions 2. Responsive Design: Use percentage width with maximum width constraints for responsive layouts 3. Performance: Limit maximum dimensions to prevent memory issues 4. User Control: Use EnableResize = true for user-friendly interfaces 5. Constraints: Always set both Min and Max constraints when enabling resize 6. Mobile Optimization: Use smaller max dimensions for mobile-friendly layouts 7. Accessibility: Always provide descriptive Alt text for accessibility
Complete Image Block Example
@page "/image-sizing"
@rendermode InteractiveAuto
@using Syncfusion.Blazor.BlockEditor
<SfBlockEditor @bind-Blocks="imageData"></SfBlockEditor>
@code {
private List<BlockModel> imageData = new()
{
// Heading
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Image Gallery" } }
},
// Responsive banner
new BlockModel
{
BlockType = BlockType.Image,
Properties = new ImageBlockSettings
{
Src = "https://example.com/banner.jpg",
Alt = "Page Banner",
Caption = "Responsive banner - scales with screen",
Width = "100%",
Height = "auto",
MaxWidth = "1200px",
EnableResize = false
}
},
// Resizable thumbnail
new BlockModel
{
BlockType = BlockType.Image,
Properties = new ImageBlockSettings
{
Src = "https://example.com/thumbnail.jpg",
Alt = "Resizable Image",
Caption = "Drag edges to resize (200-800px)",
Width = "400px",
Height = "auto",
MinWidth = "200px",
MaxWidth = "800px",
EnableResize = true
}
},
// Gallery images
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Gallery:" } }
}
};
}Divider Blocks
Add horizontal separators for visual organization:
new BlockModel
{
BlockType = BlockType.Divider
}Callout Blocks
Highlighted information blocks with different types:
new BlockModel
{
BlockType = BlockType.Callout,
Properties = new CalloutBlockSettings
{
CalloutType = "info" // info, warning, success, error
},
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "This is an important notification." }
}
}Callout Types
info: Blue informational calloutwarning: Yellow warning calloutsuccess: Green success callouterror: Red error callout
Toggle (Collapsible) Blocks
Create expandable content sections:
new BlockModel
{
BlockType = BlockType.Toggle,
Properties = new ToggleBlockSettings
{
Title = "Click to expand"
},
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Hidden content that expands on click" }
}
}Block Nesting and Hierarchy
Blocks can be nested to create hierarchical structures:
@rendermode InteractiveAuto
<SfBlockEditor @bind-Blocks="nestedData"></SfBlockEditor>
@code {
private List<BlockModel> nestedData = new()
{
// Parent heading
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Main Section" } }
},
// Nested content under heading
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Introduction paragraph" } }
},
new BlockModel
{
BlockType = BlockType.BulletList,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Point 1" },
new ContentModel { ContentType = ContentType.Text, Content = "Point 2" }
}
},
// Subsection
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 2 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Subsection" } }
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Subsection content" } }
}
};
}Complete Document Structure Example
@page "/document-structure"
@rendermode InteractiveAuto
<SfBlockEditor @bind-Blocks="documentBlocks" EnableDragAndDrop="true"></SfBlockEditor>
@code {
private List<BlockModel> documentBlocks = new()
{
// Title
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Document Title" } }
},
// Introduction
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "This document demonstrates all block types." } }
},
// Info callout
new BlockModel
{
BlockType = BlockType.Callout,
Properties = new CalloutBlockSettings { CalloutType = "info" },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Key information for readers" } }
},
// Section with list
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 2 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Features" } }
},
new BlockModel
{
BlockType = BlockType.BulletList,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Feature one" },
new ContentModel { ContentType = ContentType.Text, Content = "Feature two" }
}
},
// Steps section
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 2 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Steps" } }
},
new BlockModel
{
BlockType = BlockType.NumberedList,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "First step" },
new ContentModel { ContentType = ContentType.Text, Content = "Second step" }
}
},
// Divider
new BlockModel { BlockType = BlockType.Divider },
// Quote
new BlockModel
{
BlockType = BlockType.Quote,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "\"Success is the sum of small efforts repeated day in and day out.\"" } }
},
// Toggle for additional info
new BlockModel
{
BlockType = BlockType.Toggle,
Properties = new ToggleBlockSettings { Title = "Advanced Options" },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Additional detailed information here" } }
},
// Empty paragraph for user input
new BlockModel { BlockType = BlockType.Paragraph }
};
}Custom Block Properties
Extend blocks with additional properties:
// Block with custom formatting
new BlockModel
{
BlockType = BlockType.Paragraph,
Properties = new ParagraphBlockSettings
{
Alignment = "center",
BackgroundColor = "#f0f0f0"
},
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Centered paragraph with background" } }
}Block Content Model
Each block contains a list of ContentModel items:
public class ContentModel
{
public ContentType ContentType { get; set; } // Text, Link, Image, etc.
public string Content { get; set; }
public TextContentSettings Properties { get; set; } // Bold, Italic, Color, etc.
}Text Content with Styling
new ContentModel
{
ContentType = ContentType.Text,
Content = "Bold and italic text",
Properties = new TextContentSettings
{
Styles = new StyleModel
{
Bold = true,
Italic = true,
TextColor = "#ff0000"
}
}
}Best Practices
1. Hierarchy: Use appropriate heading levels (H1 → H2 → H3) 2. Lists: Use bullet/numbered lists for grouped items 3. Tables: Use tables for structured data presentation 4. Visual Separation: Use dividers between major sections 5. Callouts: Reserve for important information 6. Toggle Blocks: Use for optional/advanced content 7. Empty Blocks: Include at least one empty block for user input
---
Enums Reference
BlockType Enum
Represents the type of block in the editor. Each block type defines specific rendering and interaction behavior.
public enum BlockType
{
/// <summary>Standard text content block</summary>
Paragraph,
/// <summary>Section/subsection heading (use with HeadingBlockSettings Level)</summary>
Heading,
/// <summary>Unordered list items</summary>
BulletList,
/// <summary>Ordered/numbered list items</summary>
NumberedList,
/// <summary>Checklist with checkable items</summary>
Checklist,
/// <summary>Citation or quoted text block</summary>
Quote,
/// <summary>Code with syntax highlighting support</summary>
CodeBlock,
/// <summary>Tabular data with rows and columns</summary>
Table,
/// <summary>Image or media content</summary>
Image,
/// <summary>Horizontal visual separator</summary>
Divider,
/// <summary>Highlighted information box (info, warning, success, error)</summary>
Callout,
/// <summary>Expandable/collapsible content section</summary>
Toggle
}ContentType Enum
Represents the type of content within a block.
public enum ContentType
{
/// <summary>Plain text content</summary>
Text,
/// <summary>Hyperlink content</summary>
Link,
/// <summary>User mention content (e.g., @user)</summary>
Mention,
/// <summary>Label or tag content (e.g., #label)</summary>
Label
}SaveFormat Enum
Represents the format for saving image content.
public enum SaveFormat
{
/// <summary>Save as Blob reference (default)</summary>
Blob,
/// <summary>Save as Base64-encoded string</summary>
Base64
}BlockAction Enum
Represents actions available for block operations.
public enum BlockAction
{
/// <summary>Clone/duplicate a block</summary>
Duplicate,
/// <summary>Remove/delete a block</summary>
Delete,
/// <summary>Move a block up one position</summary>
MoveUp,
/// <summary>Move a block down one position</summary>
MoveDown
}TableColumnType Enum
Represents the type of column in a table block.
public enum TableColumnType
{
/// <summary>Text column - accepts any text input</summary>
Text,
/// <summary>Number column - numeric input only</summary>
Number,
/// <summary>Checkbox column - boolean values</summary>
Checkbox,
/// <summary>Dropdown column - predefined value selection</summary>
Dropdown
}Enum Usage Examples
Using BlockType:
new BlockModel
{
BlockType = BlockType.Heading, // Use enum value
Properties = new HeadingBlockSettings { Level = 1 }
}
// Check block type
if (block.BlockType == BlockType.CodeBlock)
{
// Handle code block
}Using ContentType:
new ContentModel
{
ContentType = ContentType.Text, // Plain text
Content = "Hello world"
}
new ContentModel
{
ContentType = ContentType.Link, // Hyperlink
Content = "Click here",
Properties = new LinkContentSettings { Url = "https://example.com" }
}Using TableColumnType:
// In table configuration
var tableSettings = new TableBlockSettings
{
Rows = 3,
Columns = 3,
ColumnTypes = new()
{
TableColumnType.Text, // First column: text
TableColumnType.Number, // Second column: numbers
TableColumnType.Checkbox // Third column: checkboxes
}
};---
Last Updated: March 24, 2026 Component Version: 1.0.0+
Content Handling in Syncfusion Blazor Block Editor
Getting and Setting Block Content
Access Blocks Programmatically
@rendermode InteractiveAuto
<div style="max-width: 900px; margin: 20px auto;">
<div style="padding: 15px; background-color: #f5f5f5; margin-bottom: 15px;">
<button @onclick="@GetContent">Get Content</button>
<button @onclick="@SetContent">Set New Content</button>
<button @onclick="@ClearContent">Clear All</button>
<div style="margin-top: 10px;">
<strong>Content:</strong>
<pre style="background-color: white; padding: 10px; border-radius: 4px;">@displayContent</pre>
</div>
</div>
<SfBlockEditor @bind-Blocks="blockData"></SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Sample Content" } }
}
};
private string displayContent = "";
private void GetContent()
{
displayContent = string.Join("\n", blockData.Select((b, i) => $"Block {i}: {b.BlockType}"));
}
private void SetContent()
{
blockData = new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "New Content Title" } }
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "This is new content..." } }
}
};
}
private void ClearContent()
{
blockData = new() { new BlockModel { BlockType = BlockType.Paragraph } };
}
}Content Model Structure
// Main block structure
public class BlockModel
{
public BlockType BlockType { get; set; }
public object Properties { get; set; } // HeadingBlockSettings, etc.
public List<ContentModel> Content { get; set; }
public string ID { get; set; }
}
// Content within a block
public class ContentModel
{
public ContentType ContentType { get; set; } // Text, Link, Image, etc.
public string Content { get; set; }
public TextContentSettings Properties { get; set; }
}
// Text content with formatting
public class TextContentSettings
{
public StyleModel Styles { get; set; } // Bold, Italic, Color, etc.
public string Link { get; set; }
}Paste Cleanup Configuration
Control how content is handled when pasted into the editor.
Configure Allowed Styles
Only specified CSS styles are preserved during paste:
<SfBlockEditor>
<BlockEditorPasteCleanup AllowedStyles="@(new string[]
{
"font-weight",
"font-style",
"text-decoration"
})">
</BlockEditorPasteCleanup>
</SfBlockEditor>Default Allowed Styles:
- font-weight
- font-style
- text-decoration
- text-transform
Set Denied Tags
Remove specific HTML tags from pasted content:
<SfBlockEditor>
<BlockEditorPasteCleanup DeniedTags="@(new string[]
{
"script",
"iframe",
"object",
"embed"
})">
</BlockEditorPasteCleanup>
</SfBlockEditor>Keep Format Configuration
Control whether formatting is preserved:
<!-- Keep formatting (default) -->
<BlockEditorPasteCleanup KeepFormat="true"></BlockEditorPasteCleanup>
<!-- Paste as plain text with no formatting -->
<BlockEditorPasteCleanup KeepFormat="false"></BlockEditorPasteCleanup>Plain Text Paste
Strip all HTML and styles, paste plain text only:
<SfBlockEditor>
<BlockEditorPasteCleanup PlainText="true"></BlockEditorPasteCleanup>
</SfBlockEditor>Complete Paste Cleanup Example
@page "/paste-cleanup-demo"
@rendermode InteractiveAuto
<div style="max-width: 900px; margin: 20px auto;">
<div style="padding: 15px; background-color: #f5f5f5; margin-bottom: 15px;">
<h2>Paste Cleanup Demo</h2>
<h3>Test Content:</h3>
<div contenteditable="true" style="padding: 10px; border: 1px solid #ccc; background-color: white; margin-bottom: 10px;">
<h2 style="color: red; font-weight: bold;">Formatted Heading</h2>
<p style="background-color: yellow; font-style: italic;">
This is a <span style="font-weight: bold;">bold paragraph</span> with
<span style="color: blue; font-style: italic;">italic text</span>
</p>
<script>console.log('This script will be removed');</script>
</div>
<p style="font-size: 12px; color: #666;">Copy from above and paste into the editor below</p>
</div>
<h3>Block Editor (with Paste Cleanup):</h3>
<SfBlockEditor @bind-Blocks="blockData"
Height="400px"
PasteCleanupCompleted="@OnPasteComplete">
<BlockEditorPasteCleanup AllowedStyles="@(new string[] { "text-decoration" })"
DeniedTags="@(new string[] { "script", "iframe" })">
</BlockEditorPasteCleanup>
</SfBlockEditor>
<div style="margin-top: 15px; padding: 10px; background-color: #f0f7ff; border-radius: 4px;">
<strong>Settings:</strong> Allow only text-decoration style, remove script/iframe tags
</div>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private void OnPasteComplete(PasteCleanupCompletedEventArgs args)
{
Console.WriteLine($"Paste completed. Content length: {args.Content?.Length}");
}
}Paste Lifecycle Events
PasteCleanupStarting Event
Triggers before content is pasted. Can inspect, modify, or cancel:
<SfBlockEditor PasteCleanupStarting="@OnPasteStarting">
</SfBlockEditor>
@code {
private void OnPasteStarting(PasteCleanupStartingEventArgs args)
{
// args.Content contains pasted content
// Set args.Cancel = true to prevent paste
if (ContainsProhibitedContent(args.Content))
{
args.Cancel = true;
}
}
private bool ContainsProhibitedContent(string content)
{
return content.Contains("<script>") || content.Contains("javascript:");
}
}PasteCleanupCompleted Event
Triggers after content is successfully pasted:
<SfBlockEditor PasteCleanupCompleted="@OnPasteCompleted">
</SfBlockEditor>
@code {
private void OnPasteCompleted(PasteCleanupCompletedEventArgs args)
{
// args.Content contains final pasted content
// Perform post-paste operations
LogPasteAction(args.Content);
}
private void LogPasteAction(string content)
{
Console.WriteLine($"Content pasted: {content.Length} characters");
}
}Security Best Practices
Prevent XSS (Cross-Site Scripting)
<SfBlockEditor>
<!-- Block dangerous tags -->
<BlockEditorPasteCleanup DeniedTags="@(new string[]
{
"script",
"iframe",
"object",
"embed",
"style",
"link"
})">
</BlockEditorPasteCleanup>
</SfBlockEditor>Sanitize HTML Content
@rendermode InteractiveAuto
<SfBlockEditor PasteCleanupStarting="@OnValidatePaste">
</SfBlockEditor>
@code {
private void OnValidatePaste(PasteCleanupStartingEventArgs args)
{
// Validate and sanitize content before allowing paste
if (!IsContentSafe(args.Content))
{
args.Cancel = true;
Console.WriteLine("Paste blocked: Content contains potential security risks");
}
}
private bool IsContentSafe(string content)
{
// Check for dangerous patterns
var dangersPatterns = new[]
{
"javascript:",
"onerror=",
"onload=",
"<script",
"eval(",
"expression("
};
return !dangersPatterns.Any(p => content.Contains(p, StringComparison.OrdinalIgnoreCase));
}
}Content Validation
Validate on Block Change
@rendermode InteractiveAuto
<div style="max-width: 900px; margin: 20px auto;">
<div style="padding: 15px; background-color: #f5f5f5; margin-bottom: 15px;">
<h3>Validation Status: @validationStatus</h3>
<div style="color: @(isValid ? "green" : "red");">
@validationMessage
</div>
</div>
<SfBlockEditor @bind-Blocks="blockData"
BlockChanged="@OnBlockChanged">
</SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private string validationStatus = "Valid";
private string validationMessage = "No errors";
private bool isValid = true;
private void OnBlockChanged(BlockChangedEventArgs args)
{
ValidateContent();
}
private void ValidateContent()
{
isValid = true;
validationMessage = "";
// Check block count
if (blockData.Count > 100)
{
isValid = false;
validationMessage += "Too many blocks. ";
}
// Check total content length
int totalLength = blockData.Sum(b => b.Content?.Sum(c => c.Content?.Length ?? 0) ?? 0);
if (totalLength > 50000)
{
isValid = false;
validationMessage += "Content exceeds maximum length. ";
}
// Check for empty required content
var hasContent = blockData.Any(b => b.Content?.Any(c => !string.IsNullOrEmpty(c.Content)) ?? false);
if (!hasContent)
{
isValid = false;
validationMessage = "Content cannot be empty.";
}
validationStatus = isValid ? "Valid ✓" : "Invalid ✗";
if (isValid)
validationMessage = "Content is valid and ready to save.";
}
}Export Content
Export as JSON
@rendermode InteractiveAuto
<button @onclick="@ExportAsJson">Export as JSON</button>
@code {
private List<BlockModel> blockData = new();
private void ExportAsJson()
{
var json = System.Text.Json.JsonSerializer.Serialize(blockData);
Console.WriteLine(json);
// Save to file or send to server
}
}Export as HTML
@code {
private string ExportAsHtml()
{
var html = new StringBuilder();
html.AppendLine("<html><body>");
foreach (var block in blockData)
{
html.Append(ConvertBlockToHtml(block));
}
html.AppendLine("</body></html>");
return html.ToString();
}
private string ConvertBlockToHtml(BlockModel block)
{
return block.BlockType switch
{
BlockType.Heading => $"<h{GetHeadingLevel(block)}>{GetBlockText(block)}</h{GetHeadingLevel(block)}>",
BlockType.Paragraph => $"<p>{GetBlockText(block)}</p>",
BlockType.BulletList => ConvertListToHtml(block, "ul"),
BlockType.NumberedList => ConvertListToHtml(block, "ol"),
_ => GetBlockText(block)
};
}
private string GetBlockText(BlockModel block)
{
return string.Concat(block.Content?.Select(c => c.Content) ?? Array.Empty<string>());
}
private int GetHeadingLevel(BlockModel block)
{
if (block.Properties is HeadingBlockSettings heading)
return heading.Level;
return 1;
}
private string ConvertListToHtml(BlockModel block, string listType)
{
var items = block.Content?.Select(c => $"<li>{c.Content}</li>") ?? Array.Empty<string>();
return $"<{listType}>{string.Concat(items)}</{listType}>";
}
}Import Content
Load from JSON
@code {
private async Task LoadFromJson(string json)
{
try
{
blockData = System.Text.Json.JsonSerializer.Deserialize<List<BlockModel>>(json);
}
catch (Exception ex)
{
Console.WriteLine($"Error loading JSON: {ex.Message}");
}
}
}Load from HTML
@code {
private List<BlockModel> ConvertHtmlToBlocks(string html)
{
var blocks = new List<BlockModel>();
// Parse HTML and convert to blocks
// This is simplified; real implementation would use HTML parser
if (html.Contains("<h1>"))
{
blocks.Add(new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Heading" } }
});
}
return blocks;
}
}Complete Content Handling Example
@page "/content-handling-complete"
@rendermode InteractiveAuto
<div style="max-width: 1000px; margin: 20px auto;">
<div style="display: flex; gap: 20px;">
<!-- Left panel: Content management -->
<div style="flex: 0 0 200px; padding: 15px; background-color: #f5f5f5; border-radius: 4px;">
<h3>Content Management</h3>
<button @onclick="@GetBlockData" style="display: block; width: 100%; padding: 8px; margin-bottom: 8px;">Get Data</button>
<button @onclick="@ExportJson" style="display: block; width: 100%; padding: 8px; margin-bottom: 8px;">Export JSON</button>
<button @onclick="@ClearAll" style="display: block; width: 100%; padding: 8px;">Clear</button>
<div style="margin-top: 15px;">
<strong>Status:</strong> @blockData.Count blocks
</div>
</div>
<!-- Right panel: Editor -->
<div style="flex: 1;">
<SfBlockEditor @bind-Blocks="blockData"
Height="500px"
BlockChanged="@OnBlockChanged"
PasteCleanupStarting="@OnPasteStarting">
<BlockEditorPasteCleanup AllowedStyles="@(new string[] { "font-weight", "font-style" })"
DeniedTags="@(new string[] { "script", "iframe" })">
</BlockEditorPasteCleanup>
</SfBlockEditor>
</div>
</div>
<!-- Output display -->
<div style="margin-top: 20px; padding: 15px; background-color: #f0f7ff; border-radius: 4px;">
<strong>Last Action:</strong> @lastAction
</div>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private string lastAction = "Ready";
private void GetBlockData()
{
lastAction = $"Retrieved {blockData.Count} blocks";
}
private void ExportJson()
{
var json = System.Text.Json.JsonSerializer.Serialize(blockData, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
lastAction = "Exported as JSON";
Console.WriteLine(json);
}
private void ClearAll()
{
blockData = new() { new BlockModel { BlockType = BlockType.Paragraph } };
lastAction = "Content cleared";
}
private void OnBlockChanged(BlockChangedEventArgs args)
{
lastAction = $"Content updated at {DateTime.Now:HH:mm:ss}";
}
private void OnPasteStarting(PasteCleanupStartingEventArgs args)
{
if (args.Content?.Contains("<script>") ?? false)
{
args.Cancel = true;
lastAction = "Paste blocked: Script tags detected";
}
}
}Drag-Drop and Undo-Redo in Syncfusion Blazor Block Editor
Drag and Drop
Enable intuitive block reordering through drag-and-drop functionality.
Enable Drag and Drop
By default, drag-and-drop is enabled. Use the EnableDragAndDrop property to control this behavior:
<!-- Enable (default) -->
<SfBlockEditor EnableDragAndDrop="true"></SfBlockEditor>
<!-- Disable -->
<SfBlockEditor EnableDragAndDrop="false"></SfBlockEditor>Single Block Dragging
Users can drag individual blocks by their drag handle (visible on hover):
@rendermode InteractiveAuto
<div style="max-width: 800px; margin: 20px auto;">
<h2>Drag and Drop Blocks</h2>
<p>Hover over a block and drag its handle to reorder</p>
<SfBlockEditor @bind-Blocks="blockData"
EnableDragAndDrop="true"
Height="400px">
</SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 1: Drag Me" } }
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "This is the first block - try dragging it" } }
},
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 2: Also Draggable" } }
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "This is the second block" } }
},
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 3: And This One" } }
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "This is the third block" } }
}
};
}Multiple Block Dragging
Select multiple blocks and drag them together:
@rendermode InteractiveAuto
<div style="max-width: 800px; margin: 20px auto;">
<h2>Multiple Block Selection and Dragging</h2>
<p>Select multiple blocks, then drag together</p>
<SfBlockEditor @bind-Blocks="blockData"
EnableDragAndDrop="true"
SelectionChanged="@OnSelectionChanged">
</SfBlockEditor>
<div style="margin-top: 20px; padding: 15px; background-color: #f5f5f5;">
<strong>Selected Blocks:</strong> @selectedCount
</div>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph, Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 1" } } },
new BlockModel { BlockType = BlockType.Paragraph, Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 2" } } },
new BlockModel { BlockType = BlockType.Paragraph, Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 3" } } },
new BlockModel { BlockType = BlockType.Paragraph, Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 4" } } }
};
private int selectedCount = 0;
private void OnSelectionChanged(SelectionChangedEventArgs args)
{
// Track selected block count
// args contains selection information
selectedCount = args.SelectedText?.Length ?? 0;
}
}Visual Feedback During Drag
While dragging, a visual indicator shows the drop location:
<!-- The editor automatically shows:
- Drag handle on hover
- Drop preview line during drag
- Drop zone highlight when hovering over valid targets
-->Undo and Redo
Manage content history with undo/redo functionality.
Keyboard Shortcuts
| Action | Windows | Mac |
|---|---|---|
| Undo | Ctrl + Z | ⌘ + Z |
| Redo | Ctrl + Y | ⌘ + Y |
Configure Undo/Redo Stack Depth
Control how many actions can be undone/redone:
<!-- Keep up to 50 actions in history (default is 30) -->
<SfBlockEditor UndoRedoStack="50"></SfBlockEditor>
<!-- Minimal history (10 actions) -->
<SfBlockEditor UndoRedoStack="10"></SfBlockEditor>
<!-- Large history (100 actions) -->
<SfBlockEditor UndoRedoStack="100"></SfBlockEditor>Complete Undo-Redo Example
@page "/undo-redo-demo"
@rendermode InteractiveAuto
<div style="max-width: 900px; margin: 20px auto;">
<div style="padding: 15px; background-color: #f5f5f5; margin-bottom: 15px; border-radius: 4px;">
<h2>Undo/Redo Demo</h2>
<p>Make changes and use Ctrl+Z (Undo) or Ctrl+Y (Redo) to revert/repeat</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button @onclick="@OnUndo" style="padding: 8px 16px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;">
Undo (Ctrl+Z)
</button>
<button @onclick="@OnRedo" style="padding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer;">
Redo (Ctrl+Y)
</button>
<button @onclick="@OnReset" style="padding: 8px 16px; background-color: #6c757d; color: white; border: none; border-radius: 4px; cursor: pointer;">
Reset Content
</button>
</div>
<div style="margin-top: 15px; padding: 10px; background-color: #e7f3ff; border-radius: 4px;">
<strong>History Depth:</strong> @historyDepth actions
</div>
</div>
<SfBlockEditor @bind-Blocks="blockData"
UndoRedoStack="50"
Height="500px"
BlockChanged="@OnBlockChanged">
</SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData;
private List<BlockModel> originalData;
private int historyDepth = 50;
protected override void OnInitialized()
{
originalData = GetInitialContent();
blockData = GetInitialContent();
}
private List<BlockModel> GetInitialContent()
{
return new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Undo/Redo History Demo" } }
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Start editing: add blocks, modify text, delete content..." } }
},
new BlockModel
{
BlockType = BlockType.BulletList,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Action 1" },
new ContentModel { ContentType = ContentType.Text, Content = "Action 2" },
new ContentModel { ContentType = ContentType.Text, Content = "Action 3" }
}
},
new BlockModel { BlockType = BlockType.Paragraph }
};
}
private void OnBlockChanged(BlockChangedEventArgs args)
{
// Track that changes have been made
// Can implement auto-save here
}
private async Task OnUndo()
{
// The editor will undo the last action when available
// Keyboard shortcut Ctrl+Z also works
}
private async Task OnRedo()
{
// The editor will redo the last undone action when available
// Keyboard shortcut Ctrl+Y also works
}
private void OnReset()
{
blockData = GetInitialContent();
}
}Track Changes Pattern
Implement change tracking with undo/redo:
@rendermode InteractiveAuto
<div style="max-width: 900px; margin: 20px auto;">
<div style="padding: 15px; background-color: #f5f5f5; margin-bottom: 15px;">
<h3>Change Tracking with History</h3>
<p>Changes: @changeCount | Undo Available: @(hasUndo ? "Yes" : "No")</p>
</div>
<SfBlockEditor @bind-Blocks="blockData"
UndoRedoStack="30"
BlockChanged="@OnBlockChanged">
</SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private int changeCount = 0;
private bool hasUndo = false;
private void OnBlockChanged(BlockChangedEventArgs args)
{
changeCount++;
// You can also log changes, trigger auto-save, update UI, etc.
}
}Auto-Save with Undo History
Combine undo/redo with auto-save:
@rendermode InteractiveAuto
<div style="max-width: 900px; margin: 20px auto;">
<div style="padding: 15px; background-color: #f5f5f5; margin-bottom: 15px; display: flex; justify-content: space-between; align-items: center;">
<div>
<strong>Status:</strong> @autoSaveStatus
</div>
<div>
<strong>Last Saved:</strong> @lastSaveTime
</div>
</div>
<SfBlockEditor @bind-Blocks="blockData"
UndoRedoStack="50"
BlockChanged="@OnBlockChanged">
</SfBlockEditor>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private string autoSaveStatus = "Ready";
private string lastSaveTime = "Never";
private System.Timers.Timer autoSaveTimer;
private bool hasUnsavedChanges = false;
protected override void OnInitialized()
{
// Setup auto-save timer (every 5 seconds)
autoSaveTimer = new System.Timers.Timer(5000);
autoSaveTimer.Elapsed += async (s, e) => await OnAutoSave();
autoSaveTimer.Start();
}
private void OnBlockChanged(BlockChangedEventArgs args)
{
hasUnsavedChanges = true;
autoSaveStatus = "Unsaved changes...";
}
private async Task OnAutoSave()
{
if (hasUnsavedChanges)
{
// Simulate saving to database
await Task.Delay(500);
hasUnsavedChanges = false;
autoSaveStatus = "Saved";
lastSaveTime = DateTime.Now.ToString("HH:mm:ss");
await InvokeAsync(StateHasChanged);
}
}
public void Dispose()
{
autoSaveTimer?.Dispose();
}
}Combined Drag-Drop and Undo Example
@page "/drag-undo-complete"
@rendermode InteractiveAuto
<div style="max-width: 900px; margin: 20px auto;">
<div style="padding: 15px; background-color: #f0f7ff; margin-bottom: 15px; border-radius: 4px;">
<h2>Block Editor with Full Interactivity</h2>
<p>Features: Drag-drop reordering, undo/redo, full editing</p>
</div>
<SfBlockEditor @bind-Blocks="blockData"
Width="100%"
Height="600px"
EnableDragAndDrop="true"
UndoRedoStack="50"
BlockChanged="@OnBlockChanged">
<BlockEditorCommandMenu></BlockEditorCommandMenu>
<BlockEditorContextMenu Enable="true"></BlockEditorContextMenu>
<BlockEditorActionMenu Enable="true"></BlockEditorActionMenu>
</SfBlockEditor>
<div style="margin-top: 20px; padding: 15px; background-color: #f5f5f5; border-radius: 4px;">
<div>Block Count: @blockData.Count</div>
<div>Last Change: @lastChange</div>
<div style="font-size: 12px; color: #666; margin-top: 10px;">
Tip: Use Ctrl+Z to undo, Ctrl+Y to redo, drag handles to reorder
</div>
</div>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Complete Editor Demo" } }
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "This editor has full drag-drop and undo/redo support" } }
},
new BlockModel
{
BlockType = BlockType.BulletList,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Drag blocks by their handle" },
new ContentModel { ContentType = ContentType.Text, Content = "Undo changes with Ctrl+Z" },
new ContentModel { ContentType = ContentType.Text, Content = "Redo with Ctrl+Y" }
}
},
new BlockModel { BlockType = BlockType.Paragraph }
};
private string lastChange = "None";
private void OnBlockChanged(BlockChangedEventArgs args)
{
lastChange = DateTime.Now.ToString("HH:mm:ss");
}
}Editor Menus in Syncfusion Blazor Block Editor
Table of Contents
Slash Command Menu
The Slash Command menu (/) provides keyboard-driven access to insert or transform blocks without mouse interaction.
Built-In Commands
Trigger with / to see:
| Command | Shortcut | Function |
|---|---|---|
| Paragraph | /p | Insert paragraph block |
| Heading 1 | /h1 | Insert H1 heading |
| Heading 2 | /h2 | Insert H2 heading |
| Heading 3 | /h3 | Insert H3 heading |
| Heading 4 | /h4 | Insert H4 heading |
| Bullet List | /ul | Insert bullet list |
| Numbered List | /ol | Insert numbered list |
| Checklist | /cl | Insert checklist |
| Quote | /q | Insert quote block |
| Code Block | /code | Insert code block |
| Table | /table | Insert table |
| Image | /img | Insert image |
| Divider | /divider | Insert divider line |
| Callout | /callout | Insert callout block |
| Toggle | /toggle | Insert toggle/collapsible block |
Default Slash Command Menu
<SfBlockEditor>
<BlockEditorCommandMenu></BlockEditorCommandMenu>
</SfBlockEditor>Customize Slash Command Menu
Add custom commands or modify behavior:
@page "/custom-slash-menu"
@rendermode InteractiveAuto
<SfBlockEditor Blocks="@blockData">
<BlockEditorCommandMenu PopupHeight="400px"
PopupWidth="300px"
Commands="@customCommands"
ItemSelect="@OnCommandSelect"
Filtering="@OnCommandFilter">
</BlockEditorCommandMenu>
</SfBlockEditor>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private List<CommandItemModel> customCommands = new()
{
// Keep default block creation commands
new CommandItemModel
{
ID = "para-cmd",
Type = BlockType.Paragraph,
GroupBy = "Blocks",
Label = "Paragraph",
IconCss = "e-icons e-paragraph"
},
new CommandItemModel
{
ID = "heading-cmd",
Type = BlockType.Heading,
GroupBy = "Blocks",
Label = "Heading 1",
IconCss = "e-icons e-heading"
},
// Custom application commands
new CommandItemModel
{
ID = "template-cmd",
GroupBy = "Templates",
Label = "Insert Template",
IconCss = "e-icons e-template"
},
new CommandItemModel
{
ID = "signature-cmd",
GroupBy = "Special",
Label = "Add Signature Block",
IconCss = "e-icons e-signature"
}
};
private void OnCommandSelect(CommandItemSelectEventArgs args)
{
// Handle custom command execution
if (args.Item.ID == "template-cmd")
{
// Insert template logic
}
else if (args.Item.ID == "signature-cmd")
{
// Insert signature block logic
}
}
private void OnCommandFilter(CommandFilteringEventArgs args)
{
// Filter commands based on search text
// Allows smart filtering as user types
}
}Slash Menu Events
<BlockEditorCommandMenu Filtering="@OnFilter" ItemSelect="@OnItemSelect">
</BlockEditorCommandMenu>
@code {
private void OnFilter(CommandFilteringEventArgs args)
{
// args.Text contains the typed filter text
// Implement fuzzy search or custom filtering logic
}
private void OnItemSelect(CommandItemSelectEventArgs args)
{
// args.Item contains the selected command
// args.Name contains the action name
}
}Context Menu
Right-click menu for block-level and content-level actions.
Built-In Context Menu Items
| Item | Function |
|---|---|
| Undo | Undo last action |
| Redo | Redo last undone action |
| Cut | Cut selected content |
| Copy | Copy selected content |
| Paste | Paste from clipboard |
| Indent | Increase block indent level |
| Outdent | Decrease block indent level |
| Link | Add/edit hyperlink |
Enable Context Menu
<SfBlockEditor>
<BlockEditorContextMenu Enable="true"></BlockEditorContextMenu>
</SfBlockEditor>Context Menu Properties
| Property | Type | Default | Description |
|---|---|---|---|
Enable | bool | true | Enable/disable context menu |
ShowItemOnClick | bool | false | Show submenu on click instead of hover |
Show Submenu on Click
The ShowItemOnClick property controls how submenus are triggered. Set to true for better touch device support:
<!-- Default behavior: Submenu shows on hover -->
<BlockEditorContextMenu Enable="true" ShowItemOnClick="false">
</BlockEditorContextMenu>
<!-- Mobile-friendly: Submenu shows on click -->
<BlockEditorContextMenu Enable="true" ShowItemOnClick="true">
</BlockEditorContextMenu>Use Cases for ShowItemOnClick:
- Touch Devices: Touch interfaces don't support hover, so set
truefor better UX - Desktop with Hover: Set
falsefor immediate submenu display on hover - Accessibility: Touch-friendly approach can improve accessibility
Customize Context Menu
@page "/custom-context-menu"
@rendermode InteractiveAuto
<SfBlockEditor Blocks="@blockData">
<BlockEditorContextMenu Enable="true"
ShowItemOnClick="true"
Items="@contextMenuItems"
ItemSelect="@OnContextMenuSelect"
Opening="@OnContextMenuOpening"
Closing="@OnContextMenuClosing">
</BlockEditorContextMenu>
</SfBlockEditor>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private List<ContextMenuItemModel> contextMenuItems = new()
{
// Standard items
new ContextMenuItemModel { ID = "cut", Text = "Cut", IconCss = "e-icons e-cut" },
new ContextMenuItemModel { ID = "copy", Text = "Copy", IconCss = "e-icons e-copy" },
new ContextMenuItemModel { ID = "paste", Text = "Paste", IconCss = "e-icons e-paste" },
new ContextMenuItemModel { Separator = true },
// Custom items
new ContextMenuItemModel
{
ID = "format-submenu",
Text = "Format",
IconCss = "e-icons e-format-painter",
Items = new()
{
new ContextMenuItemModel { ID = "bold", Text = "Bold", IconCss = "e-icons e-bold" },
new ContextMenuItemModel { ID = "italic", Text = "Italic", IconCss = "e-icons e-italic" },
new ContextMenuItemModel { ID = "underline", Text = "Underline", IconCss = "e-icons e-underline" }
}
},
new ContextMenuItemModel { Separator = true },
new ContextMenuItemModel
{
ID = "export",
Text = "Export",
IconCss = "e-icons e-export"
},
new ContextMenuItemModel
{
ID = "delete",
Text = "Delete",
IconCss = "e-icons e-delete"
}
};
private void OnContextMenuSelect(ContextMenuItemSelectEventArgs args)
{
// Handle custom menu item selection
if (args.Item.ID == "export")
{
// Export block logic
}
else if (args.Item.ID == "delete")
{
// Delete block logic
}
}
private void OnContextMenuOpening(ContextMenuOpeningEventArgs args)
{
// Conditionally show/hide items based on context
}
private void OnContextMenuClosing(ContextMenuClosingEventArgs args)
{
// Cleanup when menu closes
}
}Context Menu Events
<BlockEditorContextMenu Opening="@OnOpening"
Closing="@OnClosing"
ItemSelect="@OnSelect">
</BlockEditorContextMenu>
@code {
private void OnOpening(ContextMenuOpeningEventArgs args)
{
// args.Target contains clicked element
// Modify items before display
}
private void OnClosing(ContextMenuClosingEventArgs args)
{
// args.IsCanceled indicates if closed by user
}
private void OnSelect(ContextMenuItemSelectEventArgs args)
{
// args.Item contains selected item
// args.ParentItem contains parent if submenu
}
}Block Action Menu
Hover-based menu for block-level operations (drag handle menu).
Built-In Block Action Items
| Item | Function |
|---|---|
| Duplicate | Clone block |
| Delete | Remove block |
| Move Up | Move block up |
| Move Down | Move block down |
Enable Block Action Menu
<SfBlockEditor>
<BlockEditorActionMenu Enable="true"></BlockEditorActionMenu>
</SfBlockEditor>Customize Block Action Menu
@page "/custom-action-menu"
@rendermode InteractiveAuto
<SfBlockEditor Blocks="@blockData">
<BlockEditorActionMenu Enable="true"
PopupWidth="200px"
PopupHeight="150px"
EnableTooltip="true"
Items="@actionMenuItems"
ItemSelect="@OnActionMenuSelect"
Opening="@OnActionMenuOpening"
Closing="@OnActionMenuClosing">
</BlockEditorActionMenu>
</SfBlockEditor>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph, Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 1" } } },
new BlockModel { BlockType = BlockType.Paragraph, Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 2" } } },
new BlockModel { BlockType = BlockType.Paragraph, Content = new() { new ContentModel { ContentType = ContentType.Text, Content = "Block 3" } } }
};
private List<BlockActionItemModel> actionMenuItems = new()
{
new BlockActionItemModel
{
ID = "duplicate",
Label = "Duplicate",
IconCss = "e-icons e-duplicate",
Tooltip = "Clone this block"
},
new BlockActionItemModel
{
ID = "move-up",
Label = "Move Up",
IconCss = "e-icons e-arrow-up",
Tooltip = "Move up in document"
},
new BlockActionItemModel
{
ID = "move-down",
Label = "Move Down",
IconCss = "e-icons e-arrow-down",
Tooltip = "Move down in document"
},
// Custom actions
new BlockActionItemModel
{
ID = "highlight",
Label = "Highlight",
IconCss = "e-icons e-highlight",
Tooltip = "Highlight this block"
},
new BlockActionItemModel
{
ID = "convert",
Label = "Convert",
IconCss = "e-icons e-convert",
Tooltip = "Convert block type"
},
new BlockActionItemModel
{
ID = "delete",
Label = "Delete",
IconCss = "e-icons e-delete",
Tooltip = "Remove this block"
}
};
private void OnActionMenuSelect(ActionMenuItemSelectEventArgs args)
{
if (args.Item.ID == "highlight")
{
// Apply highlighting
}
else if (args.Item.ID == "convert")
{
// Show block type conversion dialog
}
}
private void OnActionMenuOpening(ActionMenuOpeningEventArgs args)
{
// Conditionally show/hide items based on block type
// args.Block contains current block
}
private void OnActionMenuClosing(ActionMenuClosingEventArgs args)
{
// Cleanup logic
}
}Show/Hide Tooltips
<BlockEditorActionMenu EnableTooltip="true"></BlockEditorActionMenu>
<!-- or -->
<BlockEditorActionMenu EnableTooltip="false"></BlockEditorActionMenu>Inline Toolbar
Text selection toolbar for formatting options.
Built-In Inline Toolbar Items
| Item | Function |
|---|---|
| Bold | Bold formatting |
| Italic | Italic formatting |
| Underline | Underline text |
| Strikethrough | Strike through text |
| Superscript | Raise text |
| Subscript | Lower text |
| Case Conversion | Change text case |
| Text Color | Change text color |
| Background Color | Change background color |
Enable Inline Toolbar
<SfBlockEditor>
<BlockEditorInlineToolbar Enable="true"></BlockEditorInlineToolbar>
</SfBlockEditor>Customize Inline Toolbar
@page "/custom-inline-toolbar"
@rendermode InteractiveAuto
<SfBlockEditor Blocks="@blockData">
<BlockEditorInlineToolbar Enable="true"
PopupWidth="120px"
Items="@toolbarItems"
ItemClick="@OnToolbarItemClick">
</BlockEditorInlineToolbar>
</SfBlockEditor>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Select text to see inline formatting toolbar" }
}
}
};
private List<InlineToolbarItemModel> toolbarItems = new()
{
new InlineToolbarItemModel { Command = CommandName.Bold },
new InlineToolbarItemModel { Command = CommandName.Italic },
new InlineToolbarItemModel { Command = CommandName.Underline },
new InlineToolbarItemModel { Command = CommandName.Strikethrough }
};
private void OnToolbarItemClick(InlineToolbarItemClickEventArgs args)
{
// args.Item contains clicked toolbar item
// args.Item.Command contains the command
}
}Menu Event Patterns
Pattern 1: Dynamic Menu Filtering
<BlockEditorCommandMenu Filtering="@OnFilter">
</BlockEditorCommandMenu>
@code {
private void OnFilter(CommandFilteringEventArgs args)
{
// Show/hide commands based on search
// Implement smart filtering for better UX
}
}Pattern 2: Context-Aware Menu Items
<BlockEditorContextMenu Opening="@OnOpening">
</BlockEditorContextMenu>
@code {
private void OnOpening(ContextMenuOpeningEventArgs args)
{
// Modify available items based on selected content
// Enable/disable items based on block type or selection state
}
}Pattern 3: Custom Action Execution
<BlockEditorActionMenu ItemSelect="@OnActionSelect">
</BlockEditorActionMenu>
@code {
private void OnActionSelect(ActionMenuItemSelectEventArgs args)
{
// Execute custom logic based on selected action
// Update UI, trigger events, modify content
}
}Complete Menu Configuration Example
@page "/complete-menus"
@rendermode InteractiveAuto
<SfBlockEditor @bind-Blocks="blockData" EnableDragAndDrop="true">
<!-- Slash Command Menu -->
<BlockEditorCommandMenu PopupHeight="400px"
PopupWidth="320px"
ItemSelect="@OnSlashSelect">
</BlockEditorCommandMenu>
<!-- Context Menu -->
<BlockEditorContextMenu Enable="true"
ItemSelect="@OnContextSelect"
Opening="@OnContextOpening">
</BlockEditorContextMenu>
<!-- Block Action Menu -->
<BlockEditorActionMenu Enable="true"
EnableTooltip="true"
ItemSelect="@OnActionSelect">
</BlockEditorActionMenu>
<!-- Inline Toolbar -->
<BlockEditorInlineToolbar Enable="true"
PopupWidth="150px"
ItemClick="@OnToolbarClick">
</BlockEditorInlineToolbar>
</SfBlockEditor>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel { BlockType = BlockType.Paragraph }
};
private void OnSlashSelect(CommandItemSelectEventArgs args) { }
private void OnContextSelect(ContextMenuItemSelectEventArgs args) { }
private void OnContextOpening(ContextMenuOpeningEventArgs args) { }
private void OnActionSelect(ActionMenuItemSelectEventArgs args) { }
private void OnToolbarClick(InlineToolbarItemClickEventArgs args) { }
}Getting Started with Syncfusion Blazor Block Editor
Installation and Setup
Prerequisites
- .NET 8.0 SDK or later
- Visual Studio 2022, Visual Studio Code, or .NET CLI
- Basic knowledge of Blazor and Razor components
Step 1: Create a Blazor Web App
Using Visual Studio: 1. Create a new Blazor Web App project 2. Choose interactive render mode (Auto, WebAssembly, or Server) 3. Configure interactivity location as needed
Using Visual Studio Code:
dotnet new blazor -o MyBlockEditorApp -int Auto
cd MyBlockEditorApp
cd MyBlockEditorApp.ClientUsing .NET CLI:
dotnet new blazor -o MyBlockEditorApp -int Auto
cd MyBlockEditorApp
cd MyBlockEditorApp.ClientStep 2: Install NuGet Packages
Using NuGet Package Manager (Visual Studio): 1. Open NuGet Package Manager 2. Search for Syncfusion.Blazor.BlockEditor 3. Install both Syncfusion.Blazor.BlockEditor and Syncfusion.Blazor.Themes
Using Package Manager Console:
Install-Package Syncfusion.Blazor.BlockEditor -Version 28.0.0
Install-Package Syncfusion.Blazor.Themes -Version 28.0.0Using .NET CLI:
dotnet add package Syncfusion.Blazor.BlockEditor --version 28.0.0
dotnet add package Syncfusion.Blazor.Themes --version 28.0.0
dotnet restoreStep 3: Register Syncfusion Service
Open Program.cs in your project and add the Syncfusion Blazor service:
For Server-Side Rendering:
using Syncfusion.Blazor;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddSyncfusionBlazor();
var app = builder.Build();
// ... rest of configurationFor WebAssembly (Client project):
using Syncfusion.Blazor;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddSyncfusionBlazor();
await builder.Build().RunAsync();Step 4: Add Import Namespaces
Open _Imports.razor and add:
@using Syncfusion.Blazor
@using Syncfusion.Blazor.BlockEditorStep 5: Include Theme Resources
In App.razor, add the theme stylesheet and script references:
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Block Editor App</title>
<base href="/" />
<link href="_framework/blazor.web.css" rel="stylesheet" />
<!-- Add Syncfusion theme -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
</head>
<body>
<!-- ... existing content ... -->
<!-- Add Syncfusion script at the end of body -->
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js" type="text/javascript"></script>
</body>Available Themes:
bootstrap5.css- Bootstrap 5 themematerial.css- Material Design themematerial-dark.css- Material Dark themetailwind.css- Tailwind CSS themetailwind-dark.css- Tailwind Dark themefabric.css- Fabric Design themefluent.css- Fluent Design theme
Choose the theme that matches your application design.
Basic Component Initialization
Minimal Block Editor
Create a simple editor with default configuration:
@page "/block-editor"
@rendermode InteractiveAuto
<SfBlockEditor></SfBlockEditor>Block Editor with Initial Content
Bind blocks to create an editor with predefined content:
@page "/block-editor"
@rendermode InteractiveAuto
<SfBlockEditor @bind-Blocks="blockData"></SfBlockEditor>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Welcome to Block Editor" }
}
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "This is a paragraph block with text content." }
}
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() // Empty paragraph for user input
}
};
}Render Modes
Choose the appropriate render mode for your use case:
Auto Render Mode
Renders on the server initially, then switches to WebAssembly:
@rendermode InteractiveAutoBest for: Applications needing fast initial load with client-side interactivity
WebAssembly Render Mode
Runs entirely in the browser:
@rendermode InteractiveWebAssemblyBest for: Progressive Web Apps, offline-first applications
Server Render Mode
All processing happens on the server:
@rendermode InteractiveServerBest for: Real-time collaboration, server-side state management
Configuration Example
@page "/editor"
@rendermode InteractiveAuto
<div id="editor-container" style="display: flex; flex-direction: column; gap: 20px;">
<div style="padding: 15px; background-color: #f5f5f5; border-radius: 4px;">
<h2>Block Editor Configuration</h2>
<p>Start editing below or press "/" to open the command menu:</p>
</div>
<SfBlockEditor @bind-Blocks="blockData"
Width="100%"
Height="500px"
EnableDragAndDrop="true"
UndoRedoStack="50">
</SfBlockEditor>
<div style="padding: 15px; background-color: #f0f7ff; border-radius: 4px;">
<strong>Block Count:</strong> @blockData.Count
</div>
</div>
@code {
private List<BlockModel> blockData = new()
{
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 1 },
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Structured Content Editor" }
}
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "This editor uses a block-based architecture for flexible, modular content creation." }
}
},
new BlockModel
{
BlockType = BlockType.Heading,
Properties = new HeadingBlockSettings { Level = 2 },
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Features" }
}
},
new BlockModel
{
BlockType = BlockType.BulletList,
Content = new()
{
new ContentModel { ContentType = ContentType.Text, Content = "Drag-and-drop block reordering" },
new ContentModel { ContentType = ContentType.Text, Content = "Comprehensive keyboard shortcuts" },
new ContentModel { ContentType = ContentType.Text, Content = "Rich text formatting options" },
new ContentModel { ContentType = ContentType.Text, Content = "Media and table support" }
}
},
new BlockModel
{
BlockType = BlockType.Paragraph,
Content = new() // Empty for user input
}
};
}
<style>
#editor-container {
max-width: 900px;
margin: 0 auto;
padding: 20px;
}
</style>Troubleshooting Installation
| Issue | Solution |
|---|---|
| NuGet packages not found | Ensure your NuGet source includes nuget.org; update package manager |
| Theme not applying | Verify theme CSS is linked in App.razor; check file path |
| Component not rendering | Ensure Syncfusion service is registered in Program.cs; verify namespaces imported |
| Events not firing | Confirm render mode is interactive (not Static); check event handler is defined |
| Styling not working | Clear browser cache; check browser console for CSS load errors |