Building a Reusable Blazor Component for Summernote

In another adventure with Blazor…

While migrating a legacy app, I had to “componentize” an HTML editor. It used Summernote, “a super simple WYSIWYG editor on Bootstrap”.

The fun part was learning JavaScript interop: calling JavaScript from .NET and viceversa.

After some Googling and sneaking into abandoned GitHub repos, here’s what I came up with:

The component

In Summernote.razor:

@using Microsoft.AspNetCore.Components.Sections

<HeadContent>
    <link rel="stylesheet" href="css/summernote.css" />
</HeadContent>

<div id="@_id">@((MarkupString)Value)</div>

<SectionContent SectionName="scripts">
    <script src="js/summernote.js" type="text/javascript"></script>
</SectionContent>

In Summernote.razor.cs:

using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

namespace MyCoolComponents.HtmlEditor;

public partial class Summernote : IAsyncDisposable
{
    private readonly string _id = $"summernote_{Guid.NewGuid()}";

    private IJSObjectReference? _module;
    private DotNetObjectReference<Summernote>? _dotnetRef;
    private bool _editorInitialized = false;

    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }

    [Inject]
    public IJSRuntime JsRuntime { get; set; } = default!;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (!_editorInitialized)
        {
            _dotnetRef = DotNetObjectReference.Create(this);

            _module = await JsRuntime.InvokeAsync<IJSObjectReference>("import", "./PutYourPathHere/Summernote.razor.js");
            await _module.InvokeVoidAsync("edit", _id, _dotnetRef, nameof(OnTextChange));

            _editorInitialized = true;
        }
    }

    [JSInvokable]
    public async Task OnTextChange(string editorText)
    {
        Value = editorText;
        await ValueChanged.InvokeAsync(editorText);
    }

    public async ValueTask DisposeAsync()
    {
        if (_module is not null)
        {
            await _module.DisposeAsync();
        }

        _dotnetRef?.Dispose();
    }
}

Here’s where the magic happens.

After rendering the component, it calls a JavaScript function that initializes the Summernote editor. Then, it registers a callback that calls a .NET function every time the editor changes to update the component state.

Notice it stores content in a MarkupString but it binds as a string.

In Summernote.razor.js:

export function edit(id, dotnetRef, callback) {
    let snid = '#' + id;
    $(snid).summernote({
        callbacks: {
            onChange: function (contents, $editable) {
                dotnetRef.invokeMethodAsync(callback, contents);
            }
        }
    });
}

How to use it

And here’s a sample form using the editor:

@using MyCoolComponents.HtmlEditor

<EditForm Model="MyCoolRequest" OnValidSubmit="OnValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div class="row">
        <div class="form-group col-sm-9">
            <label for="content" class="form-label">Content</label>
            <Summernote @bind-Value="Annotation.Content" />
            @* ^^^^^ *@
            @* Look, Ma! It works! *@
            <ValidationMessage For="() => Annotation.Content" class="invalid-feedback" />
        </div>
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
</EditForm>

This piece of code is provided “as is”, without warranty of any kind…Blah, blah, blah. Use under your own risk. Steal it.

If this helped you, you’ll love Street-Smart Coding—30 proven lessons to help you level up your coding journey.

Ten Reading Rules to Read More—and Love It

Inspired by Ryan Holiday’s reading rules, I’ve decided to come up with my own to truly enjoy reading.

I’ve had to change my mind about books. From chasing a book count to reading for action and pleasure.

Here are my 10 reading rules:

#1. Always be reading something. At least one page a day. Like Jim Kwik says, reading is like exercise for the brain. And when scrolling and distractions are the norm, reading is a revolution.

#2. Reading over phone time and social media. To reduce my phone time, I use books as conscious replacements. I leave books where I usually leave my phone to break the habit of scrolling.

And after a week off social media, I now read first thing in the morning instead of checking email or social media.

#3. Read two books at once. Balance non-fiction and fiction books. Non-fiction starts my mornings and fiction helps me unwind as night.

#4. Write a book recommendation. While reading a book, write a list of all books mentioned in the book. This is like climbing the inspiration tree as Austen Kleon says in Steal Like an Artist.

#5. Read actively. Write in margins, fold corners, underline, highlight. Your book marks can become a legacy. A book without marks is a book you haven’t read.

#6. Write a 10-idea list. I’m a big fan of 10-idea lists. Instead of note-taking apps, I write 10 ideas that resonate after finishing a book. It forces me to recall and remember what I read. Bonus point when paired with a 7-word summary.

