r/Blazor 2d ago

I built a free tool that maps your Blazor app's C#<->JS interop and Web->API calls as a graph

9 Upvotes

If you've ever tried to answer "what actually calls this JS function from C#?" or "which API endpoint does this component hit?" and ended up grepping across .razor, .cs, and .js files that don't reference each other — that's the itch I was scratching.

EdgeHop indexes your solution into a code graph using the compiler's semantic model (Roslyn + a native oxc pass for the JS/TS), and it resolves the edges that normally break tooling:

  • C# -> JS (IJSRuntime.InvokeAsync -> the JS export) and JS -> C# (DotNet.invoke* -> your [JSInvokable] method)
  • Web -> API — a typed HttpClient call linked to the minimal-API/controller action serving that route, even with no project reference between them
  • plus the usual: callers, interface implementations, RENDERS (which component renders which), type hierarchy, and shortest-path/impact queries

It runs locally (SQLite, no server, no credentials), and it plugs into Claude Code / other MCP clients so an AI agent can query your app's structure directly — but the CLI works standalone too.

Free and open source (Apache-2.0): https://github.com/EdgeHop/EdgeHop — there's a demo GIF in the README showing a UI-handler -> HTTP-client -> endpoint trace. Feedback from actual Blazor folks very welcome.


r/Blazor 1d ago

Hiring https://ichim.github.io/MapsForBlazor/

Post image
0 Upvotes

https://ichim.github.io/MapsForBlazor/

MapsForBlazor nuget and dashboard.


r/Blazor 1d ago

Blazor vs React: Why C# Developers Are Switching (3-Minute Explainer)

Thumbnail
youtu.be
0 Upvotes

r/Blazor 3d ago

Improved Flamegraph on Blazor Developer Tools v1.0.0-beta.6

Thumbnail
gallery
14 Upvotes

Sharing an update on Blazor Developer Tools (free & open source, Apache 2.0): improvements to flamegraph (and other bug fixes).

I had an issue with the flamegraph. Blazor events are measured in milliseconds, so even half a second of silence stretched the axis so much that the actual events shrank to slivers that were hard to navigate.

I took ideas from subway maps: they tell you where you are relative to other points without being true to distances. I wanted something like that, without losing the timing information entirely.

The new default view for the flamegraph is "Sequence". It preserves order with uniform spacing but flags real pauses with "+1.2s" markers so the time information is still there. I think that for us developers, this is the more useful view since you can see where events happen relative to others. But if you need a true scientific view with total numbers you now have 2 options:

- Time (linear) : the real timeline.

- Time (idle collapsed) : same, but long quiet stretches are clipped out and replaced with a ✂ marker showing how much time passed.

There is an update for both the nuget and extension.

I'm currently working on the element picker tool and closing the list of features that React dev tools offers. I'm hoping to deliver a follow up update of those in a week.

GitHub: github.com/joe-gregory/blazor-devtools


r/Blazor 3d ago

How to replace the default Blazor Router with a React/Vue-like powerful router in just 5 lines of code in existing project?

19 Upvotes

Have you ever wanted to Keep-Alive certain pages so that everything doesn't get destroyed and rebuilt every time the user navigates back?

For example, consider this common scenario: a user searches for something in a Products DataGrid, sorts a specific column, navigates to page 3, clicks the "Edit" button to modify an item on a separate page, and then clicks back to return to the Products list. You usually only have two choices to preserve this state:

  1. Painstakingly bind every single component state to equivalent properties and manage it all via a State Manager.
  2. Let a powerful router handle all of it for you out of the box, without writing a single extra line of state-tracking code.

On top of that, what if you want native Nested Routing or modern browser-native View Transitions or strongly typed navigations?

Bit.Brouter is a lightweight, fully open-source, and highly capable library designed to supercharge your Blazor application's UX while providing extensive coding flexibility.

The best part? You don't need to rewrite or re-architect your existing project. It provides awesome default features from the very first second, allowing you to opt-in and use advanced capabilities whenever you need them.

Step 1: Install the Package

First, install the NuGet package:

dotnet add package Bit.Brouter

Step 2: Register the Service

Add the required services in your Program.cs:

builder.Services.AddBitBrouterServices();

