Skip to Content
Input

Input

Basic text field for capturing user input.

#

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.

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.

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.

$
%
kg
https://
@
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. Enable showCount on the surrounding Field to show a character count.

import { Field, Input } from "@optiaxiom/react"; export function App() { return ( <Field label="Project description" showCount> <Input maxLength={50} placeholder="Enter description..." /> </Field> ); }

#

Common patterns

#

Async validation

Show a loading spinner in the addon while performing an asynchronous check such as username availability.

"use client"; import { IconCheck } from "@optiaxiom/icons"; import { Box, Field, Group, Input, Spinner } from "@optiaxiom/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 { IconCheck, IconCopy } from "@optiaxiom/icons"; import { Button, Field, Input } from "@optiaxiom/react"; import { useState } from "react"; const apiKey = "sk-1234-5678-abcd-efgh"; 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 () => { await navigator.clipboard.writeText(apiKey); setCopied(true); setTimeout(() => setCopied(false), 2000); }} rounded="full" size="sm" /> } defaultValue={apiKey} readOnly /> </Field> ); }

SearchInput

Basic search input field with clear button.

Textarea

Multi-line text field for capturing user input.

Field

Wrapper for inputs to provide context such as label, help text and required state.

#

Props

#

Input

import { Input } from "@optiaxiom/react"; // or import { Input } from "@optiaxiom/react/input";

Supports all Box props in addition to its own. Renders a <div> element but forwards all props to an inner <input> element.

Prop

addonAfter

Display content inside the input at the end.

ReactNode

addonBefore

Display content inside the input at the start.

ReactNode

addonPointerEvents

When this prop is set to none clicking empty space inside the addon will focus the input box.

"none" | "auto"

appearance

Control the appearance of the input.

"number" | "default"

Default: default

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

autoFocus

Whether the input should be focused on mount.

false | true

children

ReactNode

className

string

defaultValue

The initial value of the input, when uncontrolled.

string | number | readonly string[]

disabled

Whether the input is disabled.

false | true

error

Whether to show the input error state. Automatically set when used inside a Field component with an error prop.

false | true

htmlSize

Control the native input size attribute.

number

maxLength

The maximum number of characters allowed in a text input or textarea. Wrap it in a Field with showCount to show the character count.

number

name

The name of the form control element.

string

onValueChange

Handler that is called when the value changes.

(value: string) => void

placeholder

The placeholder text to use when control has no value.

string

required

Whether selecting this input is required.

false | true

size

Control the size of the input.

"md" | "lg" | "xl"

Default: md

type

The input type.

"number" | string & {} | "color" | "button" | "checkbox" | "radio" | "hidden" | "text" | "reset" | "range" | "search" | "time" | "image" | "tel" | "url" | "email" | "date" | "submit" | "month" | "datetime-local" | "week" | "file" | "password"

value

The value of the input. Use with onValueChange to control it.

string | number | readonly string[]

#

Accessibility

#

Labels and errors

Wrap Input in Field to connect it to its label, description and error message:

  • The label’s for points at the input’s id.
  • aria-describedby references the description and error message.
  • aria-invalid is set when the field has an error.

#

Keyboard interactions

Key

Description

Tab

Moves focus to or away from the input

Shift + Tab

Moves focus to the previous focusable element

#

Changelog

#

3.6.0

#

0.1.0

  • Added component
Last updated on