Text Field

Single-line text input control with control radius (--radius-lg), leading/trailing icon slots, and optional label/hint wrapper.

// Plain markup
import { TextField } from "@shavin/ui";

Default usage

Component Source
const [value, setValue] = useState("");

<TextField
  className="w-64"
  placeholder="Enter a value…"
  value={value}
  onChange={(e) => setValue(e.target.value)}
/>

With Icons

Component Source
{/* Leading Icon */}
<TextField placeholder="Search…" leadingIcon={<SearchIcon />} />

{/* Trailing Icon */}
<TextField placeholder="you@example.com" trailingIcon={<MailIcon />} />

{/* Both Icons */}
<TextField placeholder="Search email…" leadingIcon={<SearchIcon />} trailingIcon={<MailIcon />} />

Variants (Invalid & Disabled)

Component Source
{/* Invalid state */}
<TextField placeholder="Required field" invalid defaultValue="bad value" />

{/* Disabled state */}
<TextField placeholder="Can't touch this" disabled />

Sizes

Component Source
<TextField size="sm" placeholder="Small" />
<TextField size="md" placeholder="Medium" />
<TextField size="sm" placeholder="Small + icon" leadingIcon={<SearchIcon />} />

OTP Verification Code

Type='otp' renders single-input accessible verification slots with live focus caret, 1-tap SMS autofill, and paste support.

Enter the 6-digit code sent to your email.
Type='otp' renders single-input accessible verification slots with live focus caret, 1-tap SMS autofill, and paste support.
const [otp, setOtp] = useState("");

<TextField
  type="otp"
  length={6}
  label="Verification Code"
  hint="Enter the 6-digit code sent to your email."
  value={otp}
  onChange={(e) => setOtp(e.target.value)}
  onComplete={(code) => alert(`Verified: ${code}`)}
/>

OTP Split & Masked PIN

Split 3-3 digits with separator or mask sensitive PIN codes.

-
4-digit numerical secret PIN
Split 3-3 digits with separator or mask sensitive PIN codes.
{/* 3-3 Split with separator */}
<TextField
  type="otp"
  length={6}
  separator="-"
  separatorIndex={3}
  label="Two-factor Authentication"
/>

{/* Masked 4-digit PIN */}
<TextField
  type="otp"
  length={4}
  mask
  label="Security PIN"
  hint="4-digit numerical secret PIN"
/>

Props

PropTypeDefaultDescription
leadingIconReactNode—Icon rendered inside the field, pinned to the left edge.
trailingIconReactNode—Icon rendered inside the field, pinned to the right edge.
labelstring—Optional label text rendered above the field.
hintstring—Optional hint/helper text rendered below the field.
lengthnumber | string6Number of digits/slots when type="otp". Default 6.
maskbooleanfalseWhether to mask digits with bullet dots (for PIN / password OTP codes) or mask password text. Default false.
separatorReactNode—Optional separator element or character rendered midway (e.g. '-' or index=3 for 3-3 split).
separatorIndexnumber—Index position to render the separator (defaults to Math.floor(length / 2)).
onComplete(code: string) => void—Callback fired when all OTP digits have been entered.
slotClassNamestring—Optional custom CSS class name applied directly to individual OTP slot boxes.
showHidePasswordboolean—Password-only: render the built-in show/hide (Eye/EyeOff) trailing button. Ignored for other types.
errorMessagestring—Error message text — when set, the field renders in invalid/error state and shows this message below the input.
size"sm" | "md" | "lg""md"—
invalid"true" | "false""false"—
Configure Theme
Theme
Accent Color
Custom
Gray Family
Appearance
Radius
Scaling
Panel Style
Heading Font
Body Font