Step 3: Update Routes.razor

Replace the default Blazor <Router> component with <Brouter>, and wrap your fallback page inside the new <NotFound> tag:

<NotFound>
    <NotFound />
</NotFound>

Setup complete!

Now, let’s unlock its full potential:

Now, if you want a page to stay alive (just like the Products page scenario we discussed earlier) simply define it explicitly like this:

<Routes>
    <Broute KeepAlive Path="/counter" Component="@typeof(MauiApp1.Shared.Pages.Counter)" />
</Routes>

The Final Look of Routes.razor

Here is how your complete Routes.razor will look:

<Brouter AppAssembly="typeof(Layout.MainLayout).Assembly">
    <Routes>
        <Broute KeepAlive Path="/counter" Component="@typeof(MauiApp1.Shared.Pages.Counter)" />
    </Routes>

    <Found Context="routeData">
        <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
        <FocusOnNavigate RouteData="routeData" Selector="h1" />
    </Found>

    <NotFound>
        <NotFound />
    </NotFound>
</Brouter>

Handling Lifecycle on Keep-Alive Pages

Naturally, for a page that is kept alive, OnInitializedAsync won't be triggered every time the user revisits it.

If you need to fetch fresh data or trigger actions upon re-entry, simply inherit your page component from BrouterRouteBase and override the OnActivatedAsync method:

@code {
    protected override async Task OnActivatedAsync(BrouterRouteActivation activation)
    {
        if (activation.IsFirstActivation is false)
        {
            // This runs on subsequent navigations back to this component
        }
        await base.OnActivatedAsync(activation);
    }
}

To explore Nested Routing, view transitions, strongly typed navigations and other advanced configurations, check out the official documentation at https://brouter.bitplatform.dev!

Repo: https://github.com/bitfoundation/bitplatform/tree/develop/src/Brouter


r/Blazor 3d ago

I Created One Blazor UI Across MAUI Hybrid, Photino, and Blazor Server on .NET 10

16 Upvotes

I built MQTTProbe, a free and open source MQTT and Sparkplug B client for industrial IoT work.

I'm not here to focus on MQTT, but the part I thought ya'll might find interesting is that the entire UI lives in one Razor Class Library. That same UI gets released on Windows, macOS, Android, Linux, and as a self hosted web app (which is also published to docker). iOS works too, but I haven't published it yet.

The project targets .NET 10. Each platform is basically a thin host around the same Blazor components and services.

The shared RCL contains all the pages, components, and most of the services. It uses MudBlazor, Lucide icons, and MQTTnet.

MAUI Blazor Hybrid handles Windows, Android, and iOS.

Photino handles the macOS and Linux desktop versions. It is a normal native process hosting WKWebView on macOS and WebKitGTK on Linux. The macOS build ships as a signed and notarized universal package. Linux ships as an AppImage.

Blazor Server powers the self hosted web version, with cookie authentication. It can run through Docker or directly on the machine.

Velopack handles desktop updates, and bUnit covers a good chunk of the UI test suite.

Sharing the UI through an RCL really does seem to have work well. The important part is keeping platform specific behavior behind interfaces from the start. Every time I took a shortcut and checked the current platform from shared code, I ended up regretting it later.

Blazor also handled heavier rendering loads than I expected. A busy MQTT broker can throw thousands of messages per second at the topic tree. Virtualization helped a lot, but being careful with ShouldRender made the biggest difference. Getting it smooth took some iterations, but it did get smooth.

I originally used Mac Catalyst for the macOS version. That fell apart when I added Velopack because Catalyst runs the app as a UIKit process, which Velopack cannot update correctly. The app would crash on startup.

I replaced that head with Photino and was surprised by how little shared code had to change. That was probably the point where the RCL approach really proved itself. Photino was also what made a proper Linux desktop release practical for me.

Blazor was not the painful part. The ugly stuff was packaging. macOS signing and notarization, AppImage quirks, certificate stores, and all the little differences between operating systems took more time than the shared UI did.

The repo is here:

https://github.com/bluegrassiot/mqttprobe

The split between the shared RCL and the platform heads is pretty easy to see in the project structure.

