Rime

PrefixedInput

A fixed-format code with a set prefix: an invoice number, an account code, a membership number.

PrefixedInput is the TextInput box with a fixed prefix segment set into its left end ("INV", "ACC-") and a counter at the right. The prefix is part of the box, never typed. charset is digits (the default) or alphanumeric, upper-cased as typed.

It normalises whatever is pasted or autofilled (with or without the prefix, spaces or dashes) and reports both the full code and the body after the prefix. Leaving it part-filled marks it invalid; an empty field is left to the form's required check.

Installation

npx shadcn@latest add KezLahd/rime/prefixed-input

Also installs: base, field, icon, text-input.

Usage

import { Field, PrefixedInput } from "@/components/ui";

<Field label="Invoice number">
  <PrefixedInput prefix="INV" length={6} value={code} onValueChange={(full) => setCode(full)} />
</Field>

Examples

Digits

INV is filled in. Type the 6 digits that follow it.

Letters and numbers

Letters and numbers, as printed on your statement.

ACC- is filled in. Type the 8 characters that follow it.

Switchable prefix (phone)

prefixOptions turns the prefix into a dropdown. Each option carries its own length so a +61 mobile, a +61 landline, a 1300, a 13 and a 1800 all fit the same field. Pasting a full number picks the matching option automatically. Add validate(body) to catch invalid formats (AU numbers after +61 start with 2, 3, 4, 7, or 8) and detail(body) to show a plain-English hint under the field as the user types.

Australian mobile, landline, 1300, 13 or 1800.

+61 is filled in. Type the 9 digits that follow it.

API

Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.

PrefixedInput

PrefixedInput props
PropTypeDefaultDescription
prefixstringThe fixed segment set into the box and never typed: "INV", "ACC", "#". Omit when passing prefixOptions; the selected option's value takes over.
lengthnumberHow many characters follow the prefix. Omit when passing prefixOptions; each option carries its own length.
charsetPrefixedFormat["charset"]"digits"digits = 0-9 only (the default). alphanumeric = A-Z and 0-9, upper-cased as typed.
prefixOptionsReadonlyArray<PrefixedOption>A list of switchable prefix choices, each with its own length (and charset). The prefix slot becomes a dropdown button; pasting a full code picks the matching option automatically. The first option is the default. For a single hardcoded prefix, use prefix + length instead.
prefixValuestringControlled: which option is active. onPrefixChange fires when it changes.
onPrefixChange(value: string, option: PrefixedOption) => void
valueRequiredstringThe full code ("INV004213") or the body alone; either is read.
onValueChangeRequired(code: string, body: string) => voidEvery edit. `code` is the full form ("" while nothing is entered, never a bare prefix); `body` is what follows the prefix.
invalidbooleanOverrides the Field's invalid state and the built-in check on blur.
showCounterbooleantrueThe "4/6" count at the right of the box. On by default.
boxClassNamestringClass for the outer box rather than the <input>.

Also accepts the native attributes of AriaAttributes, DOMAttributes, HTMLAttributes, InputHTMLAttributes.

Theme tokens

Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.

PrefixedInput per-component tokens
TokenDefaultLive value
--field-focus-borderrgba(var(--rgb-brand), 0.7) / var(--focus-color)…
--field-focus-ringvar(--focus-halo)…
--field-focus-ring-invalid0 0 0 4px rgba(var(--rgb-danger), 0.14)…
--field-hover-borderrgba(var(--rgb-brand-deep), 0.3)…
--input-bgvar(--surface-solid)…
--input-bordervar(--line-input)…
--input-border-widthvar(--border-w-field)…
--input-font-sizevar(--text-input)…
--input-height-mdvar(--field-h-md)…
--input-height-smvar(--field-h-sm)…
--input-pad-xcalc(14px * var(--density))…
--input-radiusvar(--r-md) / var(--r-sm)…
--input-shadowvar(--shadow-input)…

System tokens it reads

PrefixedInput system tokens
TokenDefaultLive value
--border-w-fieldtokens.css…
--danger-basetokens.css…
--danger-field-filltokens.css…
--danger-inktokens.css…
--danger-linetokens.css…
--densitytokens.css…
--durtokens.css…
--easetokens.css…
--ease-outtokens.css…
--field-h-mdtokens.css…
--field-h-smtokens.css…
--focus-colortokens.css…
--focus-halotokens.css…
--font-bodytokens.css…
--font-monotokens.css…
--ink-bodytokens.css…
--ink-brandtokens.css…
--ink-disabledtokens.css…
--ink-headingtokens.css…
--ink-mutedtokens.css…
--ink-placeholdertokens.css…
--ink-secondarytokens.css…
--linetokens.css…
--line-inputtokens.css…
--r-mdtokens.css…
--r-smtokens.css…
--r-xstokens.css…
--recess-filltokens.css…
--rgb-brandtokens.css…
--rgb-brand-deeptokens.css…
--rgb-dangertokens.css…
--rgb-whitetokens.css…
--shadow-floattokens.css…
--shadow-inputtokens.css…
--success-inktokens.css…
--surface-solidtokens.css…
--surface-sunkentokens.css…
--text-bodytokens.css…
--text-captiontokens.css…
--text-inputtokens.css…
--text-smalltokens.css…
--wash-activetokens.css…
--wash-hovertokens.css…
--wash-subtletokens.css…
--z-popovertokens.css…

Accessibility

  • Wrap it in <Field label=…>: Field wires the label, hint and error to the control (id, aria-describedby, aria-invalid) through context, so the control needs no ids of its own.
  • The prefix and the expected length are read out through a visually hidden description tied to the input.
  • A refused key shows a short note in the counter and announces it politely, so a refusal is never silent.
  • In switchable-prefix mode the dropdown is a listbox: arrow keys move between options, Enter picks, Escape closes and returns focus to the trigger.

Do and don't

Do

  • Validate the full code on the server too.
  • Use it only where the format is truly fixed.
  • Reach for prefixOptions when the user must pick from a small set of fixed-length formats (phone type, country code, SKU family).

Don't

  • Don't use it for free text that merely starts with a word; use TextInput with a leading adornment.
  • Don't put >5 options in the prefix dropdown; use a Combobox or a dedicated phone-country picker instead.

For AI agents

This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.

Open prefixed-input.md

Related: text-input