Input
FreeA recessed text field with optional leading and trailing affixes; it never jumps when validation appears.
pnpm dlx shadcn@latest add https://caliperui.site/r/input.jsonhalden is taken. Try halden-team or halden-hq.6 / 32
When to use #
Use it for
- Names, emails, search terms and any short free text.
- Pair it with a label, or wrap it in a field.
Skip it when
- The value is a number with steps; use a number field.
- The text is long; use a textarea.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/input.json
Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above.
import { useState } from "react" import { Field } from "@/components/ui/field" import { Input } from "@/components/ui/input" export function EmailInput() { const taken = ["halden", "ostra", "atlas", "lumen", "admin"]; const [slug, setSlug] = useState("halden"); const [checking, setChecking] = useState(false); const format = /^[a-z0-9-]*$/.test(slug); const error = !format ? "Use lowercase letters, numbers and hyphens only." : !checking && taken.includes(slug) ? `${slug} is taken. Try ${slug}-team or ${slug}-hq.` : undefined; const ok = format && slug.length >= 3 && !checking && !taken.includes(slug); return ( <div className="w-full max-w-sm"> <Field label="Workspace URL" description="Lowercase letters, numbers and hyphens. 3 to 32 characters." error={error} status={checking ? "Checking availability…" : ok ? `caliper.app/${slug} is available.` : undefined} statusTone={checking ? "pending" : "success"} count={`${slug.length} / 32`} > {(control) => ( <Input {...control} leading="caliper.app/" autoComplete="off" spellCheck={false} maxLength={32} value={slug} onChange={(event) => { const next = event.currentTarget.value; setSlug(next); setChecking(true); window.setTimeout(() => setChecking(false), 450); }} /> )} </Field> </div> ); }
API reference #
Input
component
Also accepts the native props of its root element and a ref.
Accessibility #
It is built on native elements, so roles and focus come from the browser.
No custom key handling; Tab and the native activation keys do the work.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.