Happy to answer questions about the setup, using MudBlazor in a larger app, Photino versus MAUI, or testing the UI with bUnit. I would also like to hear from anyone who has shipped a Blazor desktop app using Electron.NET, Avalonia with a WebView, WPF Hybrid, or something else.


r/Blazor 3d ago

bitplatform 10.5.0 (13 new components, advanced Blazor router, motion library, multi-tenancy and more!)

Thumbnail
2 Upvotes

r/Blazor 3d ago

Stop Using Slow Regex + Bonus Scambaiting call

Thumbnail
youtu.be
0 Upvotes

r/Blazor 4d ago

unplugin-dotnet-wasm - Bundling .NET WebAssembly apps with any JS bundler

Thumbnail
3 Upvotes

r/Blazor 5d ago

SigmaDroneChart & MapsForBlazor

Post image
8 Upvotes

SigmaDroneChart (sum of charts) is as customizable GaugeChart.

https://ichim.github.io/MapsForBlazor/


r/Blazor 5d ago

.NET 10 Blazor Server app that runs even on IE11!

Thumbnail github.com
14 Upvotes

I know this may sound like a silly project, but please read on.

Blazor Server only runs on modern browsers. That's normal, but I think it's a shame. Blazor Server makes it extremely easy to build interactive web apps, so even non-IT teams can deploy them easily (like at my company). However, those companies often keep using legacy assets and can't update their browsers.

So I tried making Blazor Server work on older browsers. It worked, and I want to share the result.

  • Works on IE11 and even very old Chrome.
  • Very easy to use. Add one library, or download the JS file and load it instead of the normal blazor.web.js.
  • Includes auto-update. That means as long as the upstream code structure doesn't change drastically, updates are provided without me having to maintain it constantly.

https://github.com/arika0093/LegacyBlazorJs

I also prepared a demo site. Most people probably don't have an old browser, so try it in Edge IE mode. It should work!

https://legacy-blazor-js.app.eclairs.cc/


r/Blazor 6d ago

Commercial Diagnostics for Blazor made simple

8 Upvotes

Add realtime diagnostics to your blazor project in one line of code:

builder.Services.AddBlazrlytics();

https://blazrlytics.com


r/Blazor 6d ago

Blazor Ramp - Action Popover, made the old fashioned way.

5 Upvotes

If you are expecting to be amazed by my skills at creating prompts so AI can build something, sorry, this post is probably not for you. Given the almost daily barrage of AI-generated components, I'm sure there are plenty of those posts around already.

If, like me, you still enjoy building things by hand, using knowledge and experience gained over the years, solving problems across accessibility, CSS, browsers, JavaScript, C# and Blazor, then there may be the odd titbit in here.

If not, TL;DR: I've released an accessible Actions Popover component. A viewable working version is available on both the test site and documentation site linked at the end of the post.

So, what the hell is an Actions Popover?

It's a component that looks like a button and, when activated, opens a panel above the page content. That panel can contain any number of actions, either buttons or links, which then go off and do whatever you've asked them to do. Both expose Func callbacks and the links also have a PreventDefault parameter if you want to prevent the automatic navigation behaviour and handle the navigation manually.

The component itself is built using the relatively new Popover API, so you get a lot of accessibility behaviour for free from the browser. The panel is positioned using CSS Anchor Positioning, complete with fallback positions when there isn't enough room in the preferred location. Again, all courtesy of the browser rather than me having to write a load of JavaScript.

From an accessibility standpoint I really didn't have to do a great deal beyond understanding how these browser features work (and I had already used them in other components). The Popover API already handles Escape, light dismiss when you click outside the component, focus returning to the trigger and so on. One thing I did change was what happens when you tab away from the popover. The browser deliberately leaves it open, which I think is perfectly reasonable for something like a non-modal dialog. For what is essentially a row actions flyout though, it just ends up obscuring whatever is beneath it, so I added a small amount of JavaScript to close it when focus leaves the popover.

Now for the M-word - Menu.

I deliberately did not call this a popover menu because it doesn't implement the accessibility menu pattern. My last production version of this type of component did exactly that. It supported nested menus, the full keyboard interaction model, all the expected and optional key bindings, pretty much the whole nine yards. It wasn't especially difficult to build, but after looking back through the applications that actually used it, the largest menu I found contained four actions on a single panel. So much for N-level deep menus.

