Input
One line of text: a name, an email, a URL.
SourcePackage @weft-js/warpPlayground
Preview
tsx
import { Grid, Input } from '@weft-js/warp'export function Variants() { return ( <Grid cols={3} gap="group"> <Input label="Project name" defaultValue="storefront" /> <Input label="Project name" defaultValue="storefront" variant="classic" /> <Input label="Project name" defaultValue="storefront" appearance="line" /> </Grid> )}Installation
npm install @weft-js/warpUsage
tsx
import { Input } from '@weft-js/warp'<Grid cols={3} gap="group"> <Input label="Project name" defaultValue="storefront" /> <Input label="Project name" defaultValue="storefront" variant="classic" /> <Input label="Project name" defaultValue="storefront" appearance="line" /></Grid>Sizes
Use size to pick sm, md or lg.
tsx
import { Input, Stack } from '@weft-js/warp'export function Sizes() { return ( <Stack gap="group"> <Input label="Project name" size="sm" /> <Input label="Project name" /> <Input label="Project name" size="lg" /> </Stack> )}With icon
Every native attribute passes through: type, autocomplete, inputmode.
tsx
import { Input, Stack } from '@weft-js/warp'export function Types() { return ( <Stack gap="group"> <Input label="Work email" type="email" autocomplete="email" icon="mail" /> <Input label="Password" type="password" autocomplete="new-password" icon="lock" /> <Input label="Repository" type="url" placeholder="https://github.com/weft/storefront" icon="link" /> </Stack> )}Prefix and suffix
Use prefix and suffix to show fixed text before and after the value.
tsx
import { Input, Stack } from '@weft-js/warp'export function Affixes() { return ( <Stack gap="group"> <Input label="Domain" prefix="https://" suffix=".weft.dev" defaultValue="storefront" /> <Input label="Monthly budget" prefix="€" suffix="per month" inputmode="numeric" defaultValue="1,240" /> </Stack> )}States
An input can show a hint or an error, and can be disabled or readonly.
tsx
import { Grid, Input, Stack, Text } from '@weft-js/warp'export function States() { return ( <Grid cols={3} gap="group"> <Cell name="Empty"><Input label="Project name" /></Cell> <Cell name="Filled"><Input label="Project name" defaultValue="storefront" /></Cell> <Cell name="Hint"><Input label="Project name" hint="Lowercase letters and dashes." /></Cell> <Cell name="Invalid"><Input label="Project name" defaultValue="Store Front" error="Use lowercase letters and dashes." /></Cell> <Cell name="Disabled"><Input label="Project name" defaultValue="storefront" disabled /></Cell> <Cell name="Read-only"><Input label="Project name" defaultValue="storefront" readonly /></Cell> </Grid> )}const Cell = (p: { name: string; children?: unknown }) => ( <Stack gap="tight"> <Text role="caption" subtle>{p.name}</Text> {p.children} </Stack>)Validation
Clear the field to see the error.
tsx
import { derived, state } from '@weft-js/core'import { Input, Stack, Text } from '@weft-js/warp'export function Slug() { const name = state('Store Front') const slug = derived(() => name.value.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')) return ( <Stack gap="related"> <Input label="Project name" value={name.value} onInput={e => (name.value = e.currentTarget.value)} error={slug.value ? undefined : 'Give it a name with at least one letter.'} /> <Text subtle>{slug.value ? `Lives at ${slug.value}.weft.dev` : 'Waiting for a name'}</Text> </Stack> )}Project settings form
Fields share one form, and an input's error shows when its value is not valid.
tsx
import { derived, state } from '@weft-js/core'import { Button, Checkbox, Grid, Input, Row, Select, Slider, Stack, Text, Textarea, Toaster, toast } from '@weft-js/warp'export function FormProjectSettings() { const name = state('storefront') const about = state('The public shop, served from the edge.') const region = state<string | null>('eu-west') const copies = state(3) const onPush = state(true) const problem = derived(() => (SLUG.test(name.value) ? undefined : 'Use lowercase letters, numbers and dashes.')) const save = (e: Event) => { e.preventDefault() if (!problem.value) toast.show({ title: 'Settings saved', description: `${name.value} runs ${copies.value} copies in ${region.value}.` }) } return ( <> <Stack as="form" gap="group" onSubmit={save}> <Text role="heading">Project settings</Text> <Grid cols={2} gap="group"> <Input label="Project name" value={name.value} error={problem.value} onInput={e => (name.value = e.currentTarget.value)} /> <Select label="Region" options={REGION_OPTIONS} value={region} hint="Moving a project redeploys it." /> </Grid> <Textarea label="Description" value={about.value} counter={120} onInput={e => (about.value = e.currentTarget.value)} /> <Slider label="Copies" value={copies.value} min={1} max={8} onChange={v => (copies.value = v)} /> <Checkbox checked={onPush.value} hint="Every push to main goes live." onChange={(e: Event & { currentTarget: HTMLInputElement }) => (onPush.value = e.currentTarget.checked)}>Deploy on push</Checkbox> <Row gap="related" justify="end"> <Button emphasis="ghost" tone="neutral">Cancel</Button> <Button type="submit">Save changes</Button> </Row> </Stack> <Toaster /> </> )}const REGIONS = [ { region: 'eu-west', requests: 34.2, share: 40 }, { region: 'us-east', requests: 24.8, share: 29 }, { region: 'ap-south', requests: 12.1, share: 14 }, { region: 'us-west', requests: 9.4, share: 11 }, { region: 'eu-north', requests: 5.6, share: 6 },]const min = 60_000const SLUG = /^[a-z0-9-]+$/const REGION_OPTIONS = REGIONS.map(r => ({ value: r.region, label: r.region }))When to use
Playground
Size
Variant
import { Input } from '@weft-js/warp'<Input label="Project name" hint="Lowercase letters and dashes." clearable />Accessibility
- Tab
- Moves in and out
- Esc
- Clears a `clearable` field
- The label is a real
<label>, and the hint and error are tied to the input witharia-describedby. - An error is announced when it appears.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | The name people read and screen readers announce. Always required, and it floats on the border by default. |
hint | unknown | — | A short line of help under the field. |
error | unknown | — | Replaces the hint and marks the field invalid. |
icon | IconRef | — | An icon before the text. |
prefix | unknown | — | Fixed text before the value, like https://. |
suffix | unknown | — | Fixed text after the value, like a unit. |
variant | "floating" | "classic" | — | floating puts the label on the border, classic above the field. |
appearance | "line" | — | line draws only the bottom edge. |
size | "sm" | "md" | "lg" | — | sm, md or lg. Each follows the density around it. |
clearable | boolean | false | A clear button, shown once there is a value. |
counter | number | — | The most it should hold. Draws a count under the field. |
id | string | — | The element id. One is made for you if you leave it out. |
value | string | — | The current value, when you own the state. |
disabled | boolean | false | Can't be focused or changed, and doesn't post with a form. |
readonly | boolean | false | Can be focused, read and copied, but not changed. It still posts. |
placeholder | string | — | Sample text shown while empty. Never a substitute for a label. |
type | string | — | The native input type, such as email, url or password. Text otherwise. |
| Event | Type | Default | Description |
|---|---|---|---|
onInput | ((e: InputEvent & { currentTarget: HTMLInputElement & HTMLTextAreaElemen… | — | Called on each keystroke. |
Parts
Every element carries a stable name, so you can style it from your own CSS: [data-scope="input"] [data-part="input"]
input