Input
A single-line text field for typing a value.
<template> <Input placeholder="Hello world" /></template>Props
| Name | Type | Default |
|---|---|---|
| size | "md" | "sm" | "md" |
| error | boolean | false |
| mono | boolean | false |
| status | "pending" | "valid" | "invalid" | - |
| readonly | boolean | false |
| ...props | UInput props (type, placeholder, modelValue, disabled, icon, leadingIcon, trailingIcon, color, variant, ...) | - |
Slots
| Name | Description |
|---|---|
| leading | An icon or a text prefix such as "app.hacktron.ai/". Measured, so the typed text is padded past it; clicks on it land on the field. |
| trailing | A clear button or a suffix such as "/100". Measured, so the typed text stays clear of it; replaces the status icon. |
Guidelines
Do
- A placeholder is optional: the filled treatment reads the v-model value, not the browser's
:placeholder-shownstate. - Pair with
UFormFieldfor a labelled field (label, description, validation error) - Figma's "Input Labelled" frame is that pairing, not a separate component. - Inside a
UFormField, a validation error puts the field on the error surface by itself. Useerrorfor a state the form doesn't know, such as a failed lookup; it swaps to the actual error surface/border tokens, not just a color prop.
- Use
monofor a value that reads as code (a file path, a token, a URL slug); the field keeps its height. - Put a text prefix such as "app.hacktron.ai/" in the
#leadingslot; the typed text is padded past it automatically. - Use
statusfor a lookup's progress (a slug availability check, a commit SHA):pendingshows a spinner,valida check,invalida cross, in the trailing spot. Pairinvalidwitherror. - A value shown for copying (a webhook URL, a generated secret) takes
readonly: the field drops its hover change and text cursor (keyboard focus still shows the brand border, for select-and-copy), so it reads as a value, not a place to type. Put the copy button beside it or in#trailing.
Don't
- Don't use
type="file"expecting custom "Choose File" button styling beyond what UInput themes natively - Figma's "File" frame is the sameInputcomponent withtype="file", not a bespoke file-picker. - Don't pass
ui; it is rejected at type-check because it would replace this component's own styling. Mark an invalid field witherror, notcolor. - Don't reach for
color/variantto invent a new look - they're escape hatches to UInput's native palette for cases this token set doesn't cover, not a styling API for this component.
Examples
Leading and trailing icons
#trailing is a real forwarded slot (not just a static icon), so it can hold an interactive control like this clear button, shown only once there's text to clear. Input's own leading/trailing icon size is fixed at size-4 regardless of its own size prop, matching IconButton's scale - the same at both sizes here, no per-instance override needed.
<template> <Input v-model="search" icon="i-lucide-search" placeholder="Search"> <template v-if="search" #trailing> <IconButton icon="i-lucide-x" variant="plain" size="sm" aria-label="Clear search" @click="search = ''" /> </template> </Input> <Input v-model="searchSm" size="sm" icon="i-lucide-search" placeholder="Search"> <template v-if="searchSm" #trailing> <IconButton icon="i-lucide-x" variant="plain" size="sm" aria-label="Clear search" @click="searchSm = ''" /> </template> </Input></template>Text prefix
A fixed prefix in #leading, e.g. the workspace URL before a slug. Input measures the slot and pads the typed text past it, so the prefix needs no per-site padding.
<template> <Input v-model="slug"> <template #leading> <span class="text-text-tertiary">app.hacktron.ai/</span> </template> </Input></template>Monospace value
mono switches the text to the mono-03 token for a value that reads as code (a file path, a token, a slug) while the field keeps the control height.
<template> <Input v-model="affectedFile" mono /></template>Validation status
status shows a lookup's progress in the trailing spot: a spinner while it runs, then a check or a cross. Pair invalid with error for the red surface.
<template> <Input v-model="slug" status="pending" /> <Input v-model="slug" status="valid" /> <Input v-model="slug" status="invalid" error /></template>