How It Works
A scannable reference for numora-react's event handlers, sanitization steps, formatting stages, and value output. For the long-form narrative with diagrams and demos, see Anatomy of a Numeric Input.
React integration
NumoraInput is a forwardRef functional component that calls the core event handlers directly - no vanilla class is instantiated. Props map to core options, and onChange fires after every pipeline cycle with a synthetic event whose target exposes both the raw and formatted values.
Events
numora-react intercepts four input vectors on a single <input type="text">. See Anatomy: event pipeline for the lifecycle and timing of each.
keydown- skips the cursor over thousand separators on Delete / Backspace.beforeinput- primary formatting hook. Registered via nativeaddEventListener(see why it bypasses React's synthetic system), callspreventDefault()and writes the formatted result viasetRangeText, preserving the browser's undo stack.input- singleonChangeemitter; idempotent on values already produced by the pipeline.paste- splices clipboard text into the selection range, runs the full pipeline, repositions the cursor.focus/blur- strip / re-apply thousand separators inFormatOn.Blurmode (the React default).
Sanitization
Seven steps run in order on every value before formatting is applied. See Anatomy: sanitization for the visual pipeline and per-step demos.
- Mobile keyboard artifact filtering
- Thousand separator removal
- Compact notation expansion (opt-in via
enableCompactNotation) - Scientific notation expansion
- Non-numeric character removal
- Extra decimal separator removal
- Leading zero normalization (disable with
enableLeadingZeros)
Per-step configuration: see features/sanitization.
Formatting
Four stages run after sanitization. See Anatomy: formatting for diagrams and demos.
- Decimal trimming to
decimalMaxLength - Decimal padding to
decimalMinLength - Thousand grouping (
FormatOn.Changeonly; deferred to blur inFormatOn.Blur) - Cursor restoration after the formatted value replaces
input.value
Value output
Every value is emitted as a string on a single onChange event, with both representations exposed on e.target. See Anatomy: raw vs formatted for the mental model, and Anatomy: the Proxy on e.target for how both values reach you from the same event.
- raw - the sanitized string without separators; always returned as
e.target.value. Safe to pass directly to form libraries. - formatted - the display string with thousand separators; written to
input.valueand available ase.target.formattedValueviaNumoraHTMLInputElement.
Full pipeline
user action
│
├─ keydown ──────────── skip cursor over thousand separator (Delete/Backspace only)
│
├─ beforeinput ─────── decimal separator handling [native addEventListener]
│ character insertion / deletion (primary path)
│ e.preventDefault() + setRangeText
│ ↓ fires synchronous 'input' → React onChange (handleChange)
│
├─ paste ────────────── splice clipboard into selection (dedicated handler)
│ sanitize + format + cursor reposition
│ ↓ synthetic ChangeEvent → onChange directly
│
├─ input / onChange ─── pure emitter (handleChange)
│ reads already-formatted value from DOM
│ strips separators → raw value
│ fires onChange (e.target.value = raw)
│ (also fires for undo/redo via native browser input event)
│
│ (beforeinput pipeline produces:)
│ ▼
│ trimToDecimalMaxLength
│ │
│ ▼
│ ensureMinDecimals
│ │
│ ▼
│ formatNumoraInput (thousand grouping)
│ [FormatOn.Change only; skipped in FormatOn.Blur]
│ │
│ ┌─────┴──────────────────────┐
│ ▼ ▼
│ formatted raw
│ → input.value → e.target.value (onChange)
│ → e.target.formattedValue
│
└─ blur ─────────────── formatValueForDisplay (FormatOn.Blur only)
→ input.value