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.

~1kb minified + gzip
0 dependencies
MIT open source
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" />
}