Headless
No stylesheet to import and nothing to override. Style with CSS custom properties and data-*
hooks, and it inherits whatever your app already looks like.
Most input libraries ship a hundred components and get the hard ones wrong. This one ships only the ones that are genuinely hard — and gets them right.
Nothing below is a screenshot. Every example runs in this page, and you can edit any of it.
Grouping, symbol placement, decimal rules and digit systems all come from Intl
— not from a regex. The caret stays where you put it while the value reformats
underneath you.
function CurrencyDemo() { const [locale, setLocale] = useState('de-DE') const currency = { 'de-DE': 'EUR', 'bg-BG': 'BGN', 'hi-IN': 'INR', 'ar-EG': 'EGP', 'ja-JP': 'JPY' }[locale] return ( <div> <label style={{ display: 'block', fontSize: 13, opacity: 0.7, marginBottom: 6 }}> Locale </label> <select value={locale} onChange={(e) => setLocale(e.target.value)}> <option value="de-DE">de-DE — German</option> <option value="bg-BG">bg-BG — Bulgarian</option> <option value="hi-IN">hi-IN — Hindi (lakh grouping)</option> <option value="ar-EG">ar-EG — Arabic (native digits)</option> <option value="ja-JP">ja-JP — Japanese (no decimals)</option> </select> <div style={{ marginTop: 14 }}> <CurrencyInput locale={locale} currency={currency} defaultValue={50000} /> </div> </div> ) }
Whole, half or continuous — with any icon. Interactive, it is a native
radiogroup, so arrow keys, form submission and screen-reader announcements
come from the browser. Read-only, it is an image with a label.
function RatingDemo() { const [value, setValue] = useState(3.5) const [precision, setPrecision] = useState(0.5) return ( <div> <Rating value={value} onChange={setValue} precision={precision} /> <p style={{ marginTop: 10, fontSize: 14 }}> value <strong>{value}</strong> · precision {precision} </p> <div style={{ display: 'flex', gap: 8, marginTop: 4 }}> {[1, 0.5, 0.1].map((p) => ( <button key={p} onClick={() => setPrecision(p)} style={{ padding: '3px 12px', borderRadius: 999, cursor: 'pointer', border: '1px solid var(--rx-rule-strong)', background: p === precision ? 'var(--rx-primary)' : 'transparent', color: p === precision ? '#fff' : 'inherit', }} > {p} </button> ))} </div> </div> ) }
One native <input> behind spatial slots — so paste, SMS autofill, IME and
password managers keep working. Not six inputs held together with key handlers.
function OtpDemo() { const [code, setCode] = useState('') return ( <div> <OtpInput value={code} onChange={setCode} length={6} /> <p style={{ marginTop: 12, fontSize: 14 }}> {code.length === 6 ? '✓ complete — ' : ''}value: <strong>{code || '(empty)'}</strong> </p> </div> ) }
Headless
No stylesheet to import and nothing to override. Style with CSS custom properties and data-*
hooks, and it inherits whatever your app already looks like.
Zero runtime dependencies
react is the only peer. Nothing else reaches your bundle, and everything tree-shakes to what
you actually import.
Accessible by construction
Native platform semantics rather than ARIA sprinkled on top — keyboard, focus, and form behaviour come from the browser. Every build is checked with axe-core.
Correct on the seams
Bulgarian’s space-only-above-9999, Hindi lakh grouping, Arabic-Indic digits, formatted paste, Chrome auto-translate, IME composition, RTL and SSR.
npm install @rxova/react-inputsimport { CurrencyInput, Rating, OtpInput } from '@rxova/react-inputs'Typed, strict, and covered by a test suite that runs in a browser. Start here →