Few weeks ago, I created a package for a Markdown Editor component for Blazor with very basic functionalities. After a couple of months of work, finally, I created a very nice Markdown Editor component based on EasyMDE flexible and rich of functionalities for Blazor WebAssembly and Blazor Server with .NET6.
The full source code of the component is available on GitHub. In the same repository, there is a demo project and another project for the integration between the Markdown Editor for Blazor and the API for uploading images.
How to use
First, in your Blazor project, add the NuGet package called PSC.Blazor.Components.MarkdownEditor. Then, add the Markdown Editor to your _Imports.razor
@using PSC.Blazor.Components.MarkdownEditor
@using PSC.Blazor.Components.MarkdownEditor.EventsArgs
Now, in your index.html
or host.html
add those lines:
<link href="/_content/PSC.Blazor.Components.MarkdownEditor/css/easymde.min.css" rel="stylesheet" />
<script src="/_content/PSC.Blazor.Components.MarkdownEditor/js/easymde.min.js"></script>
<script src="/_content/PSC.Blazor.Components.MarkdownEditor/js/markdownEditor.js"></script>
Remember that jQuery
is also required. The component contains the EasyMDE script version 2.15.0. Obviously, you can add this script in your project but if you use the script in the component, you are sure it works fine and all functionalities are tested.
Add MarkdownEditor in a page
Now, in a Razor
page, we can add the component with these lines
<div class="col-md-12">
<MarkdownEditor Value="@markdownValue"
ValueChanged="@OnMarkdownValueChanged"
ValueHTMLChanged="@OnMarkdownValueHTMLChanged" />
<hr />
<h3>Result</h3>
@((MarkupString)markdownHtml)
</div>
@code {
string markdownValue = "#Markdown Editor\nThis is a test";
string markdownHtml;
protected override void OnInitialized()
{
markdownHtml = Markdig.Markdown.ToHtml(markdownValue ?? string.Empty);
base.OnInitialized();
}
Task OnMarkdownValueChanged(string value)
{
return Task.CompletedTask;
}
Task OnMarkdownValueHTMLChanged(string value)
{
markdownHtml = value;
return Task.CompletedTask;
}
}
So, the result is a nice Markdown Editor like in the following screenshot. This is a screenshot from the demo in this repository.
Code explained
So, the initial text for the Markdown Editor is coming from the markdownValue
that is a string containing Markdown text. For every change in the editor, the Value
changes and the component raises an event, in this case OnMarkdownValueChanged
: this event receives the Markdown text as a value
.
From here, I can process with my procedure the Markdown text to obtain the HTML but I want to simplify this step. So, the component converts the Markdown text in a HTML and raises the event ValueHTMLChanged
.
Documentation
The Markdown Editor for Blazor has an estensive collection of properties to map all the functionalities in the JavaScript version. In this repository, there are 2 projects:
- MarkdownEditorDemo is a Blazor Web Assembly project that contains 2 pages:
Index.razor
where I show how to use the component with the basic functions andUpload.razor
that shows how to cope with the image upload. To test the upload, the projectMarkdownEditorDemo.Api
must run - MarkdownEditorDemo.Api this is an ASP.NET Core WebApi (.NET6) how to implement a proper API for uploading images. For more details, I wrote a post about Uploading image with .NET.
Properties
Name | Description | Type | Default |
---|---|---|---|
AutoSaveEnabled | Gets or sets the setting for the auto save. Saves the text that’s being written and will load it back in the future. It will forget the text when the form it’s contained in is submitted. Recommended to choose a unique ID for the Markdown Editor. | bool | false |
AutoSaveId | Gets or sets the automatic save identifier. You must set a unique string identifier so the component can autosave. Something that separates this from other instances of the component elsewhere on your website. | string | Default value |
AutoSaveDelay | Delay between saves, in milliseconds. Defaults to 10000 (10s). | int | 10000 (10s) |
AutoSaveSubmitDelay | Delay before assuming that submit of the form failed and saving the text, in milliseconds. | int | 5000 (5s) |
AutoSaveText | Text for autosave | string | Autosaved: |
AutoSaveTimeFormatLocale | Set the format for the datetime to display. For more info, see the JavaScript documentation DateTimeFormat instances | string | en-US |
AutoSaveTimeFormatYear | Set the format for the year | string | numeric |
AutoSaveTimeFormatMonth | Set the format for the month | string | long |
AutoSaveTimeFormatDay | Set the format for the day | string | 2-digit |
AutoSaveTimeFormatHour | Set the format for the hour | string | 2-digit |
AutoSaveTimeFormatMinute | Set the format for the minute | string | 2-digit |
AutoDownloadFontAwesome | If set to true, force downloads Font Awesome (used for icons). If set to false, prevents downloading. | bool? | null |
CustomButtonClicked | Occurs after the custom toolbar button is clicked. | EventCallback | |
Direction | rtl or ltr. Changes text direction to support right-to-left languages. Defaults to ltr. | string | ltr |
ErrorMessages | Errors displayed to the user, using the errorCallback option, where image_name, image_size and image_max_size will be replaced by their respective values, that can be used for customization or internationalization. | MarkdownErrorMessages | |
HideIcons | An array of icon names to hide. Can be used to hide specific icons shown by default without completely customizing the toolbar. | string[] | ‘side-by-side’, ‘fullscreen’ |
ImageAccept | A comma-separated list of mime-types used to check image type before upload (note: never trust client, always check file types at server-side). Defaults to image/png, image/jpeg, image/jpg, image.gif. | string | image/png, image/jpeg, image/jpg, image.gif |
ImageCSRFToken | CSRF token to include with AJAX call to upload image. For instance, used with Django backend. | string | |
ImageMaxSize | Maximum image size in bytes, checked before upload (note: never trust client, always check image size at server-side). Defaults to 1024 * 1024 * 2 (2Mb). | long | 1024 * 1024 * 2 (2Mb) |
ImagePathAbsolute | If set to true, will treat imageUrl from imageUploadFunction and filePath returned from imageUploadEndpoint as an absolute rather than relative path, i.e. not prepend window.location.origin to it. | string | |
ImageTexts | Texts displayed to the user (mainly on the status bar) for the import image feature, where image_name, image_size and image_max_size will be replaced by their respective values, that can be used for customization or internationalization. | MarkdownImageTexts | null |
ImageUploadAuthenticationSchema | If an authentication for the API is required, assign to this property the schema to use. Bearer is the common one. | string | empty |
ImageUploadAuthenticationToken | If an authentication for the API is required, assign to this property the token | string | empty |
LineNumbers | If set to true, enables line numbers in the editor. | bool | false |
LineWrapping | If set to false, disable line wrapping. Defaults to true. | bool | false |
MaxHeight | Sets fixed height for the composition area. minHeight option will be ignored. Should be a string containing a valid CSS value like “500px”. Defaults to undefined. | string | |
MaxUploadImageMessageSize | Gets or sets the max message size when uploading the file. | long | 20 * 1024 |
MinHeight | Sets the minimum height for the composition area, before it starts auto-growing. Should be a string containing a valid CSS value like “500px”. Defaults to “300px”. | string | 300px |
Placeholder | If set, displays a custom placeholder message. | string | null |
SegmentFetchTimeout | Gets or sets the Segment Fetch Timeout when uploading the file. | TimeSpan | 1 min |
ShowIcons | An array of icon names to show. Can be used to show specific icons hidden by default without completely customizing the toolbar. | string[] | ‘code’, ‘table’ |
TabSize | If set, customize the tab size. Defaults to 2. | int | 2 |
Theme | Override the theme. Defaults to easymde. | string | easymde |
Toolbar | [Optional] Gets or sets the content of the toolbar. | RenderFragment | |
ToolbarTips | If set to false, disable toolbar button tips. Defaults to true. | bool | true |
UploadImage | If set to true, enables the image upload functionality, which can be triggered by drag-drop, copy-paste and through the browse-file window (opened when the user clicks on the upload-image icon). Defaults to false. | bool | false |
Value | Gets or sets the markdown value. | string | null |
ValueHTML | Gets the HTML from the markdown value. | string | null |
Events
Name | Description | Type |
---|---|---|
ErrorCallback | A callback function used to define how to display an error message. Defaults to (errorMessage) => alert(errorMessage). | Func |
ImageUploadChanged | Occurs every time the selected image has changed. | Func |
ImageUploadEnded | Occurs when an individual image upload has ended. | Func |
ImageUploadEndpoint | The endpoint where the images data will be sent, via an asynchronous POST request. The server is supposed to save this image, and return a json response. | string |
ImageUploadProgressed | Notifies the progress of image being written to the destination stream. | Func |
ImageUploadStarted | Occurs when an individual image upload has started. | Func |
ValueChanged | An event that occurs after the markdown value has changed. | EventCallback |
ValueHTMLChanged | An event that occurs after the markdown value has changed and the new HTML code is available. | EventCallback |
Upload file
Now, the Markdown Editor for Blazor can take care of uploading a file and add the relative Markdown code in the editor. For that, the property UploadImage
has to set to true
. Also, the upload API must be specified in the property ImageUploadEndpoint
.
In some cases, the API requires an authentication. The properties ImageUploadAuthenticationSchema
and ImageUploadAuthenticationToken
allow you to pass the correct schema and token to use in the call.
Those values will be added to the HttpClient
POST
request in the header. Only if both properties are not null, they will be added to the header.
So, the result is quite nice and you can see the Markdown Editor for Blazor in action in the following screenshot. How you can see, I drag an image on the editor and immediately the upload process starts. When the API returns then URL for the image, the editor adds a new Markdown text for the image.
How to create the API
Now, if you want to allow users to upload pictures via the Markdown Editor for Blazor, the property UploadImage
must set to true
. Then, the endpoint for the API must be declared via the property ImageUploadEndpoint
.
The Markdown Editor has JavaScript under the cover and this can cause same complications. For this reason, I managed in the JavaScript code to have the uploaded imaged encoded in Base64
. Then, a C# function takes care to upload the file to the specified API. This API must return a 200 HTTP code
and, as a content, the URL of the uploaded image.
It seems easy but I took a lot of time to figure out how to do it. So, I created the post Upload/Download Files Using HttpClient to explain in details how to create the required API.
The full source code of the component is available on GitHub.
Toolbar icons
Below are the built-in toolbar icons (only some of which are enabled by default), which can be reorganized however you like. “Name” is the name of the icon, referenced in the JS. “Action” is either a function or a URL to open. “Class” is the class given to the icon. “Tooltip” is the small tooltip that appears via the title=""
attribute. Note that shortcut hints are added automatically and reflect the specified action if it has a key bind assigned to it (i.e. with the value of action
set to bold
and that of tooltip
set to Bold
, the final text the user will see would be “Bold (Ctrl-B)”).
Additionally, you can add a separator between any icons by adding "|"
to the toolbar array.
Name | Action | Tooltip Class |
---|---|---|
bold | toggleBold | Bold fa fa-bold |
italic | toggleItalic | Italic fa fa-italic |
strikethrough | toggleStrikethrough | Strikethrough fa fa-strikethrough |
heading | toggleHeadingSmaller | Heading fa fa-header |
heading-smaller | toggleHeadingSmaller | Smaller Heading fa fa-header |
heading-bigger | toggleHeadingBigger | Bigger Heading fa fa-lg fa-header |
heading-1 | toggleHeading1 | Big Heading fa fa-header header-1 |
heading-2 | toggleHeading2 | Medium Heading fa fa-header header-2 |
heading-3 | toggleHeading3 | Small Heading fa fa-header header-3 |
code | toggleCodeBlock | Code fa fa-code |
quote | toggleBlockquote | Quote fa fa-quote-left |
unordered-list | toggleUnorderedList | Generic List fa fa-list-ul |
ordered-list | toggleOrderedList | Numbered List fa fa-list-ol |
clean-block | cleanBlock | Clean block fa fa-eraser |
link | drawLink | Create Link fa fa-link |
image | drawImage | Insert Image fa fa-picture-o |
table | drawTable | Insert Table fa fa-table |
horizontal-rule | drawHorizontalRule | Insert Horizontal Line fa fa-minus |
preview | togglePreview | Toggle Preview fa fa-eye no-disable |
side-by-side | toggleSideBySide | Toggle Side by Side fa fa-columns no-disable no-mobile |
fullscreen | toggleFullScreen | Toggle Fullscreen fa fa-arrows-alt no-disable no-mobile |
guide | This link | Markdown Guide fa fa-question-circle |
Keyboard shortcuts
The Markdown Editor component for Blazor comes with an array of predefined keyboard shortcuts, but they can be altered with a configuration option. The list of default ones is as follows:
Shortcut (Windows / Linux) | Shortcut (macOS) | Action |
---|---|---|
Ctrl-‘ | Cmd-‘ | “toggleBlockquote” |
Ctrl-B | Cmd-B | “toggleBold” |
Ctrl-E | Cmd-E | “cleanBlock” |
Ctrl-H | Cmd-H | “toggleHeadingSmaller” |
Ctrl-I | Cmd-I | “toggleItalic” |
Ctrl-K | Cmd-K | “drawLink” |
Ctrl-L | Cmd-L | “toggleUnorderedList” |
Ctrl-P | Cmd-P | “togglePreview” |
Ctrl-Alt-C | Cmd-Alt-C | “toggleCodeBlock” |
Ctrl-Alt-I | Cmd-Alt-I | “drawImage” |
Ctrl-Alt-L | Cmd-Alt-L | “toggleOrderedList” |
Shift-Ctrl-H | Shift-Cmd-H | “toggleHeadingBigger” |
F9 | F9 | “toggleSideBySide” |
F11 | F11 | “toggleFullScreen” |
Wrap up
In conclusion, this is a new Markdown Editor for Blazor, with a lot of functionalities, flexible and with image upload. I hope you like it and will use it. It you need help or for suggestion, please comment below or in the Forum.
Other Blazor components
- DataTable for Blazor: DataTable component for Blazor WebAssembly and Blazor Server
- Markdown editor for Blazor: This is a Markdown Editor for use in Blazor. It contains a live preview as well as an embedded help guide for users.
- Modal dialog for Blazor: Simple Modal Dialog for Blazor WebAssembly
- PSC.Extensions: A lot of functions for .NET6 in a NuGet package that you can download for free. We collected in this package functions for everyday work to help you with claim, strings, enums, date and time, expressions…
- Quill for Blazor: Quill Component is a custom reusable control that allows us to easily consume Quill and place multiple instances of it on a single page in our Blazor application
- Segment for Blazor: This is a Segment component for Blazor Web Assembly and Blazor Server
- Tabs for Blazor: This is a Tabs component for Blazor Web Assembly and Blazor Server
More examples and documentation
- Write a reusable Blazor component
- Getting Started With C# And Blazor
- Setting Up A Blazor WebAssembly Application
- Working With Blazor Component Model
- Secure Blazor WebAssembly With IdentityServer4
- Blazor Using HttpClient With Authentication
- InputSelect component for enumerations in Blazor
- Use LocalStorage with Blazor WebAssembly
- Modal Dialog component for Blazor
- Create Tooltip component for Blazor
- Consume ASP.NET Core Razor components from Razor class libraries | Microsoft Docs
There is a new version of this component. See https://puresourcecode.com/forum/markdown-editor-for-blazor/new-release-v-2-0-9/