Unless you're building something like a browser-based editor with proper File, Edit and View menus, you generally don't need the menu accessibility pattern. A simple list of buttons and links is usually sufficient.

Some of you who have followed my previous posts might remember that a few weeks ago I released a NavGroup component for side navigation using the disclosure pattern. That one can be nested to N levels. I was in two minds whether to make this component support nested popovers from day one as well, but decided against it for now. No doubt I'll add that capability at some point, but before then I suspect I'll build a popover version of NavGroup for top navigation that overlays page content. l'll probably end up with another imaginatively named component, perhaps PopoverNavGroup.

One thing I thought might be worth sharing is something I learnt while using the Popover API.

Normally you add a popovertarget attribute to the button that opens the popover, pointing at the element's ID. The browser then takes care of almost everything. The popover starts hidden, clicking the trigger opens it, clicking the trigger again closes it, pressing Escape closes it and returns focus to the trigger, and clicking outside dismisses it. All of that simply by adding an attribute.

It gets better though. It's perfectly valid to put popovertarget on the action buttons inside the popover as well, meaning that when you click one, your handler runs and the browser closes the popover automatically. No JavaScript required.

That's exactly how I originally built it.

I then started all my usual manual accessibility tests with the screen readers, JAWS, NVDA and Narrator all paired with Edge, Chrome and Firefox. TalkBack with Chrome on Android. VoiceOver with Safari on iOS. Everything behaved perfectly.

