BlazorInspector 0.1.2
dotnet add package BlazorInspector --version 0.1.2
NuGet\Install-Package BlazorInspector -Version 0.1.2
<PackageReference Include="BlazorInspector" Version="0.1.2" />
<PackageVersion Include="BlazorInspector" Version="0.1.2" />
<PackageReference Include="BlazorInspector" />
paket add BlazorInspector --version 0.1.2
#r "nuget: BlazorInspector, 0.1.2"
#:package BlazorInspector@0.1.2
#addin nuget:?package=BlazorInspector&version=0.1.2
#tool nuget:?package=BlazorInspector&version=0.1.2
BlazorInspector
A Vue/React-DevTools-style in-app component inspector for Blazor WebAssembly and MAUI Blazor Hybrid. It's a small, local debugging tool I built for my own use — shared openly in case it's helpful to you too.
Open a floating panel inside your running app to browse the live component tree, inspect (and edit)
each component's parameters and state, hover the page to highlight elements, and jump straight to a
component's .razor source in VS Code.
Debug-only by design. The inspector reflects over private ASP.NET Core internals — fine for a
local debugging tool, but can be broken by IL trimming / WASM AOT. It enables itself automatically
only when your app is built in Debug (detected at runtime) and is a no-op when your app is built
in Release, so it's safe to leave the registration and <InspectorOverlay /> tag in place. Don't
force it on in Release.
Table of contents
- Who it's for
- Features
- Platform support (WASM vs MAUI Hybrid)
- Requirements
- Installation
- Quick start
- Configuration
- Jump-to-code setup
- How it works
- Limitations & known issues
- Building from source
- Contributing
- License
Who it's for
BlazorInspector is a personal, local debugging tool — built to inspect Blazor WebAssembly and MAUI Blazor Hybrid apps from inside the running app. It's shared openly in case it's useful to you, but it's intentionally small in scope and isn't trying to be a full DevTools suite.
If you want a mature, full-featured Blazor DevTools experience — a render profiler, flamegraphs, and a browser DevTools panel, and especially on Server or InteractiveAuto — reach for BlazorDeveloperTools (NuGet · blazordevelopertools.com). It's the more polished option.
Features
- Live component tree — the real parent/child hierarchy read from the renderer's component-state map (not guessed), auto-refreshing while the panel is open, with stable expansion/selection.
- Parameter & state inspection —
[Parameter],[CascadingParameter], and a State section for a component's own non-parameter fields/properties (e.g. a page's privatecurrentCount). Rich values are summarized safely:RenderFragment→<fragment>,EventCallback→<event>, collections →Type (n items), throwing getters →<error: …>. - Expandable values — drill into any value DevTools-style: expand a list/array to its items, a dictionary to its entries, or an object to its properties/fields, and keep going into nested rows. Lazy (children load only when expanded) and guarded against reference cycles, depth, breadth, and throwing getters.
- Inline editing — edit writable scalar values in place (numbers, strings, bools, enums,
Guid, dates/times), including values nested deep inside collections and objects. The change is written to the live object and the owning component re-renders, so your app's UI updates immediately. - Element picker — hover the page to highlight the element under the cursor, then click to select its component in the tree (click-to-select on Blazor WASM; hover-highlight on MAUI Hybrid).
- Jump-to-code — every component shows a
</> sourcelink that opens its.razorfile at roughly the right line in VS Code (vscode://file/...). - One codebase, both targets — identical behavior in Blazor WASM (browser) and MAUI Blazor Hybrid
(
BlazorWebView). - Tiny footprint — two lines of integration, no third-party runtime dependencies beyond
Microsoft.AspNetCore.Components.Web, and a public surface of justAddBlazorInspector(),<InspectorOverlay />, and the options type.
Platform support (WASM vs MAUI Hybrid)
Everything works identically on both targets except picker click-to-select, which is WASM-only.
| Capability | Blazor WASM | MAUI Blazor Hybrid |
|---|---|---|
| Live component tree (real parent/child hierarchy) | ✅ | ✅ |
| Parameter & state inspection | ✅ | ✅ |
| Expandable values (collections / dictionaries / objects) | ✅ | ✅ |
| Inline editing of writable scalar values | ✅ | ✅ |
Jump-to-code (</> source → VS Code) |
✅ | ✅ |
| Element picker — hover highlight | ✅ | ✅ |
| Element picker — click-to-select | ✅ | ❌ — highlight only; select from the tree |
| Auto-enable in Debug / no-op in Release | ✅ (entry assembly) | ✅ (calling-assembly fallback¹) |
Why click-to-select differs. On WASM the runtime applies render batches in JavaScript, so the
picker can decode each batch through the exposed Blazor.platform memory reader and walk Blazor's
logical-element tree (via the DOM nodes' own Symbols) to map a clicked element to its component. MAUI
Blazor Hybrid runs on the native runtime — there is no Blazor.platform and batches are marshaled
to the WebView differently — so that path isn't available and the picker stays hover-highlight only.
It degrades gracefully (never throws); you just select the component from the tree.
Applies to both targets:
- Debug-only. The inspector auto-enables in a Debug build of your app and is a complete no-op in Release (no overlay, no component-tracking activator, no framework-internals reflection).
- Reflects private framework internals (renderer component-state map; WASM batch/struct offsets in the picker). These are centralized and documented per .NET version (verified 8.0 / 9.0 / 10.0); a runtime that changes them degrades the affected feature rather than crashing.
- Editing a top-level
[Parameter]is reverted on the next parent re-render; state and nested object/collection edits persist. - Not for Blazor Server / InteractiveAuto — see Who it's for.
¹ On MAUI Android / iOS / Mac Catalyst Assembly.GetEntryAssembly() is null, so call
AddBlazorInspector() from your app project (e.g. MauiProgram.cs), not a shared Release-built
library — the calling assembly is the Debug/Release signal there.
Requirements
| .NET | 8.0, 9.0, or 10.0 (the RCL multi-targets net8.0;net9.0;net10.0) |
| Render modes | Blazor WebAssembly, MAUI Blazor Hybrid |
| Build | Debug only — auto-enables in a Debug build of your app, no-ops in Release |
| Runtime deps | none beyond Microsoft.AspNetCore.Components.Web |
Installation
Install the package into your Blazor WASM or MAUI Hybrid app:
dotnet add package BlazorInspector
Use 0.1.2 or later. 0.1.0 and 0.1.1 are deprecated and unlisted — they were compiled out of
the package and do nothing when installed from NuGet (see CHANGELOG). If you pinned an
earlier version, bump it.
Working against the source instead of the published package? Add a
ProjectReferencetoBlazorInspector/BlazorInspector.csproj(and, for jump-to-code, the source generator as anAnalyzerreference — seesamples/SampleWasm/SampleWasm.csproj).
Quick start
Two lines of integration:
// Program.cs (WASM) or MauiProgram.cs (MAUI). Auto-enables when your app is built in
// Debug; a no-op when it's built in Release — no need to wrap this in your own #if DEBUG.
builder.Services.AddBlazorInspector();
@* MainLayout.razor (or any always-rendered component) *@
<InspectorOverlay />
Run the app in Debug and click the 🔍 button in the corner.
Configuration
builder.Services.AddBlazorInspector(o =>
{
o.Corner = InspectorCorner.BottomLeft; // floating-button corner
o.RefreshInterval = TimeSpan.FromMilliseconds(750); // tree auto-refresh cadence
o.StartOpen = true; // open the panel on load
});
| Option | Type | Default | Description |
|---|---|---|---|
Corner |
InspectorCorner |
BottomRight |
Which corner the floating button/panel docks to. |
RefreshInterval |
TimeSpan |
500 ms |
How often the tree refreshes while the panel is open. |
StartOpen |
bool |
false |
Open the panel automatically when the app starts. |
Jump-to-code setup
For the </> source links to resolve, feed the consuming project's .razor files to the bundled
source generator:
<ItemGroup>
<AdditionalFiles Include="**/*.razor" />
</ItemGroup>
Type → source mapping honors per-file @namespace directives and _Imports.razor namespace
inheritance, falling back to the RootNamespace + folder convention. Links use the
vscode://file/{absolutePath}:{line} scheme, so they open in your local VS Code.
How it works
- Tracking —
AddBlazorInspector()registers a customIComponentActivatorthat observes every component instance as it's created (chaining to any pre-existing activator, e.g. bUnit's) and keeps weak references so it never extends component lifetimes. - Reading data in-process — because the overlay runs in the same .NET runtime as the components,
it reads the tree and values directly in C#. There's no client/server bridge. The parent/child
hierarchy comes from reflecting the renderer's
ComponentStatemap. - Jump-to-code — a Roslyn
IIncrementalGeneratoremits a compile-time map of component type →.razorpath/line into the consuming assembly, which the overlay reads via reflection. - JavaScript — used only for the element picker (hover highlight + click-to-select). All tree/parameter/edit functionality is pure C#.
All reflection into framework internals is centralized in
BlazorInspector/RuntimeInternals.cs behind named constants,
documented with the .NET versions they were verified against (8.0, 9.0 & 10.0). The
RuntimeInternalsTests.AllReflectedMembersResolve test fails loudly if a future runtime renames one —
re-run the test suite when bumping the target framework.
Limitations & known issues
- Debug-only / Release no-op. Trimming and WASM AOT can strip the reflected members, so the
inspector disables itself when your app is built in Release — detected at runtime from your app
assembly's
DebuggableAttribute(the entry assembly, falling back to the assembly that callsAddBlazorInspector, since on MAUI Android/iOS/Mac Catalyst there is no managed entry assembly). Don't rely on it there. (The gate is a runtime check on your app's build, not a compile-time#if DEBUGin the package — the latter would be baked in when the package is compiled and could never react to how you build your app.) Two caveats, both solvable with an explicitAddBlazorInspector(o => o.Enabled = true):- A build with
<DebugType>none</DebugType>emits noDebuggableAttribute, so it is detected as Release and the inspector stays off. - On MAUI Android/iOS, call
AddBlazorInspectordirectly from your app project (the usualMauiProgram.cs), not from a shared startup library — the calling assembly is the detection signal there, and a Release-built helper library would read as Release.
- A build with
- Element picker click-to-select is Blazor WASM only and relies on framework internals. Mapping a
clicked DOM node to a component id is done entirely in JS: the picker decodes each render batch (a
raw WASM-memory pointer) through the exposed
Blazor.platformreader using struct offsets that mirror theRenderTreelayouts (verified on .NET 8/9/10), then walks Blazor's logical-element tree — reachable via the nodes' own Symbols — to recover each component's host node. It degrades to hover-highlight only (never throws) when those internals are absent — notably on MAUI Blazor Hybrid, which runs on the native runtime with noBlazor.platformand a different batch marshaling, so the picker there highlights and you select from the tree. One WASM edge case: a single render that simultaneously inserts and reorders sibling components in the same diff can briefly mis-map until the next render; the common cases (mount/unmount, nested components, state updates) are exact. - Editing a top-level
[Parameter]is temporary. It's reverted the next time the parent re-renders and re-supplies the parameter. Editing state and nested object/collection values persists. - Reflects private internals. Member names can change across .NET versions — see the
RuntimeInternalsnote above.
Building from source
git clone https://github.com/kobrien603/BlazorInspector.git
cd BlazorInspector
dotnet build BlazorInspector.slnx # builds the RCL (net8/9/10), generator, samples, tests
dotnet test tests/BlazorInspector.Tests # unit + bUnit + source-generator tests (runs on net8/9/10)
dotnet run --project samples/SampleWasm # manual test harness in the browser
The two sample apps (samples/SampleWasm, samples/SampleMaui) are the manual integration harness;
samples/README.md has a step-by-step manual test checklist.
Contributing
Contributions are welcome — see CONTRIBUTING.md for the build/test workflow and guidelines. Open an issue to discuss larger changes first, then send a PR. By contributing you agree that your contributions are licensed under the project's Apache-2.0 license.
Release history is in CHANGELOG.md.
License
Licensed under the Apache License 2.0 — see LICENSE. It's permissive (free to use, modify, and distribute), includes an explicit patent grant, and disclaims warranty/liability.
Copyright © kobrien603.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.0)
-
net8.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.0)
-
net9.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.2 | 108 | 6/12/2026 |