C# in the Browser Without Blazor

Monday, 28 November 2022

I’ve talked about an experiment with Blazor in the past, but wanted to see how it would go building something small in C# for the browser without going through that process. With .NET 7 it doesn’t seem too hard, so I went ahead and created an example repo to demonstrate.

C# in the browser without Blazor

I am relying on “JSExport” to expose C# to the JavaScript application and have decided to make things simple by using strings for the arguments and return values as well as forgetting about how inefficient this is (for what this is actually doing).

using System.Globalization;
using System.Runtime.InteropServices.JavaScript;

public partial class Calculator
  // ...
  internal static string add(string first, string second)
    Decimal firstNumber = Decimal.Parse(first, NumberStyles.Float);
    Decimal secondNumber = Decimal.Parse(second, NumberStyles.Float);

    return (firstNumber + secondNumber).ToString();
  // ...

Using something like DotNetJS seems more appropriate for use cases like this in the short-term, and I wonder where WASI and Wasmtime .NET solutions will wind up going forward?

Using Open

Sunday, 18 April 2021

When using the terminal on my Mac, I often use the open command to get a view of the current directory in Finder and generally use it to… open up things. So, while using other operating systems, I have naturally wanted similar functionality. To my recent surprise, I found it within Haiku, and it turns out it’s coming to Debian as well!

Haiku Screenshot

The Web Is Dynamic

Saturday, 27 February 2021

HTML, JavaScript, and CSS are key parts of the Open Web Platform as they assist in creating customized, yet reusable solutions that anyone might need to build high quality, robust web applications—without requiring unnecessary complexity.

In this small example, a few support libraries work together to provide efficient templating, as well as an implementation of React’s Hooks API, allowing us to use state of the art features, while still preserving compatibility with the wider ecosystem on the Web.

With the addition of script:

(async () => {
  const { html } = await import('https://unpkg.com/lit-html@^1.0.0/lit-html.js')
  const { component, useEffect, useState } = await import('https://unpkg.com/haunted@4.7.1/haunted.js')

  function counter({ value }) {
    const [count, setCount] = useState(value);
    const handleSetCount = (val) => () => {
      setCount(isNaN(val) ? 0 : val);

    useEffect(() => {
      this.setAttribute("value", count);

    return html`
        span {
          margin: var(--x-hook-component-span-margin, inherit);
      <button type="button" @click=${handleSetCount(Number(count) - 1)}>
      <span id="count">${count}</span>
      <button type="button" @click=${handleSetCount(Number(count) + 1)}>

  if (!customElements.get("x-hook-component")) {
      component(counter, { observedAttributes: ["value"] })

Some basic styles included:

x-hook-component {
  --x-hook-component-span-margin: 0 2rem;

  align-items: center;
  background: #2b2b2b;
  color: #fff;
  display: flex;
  justify-content: center;
  margin: 1rem 0;
  padding: 1rem;

And the element rendered (with a default value included even), we can define new and interesting behavior without much trouble at all.

<x-hook-component value="5"></x-hook-component>

Being that custom elements and dynamic import are so widely supported now—adding functionality and content in this way is starting to make a lot more sense for many use cases—especially seeing how once loaded and cached, the cost to reload any support libraries seems barely noticeable. Try this example on CodeSandbox.




Web Apps Web Enabled Applications Corporate Sites Personal Sites
Preferences Dark Theme Light Theme Notifications
Social GitHub LinkedIn RSS Feed Icon