Skip to content
weftGitHub

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/warp

Usage

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>  )}

Clear button and counter

clearable shows once there is text. counter is the most it should hold.

tsx
import { Input, Stack } from '@weft-js/warp'export function ClearableCounter() {  return (    <Stack gap="group">      <Input label="Branch" clearable defaultValue="ranking-v2" />      <Input label="Commit message" counter={72} defaultValue="Rank exact matches first" />    </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

Use it for

  • A short answer people type
  • Any native input type: email, password, url, tel

Reach for something else

  • Textarea, when the answer can run over one line
  • Select, when the answer comes from a short list
  • Search, when it filters or finds things

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 with aria-describedby.
  • An error is announced when it appears.

API Reference

PropTypeDefaultDescription
labelstring—The name people read and screen readers announce. Always required, and it floats on the border by default.
hintunknown—A short line of help under the field.
errorunknown—Replaces the hint and marks the field invalid.
iconIconRef—An icon before the text.
prefixunknown—Fixed text before the value, like https://.
suffixunknown—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.
clearablebooleanfalseA clear button, shown once there is a value.
counternumber—The most it should hold. Draws a count under the field.
idstring—The element id. One is made for you if you leave it out.
valuestring—The current value, when you own the state.
disabledbooleanfalseCan't be focused or changed, and doesn't post with a form.
readonlybooleanfalseCan be focused, read and copied, but not changed. It still posts.
placeholderstring—Sample text shown while empty. Never a substitute for a label.
typestring—The native input type, such as email, url or password. Text otherwise.
EventTypeDefaultDescription
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

RelatedFieldTextareaSearchSelect