<Stack
width="440px"
maxWidth="100%"
>
<TextField.Root
size="sm"
variant="outline"
>
<TextField.Label>Project name</TextField.Label>
<TextField.Input name="playground-project" placeholder="Website redesign" />
<TextField.HelperText>Visible to everyone in your workspace.</TextField.HelperText>
<TextField.ErrorMessage>Enter a project name.</TextField.ErrorMessage>
</TextField.Root>
</Stack>Features
- Supports
outlineandfillvariants with responsive sizes. - Supports start and end icons, affixes, and custom slots.
- Shows helper text and error messages under the input.
- Accepts native input types like
emailandpassword. - Supports controlled and uncontrolled values.
Anatomy
<TextField.Root>
<TextField.Label>Email address</TextField.Label>
<TextField.Input name="email" />
<TextField.HelperText>We will use this address for project updates.</TextField.HelperText>
<TextField.ErrorMessage>Enter a valid value.</TextField.ErrorMessage>
</TextField.Root>name.error is set.Variants
Text Field has two visual styles: outline and fill. The default is outline.
<TextField.Root variant="outline">
<TextField.Label>Outline</TextField.Label>
<TextField.Input name="outline-name" placeholder="Project name" />
</TextField.Root>
<TextField.Root variant="fill">
<TextField.Label>Fill</TextField.Label>
<TextField.Input name="fill-name" placeholder="Project name" />
</TextField.Root>Sizes
Root size scales the input, label, and icons. The default is sm.
<TextField.Root size="sm">
<TextField.Label>Small</TextField.Label>
<TextField.Input name="small-name" defaultValue="Website redesign" />
</TextField.Root>
<TextField.Root size="md">
<TextField.Label>Medium</TextField.Label>
<TextField.Input name="medium-name" defaultValue="Website redesign" />
</TextField.Root>Responsive
size also accepts a breakpoint object.
<TextField.Root size={{ xs: "sm", md: "md" }}>
<TextField.Label>Project name</TextField.Label>
<TextField.Input name="responsive-name" defaultValue="Website redesign" />
</TextField.Root>Icons and Affixes
startIcon and endIcon add an icon that focuses the input on click. prefix and suffix add text tied to the value, like a currency. An icon and affix can share a side.
import { Search } from "@vortexlabs/vortex-icons";
<TextField.Root>
<TextField.Label>Search projects</TextField.Label>
<TextField.Input name="project-search" startIcon={<Search />} placeholder="Search by name" attributes={{ type: "search" }} />
</TextField.Root>
<TextField.Root>
<TextField.Label>Budget</TextField.Label>
<TextField.Input name="budget" prefix="$" suffix="AUD" defaultValue="1200" attributes={{ inputMode: "decimal" }} />
</TextField.Root>Custom Slots
startSlot and endSlot hold other content, like an action. A slot replaces the icon and affix on its side. This example also controls the value with value and onChange.
"use client";
import { Button, TextField } from "@vortexlabs/vortex";
import React from "react";
export const TextFieldClearExample = () => {
const [value, setValue] = React.useState("Website redesign");
return (
<TextField.Root>
<TextField.Label>Project name</TextField.Label>
<TextField.Input
name="project-name"
value={value}
onChange={({ value }) => setValue(value)}
endSlot={
<Button size="xs" variant="tertiary" disabled={!value} onClick={() => setValue("")}>
Clear
</Button>
}
/>
<TextField.HelperText>{value.length} characters</TextField.HelperText>
</TextField.Root>
);
};Multiline
Root multiline lets slot content wrap onto several lines, like a list of tags.
Design
Research
Marketing
Engineering
Support
import { Badge, TextField } from "@vortexlabs/vortex";
<TextField.Root multiline width="320px">
<TextField.Label>Tags</TextField.Label>
<TextField.Input
name="tags"
placeholder="Add a tag"
startSlot={["Design", "Research", "Marketing", "Engineering", "Support"].map((tag) => (
<Badge size="sm" key={tag}>{tag}</Badge>
))}
/>
</TextField.Root>Validation
Root error marks the input invalid and shows TextField.ErrorMessage; helper text stays visible. required only adds native validation, so you decide when to set error.
<TextField.Root error required>
<TextField.Label>Workspace name</TextField.Label>
<TextField.Input name="workspace-name" defaultValue="a" attributes={{ minLength: 3 }} />
<TextField.ErrorMessage>Use at least three characters.</TextField.ErrorMessage>
<TextField.HelperText>This name is visible to everyone in your workspace.</TextField.HelperText>
</TextField.Root>Disabled and Read Only
Root disabled blocks editing and focus. readOnly in Input attributes keeps the value focusable and selectable.
<TextField.Root disabled>
<TextField.Label>Workspace slug</TextField.Label>
<TextField.Input name="disabled-slug" defaultValue="design-team" />
</TextField.Root>
<TextField.Root>
<TextField.Label>Workspace ID</TextField.Label>
<TextField.Input name="workspace-id" defaultValue="ws_12345" attributes={{ readOnly: true }} />
</TextField.Root>Native Attributes and Width
Input attributes sets native options like type, autoComplete, and minLength; type defaults to text. Root width accepts a CSS length or a breakpoint object.
<TextField.Root width={{ xs: "100%", md: "24rem" }}>
<TextField.Label>Password</TextField.Label>
<TextField.Input name="password" attributes={{ type: "password", autoComplete: "new-password", minLength: 8 }} />
<TextField.HelperText>Use at least eight characters.</TextField.HelperText>
</TextField.Root>Accessibility
TextField.Label; a placeholder doesn't replace it. Helper text and the error message are linked to the input automatically.