Input
Basic text field for capturing user input.
Want to skip the docs? Try our MCP Server
#
Documentation
#
#
Usage
#
"use client";
import type { ComponentPropsWithoutRef } from "react";
import { Field, Input } from "@optiaxiom/react";
export function App({
description,
error,
label = "Input label",
required = false,
}: Pick<
ComponentPropsWithoutRef<typeof Field>,
"description" | "error" | "label" | "required"
>) {
return (
<Field
description={description}
error={error}
label={label}
required={required}
>
<Input placeholder="Enter text..." />
</Field>
);
}#
Controlled
#
Use the value and defaultValue props to toggle between controlled and uncontrolled usage. And combine it with onValueChange / onChange to listen for changes to the state.
Using onValueChange:
Input value:
"use client";
import { Group, Input, Text } from "@optiaxiom/react";
import { useState } from "react";
export function App() {
const [value, setValue] = useState("");
return (
<Group flexDirection="column" gap="16">
<Input onValueChange={setValue} value={value} />
<Text fontSize="md">Input value: {value}</Text>
</Group>
);
}Using onChange:
Input value:
"use client";
import { Group, Input, Text } from "@optiaxiom/react";
import { useState } from "react";
export function App() {
const [value, setValue] = useState("");
return (
<Group flexDirection="column" gap="16">
<Input onChange={(event) => setValue(event.target.value)} value={value} />
<Text fontSize="md">Input value: {value}</Text>
</Group>
);
}#
Form integration
#
Integrate with form libraries like react-hook-form for validation and error handling.
"use client";
import { Button, Field, Group, Input } from "@optiaxiom/react";
import { useForm } from "react-hook-form";
export function App() {
const {
formState: { errors },
handleSubmit,
register,
} = useForm<{ email: string; name: string; password: string }>();
return (
<form
noValidate
onSubmit={handleSubmit((data) => {
console.log(data);
})}
>
<Group flexDirection="column" gap="16">
<Field error={errors.name?.message} label="Name" required>
<Input
placeholder="John Doe"
{...register("name", {
required: "Name is required",
})}
/>
</Field>
<Field error={errors.email?.message} label="Email" required>
<Input
placeholder="name@example.com"
type="email"
{...register("email", {
pattern: {
message: "Please enter a valid email address",
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
},
required: "Email is required",
})}
/>
</Field>
<Field error={errors.password?.message} label="Password" required>
<Input
placeholder="Enter password"
type="password"
{...register("password", {
minLength: {
message: "Password must be at least 8 characters",
value: 8,
},
required: "Password is required",
})}
/>
</Field>
<Button alignSelf="start" type="submit">
Submit
</Button>
</Group>
</form>
);
}#
Input types
#
Use the type prop to specify the type of data the input should accept. Common types include email, password, tel, url, and number.
import { Field, Group, Input } from "@optiaxiom/react";
export function App() {
return (
<Group flexDirection="column" gap="16">
<Field label="Email">
<Input placeholder="name@example.com" type="email" />
</Field>
<Field label="Password">
<Input placeholder="Enter password" type="password" />
</Field>
<Field label="Phone number">
<Input placeholder="+1 (555) 000-0000" type="tel" />
</Field>
<Field label="Website">
<Input placeholder="https://example.com" type="url" />
</Field>
<Field label="Number">
<Input placeholder="0" type="number" />
</Field>
</Group>
);
}#
Required
#
Use the required prop on the Field component to mark an input as required. This adds a visual indicator and sets the appropriate ARIA attributes.
Note: This only provides visual feedback. For actual validation, use the
native required attribute on the Input or a form validation library like
react-hook-form.
import { Field, Input } from "@optiaxiom/react";
export function App() {
return (
<Field label="Email address" required>
<Input placeholder="name@example.com" type="email" />
</Field>
);
}#
Sizes
#
Use the size prop to control the size of the input field. The xl size uses a larger font size for the input value and placeholder.
"use client";
import type { ComponentPropsWithRef } from "react";
import { Input } from "@optiaxiom/react";
export function App({
size,
}: Pick<ComponentPropsWithRef<typeof Input>, "size">) {
return <Input placeholder="Enter text..." size={size} />;
}#
Error state
#
Use the error prop on the Field component to display validation errors. The error message will be shown below the input and the input will be styled accordingly.
Note: When you set the error prop on Field, it automatically sets the error
state on the Input inside it. You don’t need to set error on both
components.
import { Field, Input } from "@optiaxiom/react";
export function App() {
return (
<Field error="Please enter a valid email address" label="Email">
<Input defaultValue="invalid-email" type="email" />
</Field>
);
}#
Disabled state
#
Enable the disabled prop to toggle the disabled state of the input field.
import { Input } from "@optiaxiom/react";
export function App() {
return <Input defaultValue="Some disabled value" disabled />;
}#
Read-only state
#
Enable the readOnly prop to display the read-only state of the input field.
import { Input } from "@optiaxiom/react";
export function App() {
return <Input defaultValue="Some read-only value" readOnly />;
}#
Addons
#
#
Text addons
#
Use the addonBefore and addonAfter props to add text labels like currency symbols, units, or prefixes to your inputs.
import { Field, Group, Input } from "@optiaxiom/react";
export function App() {
return (
<Group flexDirection="column" gap="16">
<Field label="Price">
<Input addonBefore="$" placeholder="0.00" type="number" />
</Field>
<Field label="Discount">
<Input addonAfter="%" placeholder="0" type="number" />
</Field>
<Field label="Weight">
<Input addonAfter="kg" placeholder="0" type="number" />
</Field>
<Field label="Website">
<Input addonBefore="https://" placeholder="example.com" type="url" />
</Field>
<Field label="Username">
<Input addonBefore="@" placeholder="username" />
</Field>
</Group>
);
}#
Interactive addons
#
You can also provide icons, buttons, or any other interactive element as addons.
"use client";
import { IconEye, IconEyeSlash } from "@optiaxiom/icons";
import { Button, Input } from "@optiaxiom/react";
import { useState } from "react";
export function App() {
const [hidden, setHidden] = useState(true);
return (
<Input
addonAfter={
<Button
appearance="subtle"
aria-label={hidden ? "Show password" : "Hide password"}
icon={hidden ? <IconEye /> : <IconEyeSlash />}
onClick={() => setHidden((flag) => !flag)}
rounded="full"
size="sm"
/>
}
placeholder="Password"
type={hidden ? "password" : "text"}
/>
);
}#
Handling addon clicks
#
Use the addonPointerEvents prop to control whether clicking the addons focuses the input or not.
"use client";
import { Input } from "@optiaxiom/react";
import { type ComponentPropsWithoutRef } from "react";
export function App({
addonPointerEvents = "none",
}: Pick<ComponentPropsWithoutRef<typeof Input>, "addonPointerEvents">) {
return (
<Input
addonBefore="@"
addonPointerEvents={addonPointerEvents}
placeholder="Email"
/>
);
}#
Appearance
#
Use the appearance prop to select between the different input appearances.
"use client";
import { Group, Input } from "@optiaxiom/react";
export function App() {
return (
<Group gap="16">
<Input defaultValue="Text value" />
<Input appearance="number" defaultValue="23.50" />
</Group>
);
}#
Character limit
#
Use the maxLength prop to limit the number of characters users can enter. Combine with a character counter in the Field description for better UX.
"use client";
import { Field, Input } from "@optiaxiom/react";
import { useState } from "react";
export function App() {
const [value, setValue] = useState("");
const maxLength = 50;
return (
<Field
description={`${value.length}/${maxLength}`}
label="Project description"
>
<Input
maxLength={maxLength}
onValueChange={setValue}
placeholder="Enter description..."
value={value}
/>
</Field>
);
}#
Common patterns
#
#
Async validation
#
Show a loading spinner in the addon while performing an asynchronous check such as username availability.
"use client";
import { Box, Field, Group, Input, Spinner } from "@optiaxiom/react";
import { IconCheck } from "@tabler/icons-react";
import { useRef, useState } from "react";
const takenUsernames = ["admin", "user", "test"];
export function App() {
const [value, setValue] = useState("");
const [error, setError] = useState("");
const [checking, setChecking] = useState(false);
const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
const available = value && !checking && !error;
return (
<Field
description={
available && (
<Group gap="4">
<Box asChild color="fg.success">
<IconCheck />
</Box>
Username is available
</Group>
)
}
error={error || undefined}
label="Username"
w="224"
>
<Input
addonAfter={checking && <Spinner size="2xs" />}
onValueChange={(value) => {
setValue(value);
clearTimeout(timeoutRef.current);
setError("");
if (!value) {
setChecking(false);
return;
}
setChecking(true);
timeoutRef.current = setTimeout(() => {
if (takenUsernames.includes(value.toLowerCase())) {
setError("Username is already taken");
}
setChecking(false);
}, 800);
}}
placeholder="Choose a username"
value={value}
/>
</Field>
);
}#
Copy to clipboard
#
Add a copy button in the addon to let users copy the input value to their clipboard.
"use client";
import { Button, Field, Input } from "@optiaxiom/react";
import { IconCheck, IconCopy } from "@tabler/icons-react";
import { useState } from "react";
export function App() {
const [copied, setCopied] = useState(false);
return (
<Field label="API Key">
<Input
addonAfter={
<Button
appearance="subtle"
aria-label={copied ? "Copied" : "Copy to clipboard"}
icon={copied ? <IconCheck /> : <IconCopy />}
onClick={async (event) => {
await navigator.clipboard.writeText(event.currentTarget.value);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}}
rounded="full"
size="sm"
/>
}
data-copy-input=""
defaultValue="sk-1234-5678-abcd-efgh"
readOnly
/>
</Field>
);
}#
Related
#
SearchInput
Basic search input field with clear button.
Textarea
Multi-line text field for capturing user input.
#
Props
#
#
Input
#
Supports all Box props in addition to its own. Renders a <div> element but forwards all props to an inner <input> element.
Prop |
|---|
addonAfterDisplay content inside the input at the end.
|
addonBeforeDisplay content inside the input at the start.
|
addonPointerEventsWhen this prop is set to
|
appearanceControl the appearance of the input.
Default: |
asChildChange the default rendered element for the one passed as a child, merging their props and behavior. Read the Composition guide for more details.
|
autoFocusWhether the input should be focused on mount.
|
children
|
className
|
disabledWhether the input is disabled.
|
errorWhether to show the input error state. Automatically set when used inside a Field component with an error prop.
|
htmlSizeControl the native input
|
nameThe name of the form control element.
|
onValueChangeHandler that is called when the value changes.
|
placeholderThe placeholder text to use when control has no value.
|
requiredWhether selecting this input is required.
|
sizeControl the size of the input.
Default: |
typeThe input type.
|
#
Accessibility
#
Input automatically handles accessibility attributes when wrapped in Field:
- Properly associates labels via
Field - Supports ARIA attributes for error states
- Manages focus states appropriately
#
Keyboard interactions
#
Key | Description |
|---|---|
Tab | Moves focus to or away from the input |
Shift + Tab | Moves focus to the previous focusable element |
#
Changelog
#
#
0.1.0
#
- Added component