Blog: Hello, World Freezes Over
How I Refactored my Blazor App from Messy to Manageable
So, it's "done." Well, done enough to be usable: I’m talking about the Blazor app I’ve been working on—partly as a learning project, partly as something I eventually plan to port over to Oqtane. It’s a Blazor WASM Hosted app for managing and displaying FAQs, and it's now much more full-featured than the Blazor Server version I started with.
Where We Started
My initial app worked—mostly. I had some off-by-one bugs in the sorting logic and other fun stuff, but the real problem wasn't bugs. It was the monolith I created in Edit.razor
.
And the .razor file had ballooned to over 600 LOC! 450 of which were in the @code{} block. Yuck. It had quickly become an unmanageable mess, with repeated code, it was a mess to debug, and the more features i added the more the code turned into a bloated Jabba The Hutt-esque bit of nonsense.
It worked though, and it worked well. So...
It's Time to Refactor!
Early on, my focus was on modularizing the components: the razor/html UI stuff, not so much the logic. And boy howdy, did I learn a lot.
To talk about the power of modularizing components, even if they aren't necessarily going to be reused that often, is just cleaning up the codebase and readability. For instance, 22 lines of nested MudBlazor layout code became:
<FAQComponent faq="faq" OnEdit="EditFAQAsync" />
Much better, isn't it? It's much easier to discern what the single line does and represents, and it's much easier to read one line than 22.
I also had nested Question and Answers inside Question and Answer sets, nesting @foreach loops reaching over 100 lines of code! Yikes.
Farm it out, farm it out, farm it out. I created a QnAItem.razor component to display Questions and Answers with their related controls, and a QnASetItem.razor component to display individual Categories of Q&As. This got a bit trickier, having to implement EventCallbacks for function passing and a bit of trickiness with parameters, but it made all the difference: what was once nearly 200 LOC is now down to approximately 40.
Time to refactor the logic
Another issue I ran into was the bloat in the @code{} block. I ended up creating three service/manager classes, one for editing Categories, one for editing Q&As, and one for handling all the sorting logic.
...And of course, I forgot to inject one. That bug didn't last long, but it's a downside of the modern paradigm of files, folders, files, files, folders, and more files all split up: sometimes to get one thing to work you have to edit five different files. In this case it was five: the interface, the service, program.cs, imports.razor, and the razor.file itself (where I missed the injection). It's nice to make these mistakes in more low-stakes environments like these smaller apps though: easier to debug.
The logic being farmed out to services shrinks the razor page significantly, and makes the functions in there easier to read... and most importantly, makes the razor page mostly focus on the UI/UX of the client.
More Improvements
I added a rich text-editor and full markup for answers so you can do bullet points, links, whatever strikes your fancy with a BlazoredTextEditor, based on Quill.js. I added a dark-mode/light-mode toggle that uses LocalStorage which is very convenient. I added dialog modals for confirmation of deletion and for editing, to make sure the edit page itself didn't get too clumsy and cluttered. And I added, using JS, a helper method to scroll to certain elements in the page when things change: when you create a new Category, it scrolls automatically down to the category, etc. Overall it's gotten a thick coat of polish, but all these required clever usage of modularity in order to stop the razor file from cracking the 1000! LOC mark!
What I Learned
The power of modularity is great. Keeping UI and business logic separated (you gotta keep 'em separated!) is oh so choice.
And the payoff: from ~600 LOC in one bloated crappy file to under 200 total.
Next Steps
There's a few more functionality things I'd like to add to the app, mainly a search function and possibly meta-tags about the questions, but for now, my main goal is to port it to Oqtane. I'm going to start small, with a more interesting Hello World type app that's actually deployed before I dive into porting. Feel free to leave comments or email me if you have any experience developing Oqtane modules or just general tips in porting something like this.
So, from bloated and brittle to modular and maintainable. I even reorganized my methods based on function! Don't be afraid to refactor: it's like, idk, flossing or something. It's hard to WANT to do it, it's not particularly pleasant, but the end result makes you feel good and is good for you.
Side Note: I tried the McCrispy Strips Today
We live on a golf course, it's a few miles to civilization and the closest eatery is McDonald's so I eat too much of it generally and was really looking forward to the McCrispy strips. They are fire, and the Chili Dip was... also fire. I still miss the Wings they used to have tho!
Share Your Feedback...
Website and all content ©2025 Jake Fitzenreider
Website powered by Oqtane