Username Input

Reads back the exact handle that will be registered and blocks reserved words, case-only duplicates and lookalikes of existing names.

StopsHandle impersonates someone else

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/username-input.json

The files land in your project. They're yours to edit.

Usage

username-input-usage.tsx
import { UsernameInput } from "@/components/ui/username-input"

<UsernameInput label="Username" defaultValue="Pr1ya_Sharma" existing={verified} checkAvailability={checkAvailability} />

What it catches

  • Reserved and staff words, including digit swaps (adm1n)
  • Case-only duplicates of existing handles
  • Lookalikes of existing names (Cyrillic/Greek homoglyphs, 0/o, 1/l)
  • Length outside minLength–maxLength
  • Taken handles, with free alternatives

Props

UsernameInput props
PropTypeDefault
label

Visible label.

string"Username"
value

Controlled value, as typed (without the

string—
defaultValue

Initial value when uncontrolled.

string""
onValueChange

Called with the cleaned text and the canonical handle, or null while the handle can't be used.

(value: string, handle: string | null) => void—
reserved

Words nobody may register, also caught in disguise (adm1n, supp0rt).

string[]RESERVED_USERNAMES
existing

Handles that exist, e.g. verified or staff accounts, checked for case-only and look-alike clashes.

string[][]
checkAvailability

Resolves whether the canonical handle is free. Debounced; stale answers are ignored. Keep it stable.

(handle: string, signal: AbortSignal) => AvailabilityResult | Promise<AvailabilityResult>—
minLength

Fewest characters.

number3
maxLength

Most characters.

number30
delay

Pause in ms before checking availability.

number400
messages

Override any of the default messages.

Partial<typeof USERNAME_MESSAGES>—

* required. Also accepts the native props of <input> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Visible <label>; aria-invalid on blocking issues and status via aria-describedby
  • Status in a polite live region; blocking errors use role=alert
  • Suggestions are buttons

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Enforce uniqueness on the normalised, skeleton form of the handle with a database constraint.

  • Skeleton folding covers common confusables, not the full UTS #39 data
  • Availability depends on your checkAvailability; race conditions remain until the server reserves the name

What it stops

Handle impersonates someone else

Also known as

username · handle · @handle · signup · profile url · availability check · reserved names · display name

Field type

Dependencies

Pairs well with