Then I tried VoiceOver on macOS with Safari :¬(

Every action button was announced correctly, but VoiceOver also announced "expanded", which is actually the state of the trigger button rather than the action itself. At first I assumed this was simply a VoiceOver quirk, but I kept digging. I even tried VoiceOver with other browsers on macOS, and they all behaved as I'd originally expected.

The interesting annoying part was that VoiceOver paired with Safari exposed the trigger's expanded state to the action buttons because of the popovertarget relationship. None of the other browser and screen reader combinations I tested announced it, despite behaving correctly in every other respect. Whether that's because they deliberately suppress redundant information or simply implement the accessibility mapping differently, I couldn't say. The end result was that VoiceOver with Safari was the only combination where the extra announcement became noticeable, so I removed the popovertarget attribute from the action buttons and closed the popover with JavaScript instead.

I think I spent longer figuring that out than I did writing the component itself.

Still, that's development, I guess. As annoying as it was at the time, I learnt something new that may come in handy on another rainy day.

Another small thing worth mentioning is that the callbacks raised by ActionPopoverButton and ActionPopopverLink use a Func rather than an EventCallback. I do this quite often when I don't want the parent component to automatically re-render simply because it handled an event raised by one of its children. As you know, if the parent renders then that cascades down to all the children. Sometimes that's exactly what you want, other times it's completely unnecessary. Using a Func lets the developer decide whether to call StateHasChanged() or not. It's a useful little trick to have up your sleeve.

With this release I now have most of the building blocks I generally use around a data table. There's an accessible debounce filter, accessible pagination and now accessible row actions, all manually tested with numerous screen reader and browser combinations, along with voice control software. I suppose that means I should finally start work on a data table component.

Notice I said data table, not data grid.

Accessibility makes quite a distinction between those two. Just like the M-word, there's also a G-word. Once you decide you're building a grid, you're effectively saying you're building something closer to Excel, complete with the mountain of expected keyboard interactions that go with it. A normal HTML table can still have sortable columns, filters, selectable rows, editable inputs, row actions and everything else most business applications need, all without pretending to be a spreadsheet.

One final thing I mention from time to time. Internally I use BEM naming throughout, for my internal css classes , but I don't expose CSS classes or class parameters on any of the components. Everything is driven by CSS custom properties defined in the Core package, which every component references. The documentation already lists every CSS variable used by each component, along with every variable defined in Core. What I need to add at some point is a basic theme builder to make it a little easier for you. Pick a primary colour, tweak things like border radius and a handful of other settings, then have it list the handful of CSS variables and values that you'd need to copy paste into your own stylesheet for your instant light and dark themes etc.

Anyway, that's enough from me for this one.

Fire up a screen reader and have a play with the Action Popover. The test site is more geared up for use with assistive tech with instructions and what to expect; the doc site has a more life like example on each components usage page.

Test site: https://blazorramp.uk

Docs: https://docs.blazorramp.uk

Repo: https://github.com/BlazorRamp/Components

Regards,

Paul


r/Blazor 7d ago

I built an offline Markdown viewer with Photino + Blazor + Fluent UI on .NET 10 (MIT)

Thumbnail
github.com
24 Upvotes

I wanted a fast, native-feeling Markdown viewer (not another editor) that renders a .md file into a clean, navigable document and works fully offline. So I built MarkdownBlaze. It's MIT-licensed and open source.

The interesting part for this sub is probably the stack:

  • .NET 10 with PhotinoX.Blazor — Blazor UI hosted in the native OS WebView (WebView2 on Windows, WebKitGTK on Linux) instead of shipping a whole Chromium runtime like Electron. Self-contained binaries stay small.

  • Microsoft Fluent UI Blazor for the shell — resizable/pinnable sidebar, toolbar, Light/Dark/System theming via Fluent design tokens.

  • Markdig for CommonMark + advanced extensions, plus a small extensions package I split out (bwets.Markdig.Extensions) for admonitions in both Docusaurus (:::tip) and MkDocs (!!! note) syntaxes.

  • highlight.js and Mermaid bundled locally so syntax highlighting (~190 languages) and diagrams work with zero network access.

Some features that took the most work:

  • Smart link rewriting relative to the opened file — extension-less wiki links resolve to .md, folder links resolve to index.md, local links open in-app while external links go to the system browser. Basically browse a docs folder like a mini wiki.

  • Auto-refresh on file change, back/forward session history + a persisted global history.

  • Local images inlined as data URIs so they render inside the WebView.

It runs on Windows and Linux (.deb / AUR packaging included).

Honestly the biggest takeaway was how viable Photino + Blazor is as a lightweight desktop alternative to Electron if you're already in the .NET world — the WebView interop and Fluent theming were smoother than I expected.

Source (MIT): https://github.com/bwets/MarkdownBlaze

Microsoft Store: https://apps.microsoft.com/store/detail/9P9VDVST2980?cid=DevShareMRDPCS

Happy to answer questions about the Photino setup, the WebView interop, or the Markdig extension work.


r/Blazor 8d ago

Commercial How I Built an Interactive Template Editor for SaaS Using Blazor [Series]

Thumbnail
htmlcsstoimage.com
11 Upvotes

I mentioned a couple weeks ago that we launched our new Template Editor, and it is a Blazor WASM app (within our Blazor Static site).

I also wrote a series of posts about how it works with a ton of code samples, internal considerations and more.

It turned from one reallllllly long post into 8 shorter (but still not short) posts.

TLDR:

Post Description
Building a Visual Template Editor in Blazor WASM A high-level tour of the decisions, architecture, and shared systems behind the editor.
Building Consistent Blazor UX with Source Generators Turning typed C# models into consistent property controls, previews, and rendered blocks with source generators.
Undo, Redo, and Live Preview - No Diffs Needed Compact property addresses, generated tracking facades, transactions, undo/redo, and preview state.
Why Our Blazor App Still Uses TypeScript A practical split between Blazor-owned application state and TypeScript-owned browser behavior.
Beyond Text Replacement: Building Typed Template Variables Keeping Handlebars for content while extending variables to colors, sizes, images, and other typed properties.
Building a System to Generate Complex CSS Generating deterministic CSS for nested blocks, panels, previews, and both browser and server renderers.
Simplifying Complexity: MemoryPack to the Rescue MemoryPack storage, read-time schema repair, and typed MagicOnion communication across browser and server.
Productionizing Our Blazor WebAssembly Editor Runtime performance, trimming, loading experience, and browser-backed testing for a production Blazor WASM editor.

Hope you like it! Would love to know what you think, if there's anything you'd like more info on - or if you think something is dumb (and why ideally).


r/Blazor 9d ago

FluentKit - WinUI 3 Design Component Library for Blazor

Thumbnail
gallery
22 Upvotes

You might call this AI slop, Sure! Because it really is... Like 90% Vibe coded.

Just thought sharing it here because why not (no reason). Open Source, Heavily relied on Fluent Svelte & Microsoft.UI.Xaml & WinUI 3 Gallery.

Includes basic WinUI controls.

The most notable features are almost real-behaving Mica ( With live, base wallpaper change support ) & Acrylic (I mean, not that impressive).

Open source, if you want you can check out at: https://github.com/VibeNoobNotFound/FluentKit/

Live Gallery: https://vibenoobnotfound.github.io/FluentKit/

(I was not exactly sure whether to put Commercial flair here because it's OSS, I earn nothing from it.)


r/Blazor 9d ago

MapsForBlazor

18 Upvotes

Maps for Blazor is a library that provides components for displaying maps in Blazor applications. It supports various map providers (Esri, Leaflet) and allows developers to easily integrate interactive maps, without any JavaScript settings, into their Blazor projects. One code, one blazor component and many technologies.

KEYWORDS: Minimizing Invoke callers to JavaScript, No JavaScript specific settings, no script references, no css links. One code, one component many technologies.

Https://ichim.github.io/MapsForBlazor/


r/Blazor 9d ago

MapsForBlazor

Thumbnail ichim.github.io
7 Upvotes

Maps for Blazor is a library that provides components for displaying maps in Blazor applications. It supports various map providers (Esri, Leaflet) and allows developers to easily integrate interactive maps, without any JavaScript settings, into their Blazor projects. One code, one blazor component and many technologies.


r/Blazor 11d ago

Blazor Server enhanced nav silently hangs

5 Upvotes

I chased signup drops on my Blazor Server app for a while today. The log in button was taking almost a minute to reach the damn login page. People clicked, nothing happened, they left. No error anywhere.

The button is just an anchor pointing at /account/login. The endpoint was doing an OIDC challenge so it 302s to our Auth0 tenant on a different domain. If you hit the endpoint directly then the redirect is instant ~ 200ms.

Unfortunately, enhanced navigation intercepts internal anchor clicks and fetches the page instead of doing a full load and when that fetch follows a 302 to a different origin, it can't swap in a cross-origin document, so it just sits there. I measured 60-90s before it gave up.

Adding one attribute, the data-enhance-nav="false" on the auth links worked and now it's instantaneous.

Posting as more a PSA since I had this one sitting on my site for a week or more and lost a bunch of signups because of it.


r/Blazor 10d ago

What is your workflow with Blazor and AI tools ATM?

2 Upvotes

So we have a few internal tools and one external site running Blazor Server (architected with option to deploy as Blazor WASM) we have been developing for the last few years.

All good, I enjoy Blazor and since switching to Blazor Server for dev, hot reload and debugging is much smoother.

However, with claude and all that shit, having some of the non-engineering (but still technical) team write their own tools is becoming a thing. Currently I sandbox them with nice boundaries, but it is a bit of a wild west in terms of structure and frameworks being used.

To rectify this, I'm trying to come up with some framework guidelines, coding style, and libraries to use etc so that at least the code is somewhat familiar from tool to tool.

Most of the stuff they've done already ends up using react, simply because that is what claude decides to use. And initial impressions are that we should be using a js fraemwork because that is what the latest AI tooling needs?

I could be wrong here, but feeling the push to adopt a js framework along side blazor to best enable vibe coding these internal tools. E.g. they love things like v0(?) where you can click on a ui element and tell it to modify it.

So... are we being forced into js land? What AI assist tooling is working for you all with Blazor? Any Blazor specific niggles?


r/Blazor 11d ago

Blazor Failed to Mute Audio in Video Element.

0 Upvotes

html <video class="w-full h-full object-cover" autoplay loop muted> <source src="@SelectedFile.BlobUrl" type="video/mp4"/> </video>

Blazor in video element i am using muted attribute still the audio is playing and i try this muted]="'muted'" still the audio was playing, they have any options to fix this issue without using JSRuntime ?

SOLVED: oncanplay="this.muted=true"


r/Blazor 14d ago

Zits UI + Navius UI a component framework for .NET Blazor 100% Free and Open Source (inspired by shadcn & Base UI)

14 Upvotes

Just shipped a component framework for .NET Blazor. Two sites:

What it is:
A full component system for Blazor not just styled buttons, but components, docs, behavior, and developer experience built as one coherent system. Design-first, inspired by shadcn and Base UI.

What's included:

  • A library of composable components
  • Documentation for each one
  • Consistent behavior and structure across the system
  • A focus on developer experience clean APIs, sensible defaults

It's still early, so there are rough edges. If you build with Blazor, I'd love feedback what's missing, what's confusing, and what you'd actually use in a real project. Would love testimonials as well if its something you guys would use as well :) .


r/Blazor 14d ago

Are component libraries really necessary?

0 Upvotes

I've been building a new product and to this point, mainly working on proving out the concept which is focused on the backend. I know the frontend is coming and so I've been mentally preparing for that.

A genuine question; how necessary are Blazor component libraries in the current world of AI assistance?

I'm reluctant to invest my code base into a component library in general. I'm more likely to build my own components as I go, and I'd imagine today it's far easier to do that with the ability to ask a coding model to build a contained component for a specific thing.

The way new component libraries seem to pop up weekly, I imagine I'm not far off on how easy it is to create them with AI tools.

Where are you guys at with respect to component libraries currently? Are you sticking with the few ogs, are you adopting those popping up more recently, building your own as needed, or something else altogether?


r/Blazor 15d ago

Blazor Ramp - Did Somebody Page Me?

1 Upvotes

The last few weeks on the open source project have been, well, let's just say trying at best, but I've finally released a pager.

It's been on the roadmap since January, I just had other things to build first. The original plan was to dust off one of the pagers I've built over the years, tweak it for Blazor Ramp, test it with the usual browsers and screen readers and ship it. However, between then and now, I've had debates with some accessibility folks, which made me rethink my whole approach.

This post is partly a release announcement and partly a look at the process you go through when building an accessible component yourself, rather than just asking a friendly AI to build what it thinks might be accessible. If this is not your cup of tea then best you go read a different post, I did warn you.

I suspect you have used and/or built many pagers over the years, but have you really thought about them before, why we build them like we do?

The ones I used to build are the typical ones, you know, previous and next at each end, numbered selectors in between, an ellipsis for the gaps in the page sequence when you have lots of pages, with the current page always being in the middle of the pager. These buttons or links are usually list items in an unordered list inside a nav element, I am sure you know the type I mean.

A screen reader user, not only has the Tab key but has other means to get around a page, lists of headings, landmarks and interactive elements that they can jump directly to. However, a keyboard-only user doesn't have any of that; they've just got a Tab key, so unless you implement custom keyboard support to get to the other side of a pager, they have to go through it.

This then raises the first important question, for a pager, what keys should move you around it? Just the Tab or the arrow/home and end keys?

I have seen pagers that use a roving-tabindex approach - tab to the pager, then use the arrow, home and end keys from there. And with the roving index approach you tab to get to the pager and tab to leave it for the next focusable element on the page. This approach makes it easy for a keyboard-only user to skip the entire pager if need be, tab on tab off (Mr Miyagi style).

This is fine for keyboard-only users, provided you tell them what keys to use (as most users are not psychic) but it's a different story for screen reader users. Screen readers take over the arrow keys, as these are used for reading a page, so for these users, they would need to switch from browse to forms/focus mode (in the case of Windows-based screen readers) or use shortcut keys to pass the arrow keys through to the pager. What's good for one group of users may not be good for another and you should think about all of this during the design process. If you do go the custom keyboard route, you'd better put instructions on the page so users know what keys to use, otherwise you're setting yourself up to fail any WCAG audit that may be conducted, irrespective of annoying your screen reader users.

Now, what about that unordered list, why are the links or buttons even in a list, what does that list give you, ever thought about that?

For a screen reader user, a list gives you a count of items. The question I started to ask myself after using screen readers is whether or not that count is actually useful on a pager and is it even honest. A pager that has six buttons for six pages, fine, it lines up but most pagers I use sit under data tables with tens or hundreds of pages — so what does hearing "list, 6 items" actually tell you, beyond there being 6 items in a list? It tells you nothing about how many pages there really are, and nothing about what page it will take you to, until you land on it. Some will say, semantically it's a grouping of related items so a list makes sense, I would question that for just a simple pager as the nav element itself is a container for navigation items.

This is the bit that gets me: most of us do things because it's what we've always done, and is now what some people just tell AI to build for them, without ever asking why.

For most LOB apps, if you actually use a screen reader you may wonder what benefit it gives you if any, please do not trust me, go try for yourself and use your own judgement. My thought is that for a pager a list is just unnecessary noise.

What about those numbered items for pages, are they even necessary?

At this point you may be thinking this guy has lost the plot, it's a pager, obviously we need those numbers - Are you sure?

A book index gives you a page number because the content on that page is fixed. A pager under a data table isn't like that (I am assuming that's where the use of page numbers came from?) - its data is most likely coming from a database with rows constantly being added, updated, and deleted, so you generally don't know what's actually going to be on page X of Y. The only way I know of to reduce that uncertainty would be to stop any database changes or give people search and filtering mechanisms to reduce the number of pages, ideally to one so they have less to scan. And before you say "but what about jumping straight to page 100 of 200", that's not really a pagination problem, that's a filtering problem. No amount of numbered selectors fixes a data set that's too big to page through comfortably in the first place, and you still have no idea what is actually on page 100.

