Text Input
A text input component supporting different visual variants, as well as leading and trailing icons. Built on the native <input> element for full form compatibility.
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function TextInputDemo() {
return (
<TextInput aria-label="Email" placeholder="you@example.com" type="email" />
);
}TextInput an accessible name by either pairing it with a <label> via htmlFor or giving it an aria-label. Without one, assistive technology announces the field with no indication of what it is for.Options
| Prop | Type | Description | Default |
|---|---|---|---|
size | "sm" | "md" | "lg" | The size of the text input. Affects padding and typography. | "md" |
variant | "default" | "warning" | "success" | "error" | The visual variant, which determines color scheme. | "default" |
iconLeft | ReactNode | An optional icon rendered to the left of the input field. | — |
iconLeftAction | { handler: (e: MouseEvent<HTMLButtonElement>) => void; ariaLabel: string } | When provided, the left icon renders as a <button> that calls handler when clicked, labelled by ariaLabel. | — |
iconRight | ReactNode | An optional icon rendered to the right of the input field. | — |
iconRightAction | { handler: (e: MouseEvent<HTMLButtonElement>) => void; ariaLabel: string } | When provided, the right icon renders as a <button> that calls handler when clicked, labelled by ariaLabel. | — |
type | "text" | "email" | "password" | "url" | "tel" | "search" | "number" | "date" | "datetime-local" | "week" | "month" | "time" | The HTML input type. | "text" |
ref | Ref<HTMLInputElement> | A ref forwarded to the underlying <input> element. | — |
placeholder | string | Placeholder text for the input. | " " |
disabled | boolean | When true, prevents interaction. | false |
required | boolean | When true, marks the input as required. | false |
readOnly | boolean | When true, makes the input read-only. | false |
Form props (value, disabled, name, form, etc.), input props (autoCorrect, spellCheck, autoFocus, etc.), aria-* attributes, ref, and event handlers related to focus, blur, scroll, input, and change events are forwarded to the underlying <input> element. All other props are forwarded to the wrapping <div> element.
Examples
Uncontrolled vs controlled
Render a TextInput in an uncontrolled fashion to let it keep track of its own value:
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function UncontrolledTextInput() {
return <TextInput aria-label="About" placeholder="Enter some text" />;
}If you need to control the value of the TextInput, you can do so via the value and onChange props:
import { useState } from "react";
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function ControlledTextInput() {
const [value, setValue] = useState("");
return (
<TextInput
aria-label="About"
placeholder="Enter some text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}Different sizes
The TextInput component can be rendered at sm, md, or lg size, defaulting to md. Size affects the padding and text styles of the input, as well as the size of the rendered icons.
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function TextInputSizes() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-4)",
}}
>
<TextInput
aria-label="Username"
size="sm"
placeholder="A small text input"
/>
<TextInput
aria-label="Username"
size="md"
placeholder="A medium text input"
/>
<TextInput
aria-label="Username"
size="lg"
placeholder="A large text input"
/>
</div>
);
}Rendering icons
You can render icons to the left or the right of the input via the iconLeft and iconRight props:
import { Globe, Search } from "@tenstorrent/vesper/icons";
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function TextInputWithIcons() {
return (
<TextInput
aria-label="Search"
placeholder="Enter some text"
iconLeft={<Globe />}
iconRight={<Search />}
/>
);
}Rendered icons can be treated as buttons by passing the iconLeftAction and iconRightAction props. Each action takes a handler that is called when the icon is clicked, and an ariaLabel that gives the resulting button an accessible label:
import { Search } from "@tenstorrent/vesper/icons";
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function TextInputWithIconAction() {
return (
<TextInput
aria-label="Search term"
placeholder="Enter some text"
iconLeft={<Search />}
iconLeftAction={{
handler: () => console.log("Search logs"),
ariaLabel: "Search logs",
}}
/>
);
}Disabling the input
You can disable a TextInput by passing disabled={true} or just disabled as a prop:
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function DisabledTextInput() {
return (
<TextInput
aria-label="Byline"
placeholder="Who wrote this article?"
disabled
/>
);
}Variants
The TextInput component can be rendered in one of four variants: default, warning, success, or error. The variant determines the colour scheme of the input.
Default variant
A TextInput renders with the default variant when no variant prop is specified:
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function DefaultTextInput() {
return (
<TextInput
aria-label="Username"
placeholder="Enter your desired username"
/>
);
}Warning variant
Use the warning variant to flag a value that needs attention, but that doesn't prevent the form from being submitted:
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function WarningTextInput() {
return (
<TextInput
aria-label="Password"
placeholder="Enter your password"
variant="warning"
type="password"
defaultValue="password1"
/>
);
}Success variant
The success variant is best suited for confirming that a value has been validated as expected:
import { useState } from "react";
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function SuccessTextInput() {
const [workspace, setWorkspace] = useState("vesper");
return (
<TextInput
aria-label="Workspace"
placeholder="Enter the name of your workspace"
variant="success"
value={workspace}
onChange={(e) => setWorkspace(e.target.value)}
/>
);
}Error variant
The error variant highlights an invalid value that must be corrected before the form can be submitted:
import { useState } from "react";
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function ErrorTextInput() {
const [email, setEmail] = useState("you@example.com");
return (
<TextInput
aria-label="Email"
placeholder="Enter your email"
variant="error"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
);
}Usage in forms
TextInput renders a native <input>, so it works with regular form submission and native validation via props like name, form, required, pattern, minLength, and maxLength:
import { Button } from "@tenstorrent/vesper/button";
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function ContactForm() {
return (
<form action="/api/contact" method="post">
<TextInput
aria-label="Phone number"
name="phone"
type="tel"
placeholder="555-555-5555"
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
required
/>
<Button type="submit">Submit</Button>
</form>
);
}Accessing the underlying element
Use the ref prop when you need direct access to the underlying <input> element, eg. to focus it or read its selection:
import { useRef } from "react";
import { Search } from "@tenstorrent/vesper/icons";
import { TextInput } from "@tenstorrent/vesper/text-input";
export default function SearchField() {
const ref = useRef<HTMLInputElement>(null);
return (
<TextInput
aria-label="Search term"
placeholder="Search logs"
ref={ref}
iconLeft={<Search />}
iconLeftAction={{
handler: () => ref.current?.focus(),
ariaLabel: "Search logs",
}}
/>
);
}