Split out of #328 (@leoneljerez), which bundled several unrelated things. This is the headline request from it: a numeric input that does not round-trip through JS.
The request
BbNumericInput has no mode that avoids JS interop. The reporter hit this building an Excel-like editing surface on WebAssembly — many numeric editors live at once — and measured a noticeable slowdown against MudBlazor's equivalent, which is pure C#.
They asked whether a Primitives-layer numeric input exists that strips the JS, or an attribute to disable it. Neither does.
What the JS layer actually costs
Per instance, on first render: a module import (cached after the first component), a DotNetObjectReference, and four addEventListener registrations — input, blur, focus, keydown — plus wheel when EnableWheelStep is set. Each instance also needs its dispose interop call on teardown.
Per keystroke: sanitizeInput runs in JS, then dotNetRef.invokeMethodAsync('JsOnInput', value). blur, focus and the stepping keys each round-trip separately.
DisableDebounce is not the escape hatch it looks like. It does not reduce interop — it removes the setTimeout, so JsOnInput fires immediately on every keystroke rather than once after DebounceInterval (default 500ms). It exists to make bindings feel immediate, and it makes the call volume strictly worse. Worth stating plainly because it is the parameter anyone would reach for first, and the issue sat for months partly because nobody said so.
What a JS-free mode would have to give up or reimplement
The JS is not decorative — porting it to C# means reimplementing:
- Input sanitisation with cursor preservation. Strips non-numeric characters as you type and restores the caret to the right offset, accounting for removed characters before it. Doing this from
@oninput in C# means a round-trip to set SelectionStart, which is itself interop — so a naive port may not save anything.
- Full-width character folding (
foldFullWidth), which matters for CJK input methods.
- Key stepping for Arrow/PageUp/PageDown/Home/End, currently handled by a JS
keydown listener.
- Wheel stepping, with its accumulator for trackpad momentum.
A @oninput-driven C# implementation is straightforward for value binding and stepping, but the caret behaviour during sanitisation is the part that will decide whether it is actually usable.
Open questions
- Which layer? The reporter asked for a
Primitives version. That fits the library's split — headless and unstyled, consumer supplies behaviour — but the sanitisation is behaviour, not styling, so it is not obvious the primitive is the right home.
- New component or a parameter? A
DisableJsInterop-style flag on the existing component keeps one API but means two divergent code paths with subtly different typing behaviour, which is a support burden. A separate component is honest about the trade-off but duplicates surface.
- Is the cost per-instance or per-keystroke? Worth measuring before designing. If the dominant cost is per-instance setup in a grid of 200 editors, the fix might be shared listener delegation rather than removing JS.
Related
The same reporter noted that if this is not feasible, a performant editable DataGrid would address their underlying need. That is a larger piece of work but the same motivation — many simultaneous editors — and worth keeping in mind when scoping this.
Split out of #328 (@leoneljerez), which bundled several unrelated things. This is the headline request from it: a numeric input that does not round-trip through JS.
The request
BbNumericInputhas no mode that avoids JS interop. The reporter hit this building an Excel-like editing surface on WebAssembly — many numeric editors live at once — and measured a noticeable slowdown against MudBlazor's equivalent, which is pure C#.They asked whether a
Primitives-layer numeric input exists that strips the JS, or an attribute to disable it. Neither does.What the JS layer actually costs
Per instance, on first render: a module import (cached after the first component), a
DotNetObjectReference, and fouraddEventListenerregistrations —input,blur,focus,keydown— pluswheelwhenEnableWheelStepis set. Each instance also needs itsdisposeinterop call on teardown.Per keystroke:
sanitizeInputruns in JS, thendotNetRef.invokeMethodAsync('JsOnInput', value).blur,focusand the stepping keys each round-trip separately.DisableDebounceis not the escape hatch it looks like. It does not reduce interop — it removes thesetTimeout, soJsOnInputfires immediately on every keystroke rather than once afterDebounceInterval(default 500ms). It exists to make bindings feel immediate, and it makes the call volume strictly worse. Worth stating plainly because it is the parameter anyone would reach for first, and the issue sat for months partly because nobody said so.What a JS-free mode would have to give up or reimplement
The JS is not decorative — porting it to C# means reimplementing:
@oninputin C# means a round-trip to setSelectionStart, which is itself interop — so a naive port may not save anything.foldFullWidth), which matters for CJK input methods.keydownlistener.A
@oninput-driven C# implementation is straightforward for value binding and stepping, but the caret behaviour during sanitisation is the part that will decide whether it is actually usable.Open questions
Primitivesversion. That fits the library's split — headless and unstyled, consumer supplies behaviour — but the sanitisation is behaviour, not styling, so it is not obvious the primitive is the right home.DisableJsInterop-style flag on the existing component keeps one API but means two divergent code paths with subtly different typing behaviour, which is a support burden. A separate component is honest about the trade-off but duplicates surface.Related
The same reporter noted that if this is not feasible, a performant editable DataGrid would address their underlying need. That is a larger piece of work but the same motivation — many simultaneous editors — and worth keeping in mind when scoping this.