I am hoping at this point, I have made you think, and it does not matter if you agree or disagree with anything I have said, the point is you are thinking and this is exactly what you need to do when creating accessible components.

So far, I haven't even touched on the technical side of things, especially given we are working with Blazor and the way it works with the DOM and its diffing mechanisms. I mention this because lately I have seen way too many people post on Reddit, AI-generated libraries/NuGet packages all claiming their work is accessible, not because they know anything about accessibility, but because they have checked it against axe.core and it passed (any ideas why it did not pass my simple manual checks?). I cannot stress this enough, tools like axe.core are great and do help but they only handle approximately 40% of what you need to know for WCAG if that's important to you, for me it's about usability/accessibility-first and then making sure I tick the WCAG SC boxes.

At the start of this post I mentioned the last few weeks have been trying at best. I am not going to go into detail as sometimes the only way to learn and have it sink in is to discover it for yourselves. Screen readers have to access the DOM. They do cache and monitor various things, so when working with Server with and its DOM handling and diffing mechanism, things can go awry. Axe.core, Playwright or even your friendly AI are not going to help you find these types of issues that can render your component unusable, the only way to discover these is with old fashioned manual testing with a screen reader, you know the same thing that some of your audience might have to use, not by choice but out of necessity.

After all this, what's my pager like?

