Blazor WASM First Impression: Building a Game with AI and DDD
- published: November 18, 2025 estimate: 8 min read view-cnt: 2 views
It’s been a while since my last handmade-post 😆 Well, I think I’ve already given up on making a post on a daily basis Especially since I am having education recall this week PLUS trying to build my VIM-keys and Snake inspired game using Blazor wasm
I will leave the game concept to another article, and do a quick review of what I’ve learned so far
This article is going to cover two major parts, the first is to discuss the developer experience (DX) of Blazor wasm, and the second is to discuss the DX of AI-driven development
I tried putting things I remembered here; from the start of the project to the last commit I’ve made
Things will be written without a specific structure
My .NET Background
I’ve used WebForm, MVC, RazorPages in my working experience, however, I switched to other front-end stacks temporarily since 2023 (e.g. react, astro.js)
.NET 10 just came out last week on 2025-11-10, making it easily my go-to choice for this small side project
Hopefully, this project would remind me about some good old full-stack development memories
Technical Motivation
I want to start by working on the core logic of the game and a thin UI layer without bothering with the backend stuff, so starting with Blazor wasm is a reasonable choice
Also, this is a good opportunity to use domain driven development (DDD) as well where all the layers like UI, API and DB should be replaceable except for the core domain logic
First Impression
My first task when using any starter template is to clean up the stuff I don’t need at the time
So, say goodbye to built-in js, css, API, Layout, sidebar and navigation
The only tricky thing is that this file “wwwroot/index.html” is used in the build process which is not so intuitive IMHO (I am too used to JAMstack stuff 😂)
The issue persists even if you run dotnet watch; you still need to hit ctrl+R to restart the app and apply the changes
JSInterop
AI generated different ways to handle events and javascript interoperation
Since we’re trying to run DDD in this project, the expected way to deal with interop is to minimize the usage of javascript
However, things like registering keydown events to the window object or invoking arbitrary functions of DOM elements are just inevitable at this point
What we can do is add a layer of abstraction, extract commonly used components, and hide interop details from the caller
Minimize The Javascript
Avoid registering events on the top-level objects such as window, document or body if possible
Once I registered the keydown handler at the component level, I was able to eliminate the weird event registration pattern between .NET and JS
Try to declare your event this way whenever possible, Blazor already provides a clean way to delegate events on component markup with rich built-in event arguments support.
You also gain the benefit of omitting “StateHasChanged” when declaring event handlers like this.
It reminds me of React, where I avoid overly declaring useEffect by focusing my implementation on user-triggered events
AI Driven Development
Opinionated Design
The LLM gave a full-fledged game state enumeration: NotStarted, Ready, Playing, Completed
I then simplified to: Ready and Playing (since these two states are sufficient for the current gameplay)
Declaring new models everywhere
This is more of a personal preference, I tend to defer declaring new models to the last minute
I guess a polite explicit prompt would solve this problem
Weird Part
Most of the time, the LLM nails the form validation part in one shot
However, sometimes it populates two different approaches at once without a clue
Not to mention the LLM tries to reimplement those errors if we fix them quietly (sometimes you need a proper shout 🤣)
The Good Part
The best architecture is to create once, and forget it
Parts of the code I’ve never visited after the LLM generated them
These things are surely where the LLM shines (e.g. the core logic of moving a player)
The Bad
The LLM gave eval for some JSInterop tasks, I was amazed by the fact that not only was the code inelegant, but also it didn’t work at all
It wasn’t that we cannot use eval in an InvokeVoidAsync call, but the js pre-defined object arguments were not available at all
Still have no idea why the LLM came up with this solution, but it is a strong indicator that the LLM cannot work with Blazor proficiently yet
A few more instances:
- spamming
StateHasChangedin places where you can simply omit - trying to invoke unnecessary calls in LINQ
- e.g. invoke
ToList()multiple times
- e.g. invoke
- trying to create unnecessary wrapper function that is only invoked once in the codebase
- this one is WAY more troublesome than the eagerly model creation and inefficient LINQ 😭
- the satisfaction rate on code refactoring is around 2 out of 5
- I haven’t seen the LLM actually reduce lines of code (LoC) when I asked it to make code DRY
- I tried to specify all the details about actually reducing LoC which makes me doubt myself for pursuing minimalism
HTML Still Matters
Since all HTML requires a build process in Blazor, it is way more efficient for me to use the data:, protocol in a browser’s address bar
Then, do all the nitty-gritty proof of concepts there
Blazor Weirdness
I found out there are plenty of ways to declare swappable components
For now, <DynamicComponent> is good enough; Maybe one day I will hit its limitation and switch
Scoped styles require additional setup like this <link href="{PACKAGE ID/ASSEMBLY NAME}.styles.css" rel="stylesheet">
It is not a problem for most developers who don’t tamper with the starter template
However, I deleted that line of code on the first day 🤣
I purely solved this problem from reading the docs (Docs FTW!)
Blazor has amazing data binding features, however, it does not support binding to an arbitrary attribute of an element
Part of the reason is that HTML specs are a mess when it comes to “optional attributes”
e.g. checked, required, disabled… etc
HTML specs make it clear how boolean attributes should work, however, it is still confusing for developers to make the right call
Thus, when you try to bind a boolean variable to an optional attribute in Blazor, it renders the attribute with an empty string value (e.g., checked="") instead of removing it or setting it to null/false
This makes the browser read the attribute as true (which is NOT what we want)
My workaround is to add a few more JSInterop calls until it is correct
There was an error in the devtools console:
Firefox can’t establish a connection to the server at wss://localhost:45289/. aspnetcore-browser-refresh.js:350:25
It only happens when I run dotnet watch instead of dotnet run
However, it doesn’t affect the actual DX, so I just leave it there
Lastly, Blazor is kind enough to support arbitrary inline attributes rendering
However, the value is read-only (i.e. immutable), you cannot bind it to a variable
Painful DevOps!
I tried to deploy my website to Azure, however, it doesn’t support .NET 10 at the time I was trying (2025/11/14)
Thankfully, there’s not much work to do to downgrade my newly created project (no need to install additional SDK as well)
I spent almost 4 hours debugging the fingerprint problem in the file wwwroot/index.html
I tried hard using AI to troubleshoot, and ended up finding this GitHub Issue
Sadly, it doesn’t work in my case, so I removed the fingerprint from the code
I will leave this to my future self 🫠
Painful EditForm!
BIG issue with <EditForm>!
I was trying to make a form inside a dialog using all the native stuff
Turns out that getting values from a form element in .NET is not a trivial task
I don’t want to invoke new FormData(form) or form.elements via JSInterop
Thus, <EditForm> seems to be a reasonable option
Background story:
I used an invisible submit button with
<form method="dialog">and<dialog closedby="any">to achieve three different ways to submit a form
- pressing ENTER
- hitting ESC
- clicking the backdrop of a dialog
However, it breaks the existing logic where users can submit the form via a simple ENTER keystroke
The <EditForm> version doesn’t close the dialog when submitting the form
My workaround is to handle all the submit and close events manually via JSInterop in the .NET code
Which is almost same amount of work to explicitly declaring all the submission event handlers
My Own Handicap
I use LazyVim to develop the Blazor app. There’s no LSP or syntax highlighting available at all
Not to mention debugger or intellisense
Huge handicap in terms of developer experience, however, this is a good way to force myself to use more AI tools
Lastly, Claude Web UI malformats Blazor code snippets, which is also troublesome.
It is solvable by injecting some scripts or styles, but making all these extra efforts is just a bummer
Verdict
After this first week of experience, I found my mental model works like this:
- try to improve the code to meet my own taste
- find out limitations
- implement a workaround
- gradually build my own toolbox or framework on top of the existing one
Hopefully, I can keep working on this project until I reach step 4
Stay tuned for more Blazor or game development content, I will see you in the next one 😎
No comments yet
Be the first to comment!