Rifm / React input format & mask
Formatting that keeps the cursor in place.
Rifm turns any input into a precise formatter or mask. Tiny, dependency-free, and designed around the detail that matters: where your cursor lands next.
01 / WHY RIFM
One small job. Done carefully.
Cursor aware
Rifm tracks accepted characters and restores the cursor position.
Input agnostic
Native inputs, your design system, or a third-party text field. Rifm only supplies value and onChange.
Format anything
Numbers, dates, phone numbers, card masks, casing rules, and domain-specific values all use the same small API.
02 / GET STARTED
Try it live.
Number formatting
Copy code
import { createNumberFormatter } from 'rifm/number'
const number = createNumberFormatter({
locales: 'en-US',
allowNegative: true,
useGrouping: true,
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
export function PriceInput() {
const [value, setValue] = useState('240000')
const rifm = useRifm({
value,
onChange: setValue,
...number,
})
return <input {...rifm} inputMode="decimal" />
}
Phone number
Copy code
import { AsYouType } from 'libphonenumber-js/min'
const formatPhone = (value: string) =>
new AsYouType('US')
.input(value.replace(/\D/g, '').slice(0, 10))
export function PhoneInput() {
const [phone, setPhone] = useState('4155552671')
const rifm = useRifm({
value: phone,
onChange: setPhone,
format: formatPhone,
accept: /\d/g,
})
return <input {...rifm} inputMode="tel" />
}
Date mask
Copy code
const formatDate = (value: string) => {
const digits = value.replace(/\D/g, '').slice(0, 8)
return [
digits.slice(0, 2),
digits.slice(2, 4),
digits.slice(4),
].filter(Boolean).join(' / ')
}
export function DateInput() {
const [date, setDate] = useState('12082026')
const rifm = useRifm({
value: date,
onChange: setDate,
format: formatDate,
accept: /\d/g,
})
return <input
{...rifm}
inputMode="numeric"
placeholder="DD / MM / YYYY"
/>
}
Text enforcement
Copy code
const uppercaseLatin = (value: string) =>
value.replace(/[^a-z ]/gi, '').toUpperCase()
export function AirportInput() {
const [airport, setAirport] = useState('london heathrow')
const rifm = useRifm({
value: airport,
onChange: setAirport,
format: value => value,
replace: uppercaseLatin,
accept: /[a-z ]/gi,
})
return <input {...rifm} placeholder="TYPE A NAME" />
}