It's a nav element that contains four or just two visually labelled selectors (links or buttons) with the page information above them, sighted users can see the information, screen reader users have it announced. On entering the pager, which has the aria-labelledby attribute on the nav element, screen reader users will hear the pager's name and its current state. When you activate a selector, that same on-screen text gets announced via the Blazor Ramp Live Region Service so all users get exactly the same information. For a typical pager with all four selectors, you would see First, Previous, Next and Last selectors, but for a data set with only a few pages, you may ditch First and Last, so it's just Previous and Next. Just tab stops, no custom keyboard support and/or user manual required.

I am sure this will not be to everyone's taste, which is fine, there are more than one way to skin a cat, but I am happy enough with it to have included it in my library.

As much as I may waffle on about stuff, the proof of the pudding is always in the tasting, so irrespective of your views, next time you need a pager component or are looking for ideas when building your own, fire up a screen reader and go play around with my examples.

You can see/hear the pagers in isolation on both the test and doc sites, with a more realistic example shown on the Pagers usage page on the doc site.

GitHub repo: https://github.com/BlazorRamp/Components
Test site: https://blazorramp.uk (pager in isolation examples)
Doc site: https://docs.blazorramp.uk (pager on a filtered data table example)

Regards

Paul


r/Blazor 15d ago

Always error after some time in razor.g.cs

0 Upvotes

so I always get an "Unexpected )" after a while in a _razor.g.cs file.
I just removed a line, the code should work but somehow VS messed up the .g.cs file.

This is the .g.cs file: https://pastecode.io/s/v5izfiao

And the normal razor file related to it: https://pastecode.io/s/sm2x2hv3

SOLVED

it was an empty @onclick method