Input
A text input component with support for icons, prefix/suffix, chips, and size variants.
Anatomy
Import and assemble the component:
1import { Input } from "@raystack/apsara";23<Input placeholder="Enter text" />
For labels, description, and error messages, wrap with Field:
1import { Field, Input } from "@raystack/apsara";23<Field label="Email" description="We won't share it" error={errors.email?.message}>4 <Input placeholder="Enter email" />5</Field>
Usage
An input can carry more than text. Icons, a prefix or suffix, and chips all sit inside the same frame.
Basic Input
A single-line text input. placeholder hints at the expected value; it is not a label and does not replace one.
1<Input placeholder="Enter text" />
Size
Input comes in two sizes: small (24px) and large (32px).
1<Flex direction="column" gap={5}>2 <Input placeholder="32px height (default)" />3 <Input placeholder="24px height" size="small" />4</Flex>
With prefix and suffix
Prefix and suffix sit inside the field for fixed units and protocols — a currency symbol, https://, @. They are decoration, not part of the value.
1<Input placeholder="0.00" prefix="$" suffix="USD" />
With icons
A leading icon names the field at a glance; a trailing icon usually holds an action, like clear or reveal.
1<Input2 placeholder="Enter text"3 leadingIcon={<Home size={16} />}4 trailingIcon={<Info size={16} />}5/>
With chips
Input that can display and manage chips/tags.
1<Flex direction="column" gap={5}>2 <Input3 placeholder="Type and press Enter..."4 chips={[5 { label: "A", onRemove: () => console.log("Remove A") },6 { label: "B", onRemove: () => console.log("Remove B") },7 ]}8 />9 <Input10 placeholder="Type and press Enter..."11 size="small"12 chips={[13 { label: "A", onRemove: () => console.log("Remove A") },14 { label: "B", onRemove: () => console.log("Remove B") },15 ]}
Interactive chips
Accepts an array of chips, each with a label and an optional onRemove callback.
maxChipsVisible caps how many are rendered before collapsing the rest into an overflow counter.
1chips?: Array<{ label: string; onRemove?: () => void }>;2maxChipsVisible?: number; // defaults to 2
This is purely a presentation layer — it does not manage chip state. The wrapper component owns the array and decides when items are added or removed.
The example below
appends a chip on Enter, removes one when its dismiss button is clicked.
1const [chips, setChips] = React.useState([2 { label: "Tag1" },3 { label: "Tag2" },4 { label: "Tag3" },5 { label: "Tag4" },6 { label: "Tag5" },7]);8const [input, setInput] = React.useState("");910<Input11 placeholder="Type and press Enter..."12 value={input}13 onChange={(e) => setInput(e.target.value)}14 onKeyDown={(e) => {15 if (e.key === "Enter" && input.trim()) {
With Field
Use Field to add label, description, and error handling.
1<Flex direction="column" gap={5} style={{ width: 560 }}>2 <Field label="Email" required description="We won't share your email">3 <Input type="email" placeholder="Enter email" />4 </Field>5 <Field label="Name" error="This field is required">6 <Input placeholder="Enter name" />7 </Field>8 <Field label="Phone" required={false}>9 <Input type="tel" placeholder="Enter phone" />10 </Field>11</Flex>
Controlled
Use onValueChange for a callback that receives only the new string value, or onChange for the full React change event. Both fire on every keystroke — pick whichever fits your needs.
1(function ValueChangeExample() {2 const [value, setValue] = React.useState("");34 return (5 <Flex direction="column" gap={5} style={{ width: 400 }}>6 <Input7 placeholder="Type something..."8 value={value}9 onValueChange={setValue}10 />11 <Text size="small">Current value: {value || "(empty)"}</Text>12 </Flex>13 );14})
Disabled
disabled stops interaction and removes the field from the tab order. Use readOnly instead when the value still needs to be selectable and copyable.
1<Input placeholder="Enter text" disabled />
Disabled with chips
When disabled, chips become non-interactive and their dismiss buttons are hidden.
1<Input2 placeholder="Type and press Enter..."3 disabled4 chips={[5 { label: "Tag1", onRemove: () => console.log("Remove Tag1") },6 { label: "Tag2", onRemove: () => console.log("Remove Tag2") },7 ]}8/>
API Reference
Renders a text input control. For label, description, and error support, use with Field.
Prop
Type
Slots
Every rendered part carries a stable data-slot attribute for styling and testing:
| Slot | Element |
|---|---|
input-container | The outer wrapper <div> |
input-leading-icon | Wrapper around the leading icon (when leadingIcon) |
input-prefix | The prefix text (when prefix) |
input-chip-container | Wrapper around the chips and the <input> element |
input-chip | Each visible chip (when chips) |
input-chip-overflow | The +N overflow count (when chips exceed maxChipsVisible) |
input | The <input> element itself |
input-suffix | The suffix text (when suffix) |
input-trailing-icon | Wrapper around the trailing icon (when trailingIcon) |
Accessibility
- Use with Field for automatic label association and error linking
- Required state is communicated via
aria-required - Supports all native
<input>attributes