#7. Reread. I’ve had to reconcile myself with rereading. It used to be a sin when I wanted to hit a large book count. The goal is to gain insight, not just hit a book number. Rereading is fine.

#8. Prefer physical books. They give you spatial clues to remember information. You might remember that an important concept is on a left-hand page, near the bottom. eBooks can’t beat that.

#9. No shame in piling books. From How to Be Rich by Ramit Seti, I learned we shouldn’t cut on things we love. I’m not cutting on books.

You’re the books you’ve read. And those you haven’t read yet. Just don’t let your books become “shelve-help.” Build a pile, go through it, then build another one.

#10. Binge-read your favorite writer. I did it when I found James Altucher for the first time. It shows you how their voice change and how your perspective expands.

My February Book Experiment Is Already A Success—But Still in Progress

Last February, I decided to write another book but in a completely different way.

I started from a validated idea. I turned my most popular personal development post, into a mini book. Instead of starting with the first words, I began with the outline, title, sales page, and cover. I wrote it backwards.

Here’s the final result: 10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too.

My success metric was a single sale.

Yesterday, I announced my book in my Friday Links email and one reader bought it. A small victory dance.

First sale of 10 Surprisingly Simple Ideas That Changed My Life
First sale

With that milestone reached, here’s how I’m continuing the experiment:

  1. Reach out to my “advance readers” team to get my first review on Amazon.
  2. Promote my book via a book promotion email list for more reach.

This experiment will shape my launching strategy for future books. The real works begins after the writing is done.

Friday Links: Life changes, leaving Google, and lone wolves

Hey there!

Burnout hit me hard in 2023. It changed everything.

It hurt both my mind and my body. I was at my best coding job. Then, in 2024, I was laid off. Perfect combo, right?

The way up was slow. Master plans didn’t work. Getting up felt impossible.

One quote inspired me to get up: “If you’re lost, start with your health.” That became my first step.

Small daily actions made change possible.

That journey led me to write 10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too. I’m launching it this week.

This isn’t a 7-day program or a magic formula. It’s about small daily ideas that create big change.


And as usual, here are 4 stories that resonated with me this week:

#1. I don’t use Google as my default search engine, but I still use it for email. For digital hygiene or simply for boycotting big corporations, leaving Google may improve your digital life too (7min)

#2. Hiring sucks. CVs, interviews, recruiters…But the best way to think of hiring is a two-way sales negotiation. If you’re looking for a job, here’s a piece of advice you shouldn’t miss (5min)

#3. AI is changing our job descriptions… and killing the need for junior coders. AI is splitting the dev population into expert beginners and lone wolves (8min)

#4. The next time I’m asked what superpower I’d like to have, I’d say talk to anyone in any situation. Here’s a good story from The Guardian (14min) about it.


Thanks to the internet, a February post got traction last week. In case you missed it, here are the numbers that deflate the AI hype about replacing coders—and ease FOMO (2min).


(Bzzz…Radio voice) This email was brought to you by… 10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too. If you’re ready to start small and see big change, check it out.

Until next Friday. Keep coding smartly!

Cesar

What to Do to Thrive in the AI Era

Whether you see it as a tool or a breakthrough, AI is changing jobs.

Translators are panicking. Some coders brag about “coding” from their phones. Every new tool and model fuels AI FOMO.

Real numbers about AI adoption tell one story. CEOs tell another. Maybe we’re all doomed. Predictions are hard. I hope I wasn’t wrong with mine.

Here are 2 ideas about how to AI-proof yourself:

#1. Learn to learn

From 21 Lessons for the 21st Century, the only skill to learn is the ability to learn new things.

The only certain thing is change. And being able to adapt is crucial. Pick new skills you enjoy. Bonus points if they earn money.

#2. Literacy and health

Papyrus made me think about the future.

It’s a book about the invention of books in the ancient world. But the past teaches us about the future.

In ancient Greece, free men learned to read, talk, and think, and took care of their bodies. Manual labor was for slaves. Of course, that wasn’t a good thing.

If AI takes over repetitive work, as slaves once did in ancient Greece, we’ll be like free to focus on creativity and growth.

Maybe AI will kill most knowledge jobs, because it thrives on tasks humans do and hiring people may no longer make economic sense.

Until then, while waiting for universal basic income or or something darker, I’m doubling down on writing and chasing my curiosity—simply because I enjoy it.