---
title: "Input"
description: "Single-line text input field"
source: "Equality"
---
import React from "react";
import { Input, IconButton } from "@eqtylab/equality";
import { Search, Eye, Mail, Check, User } from "lucide-react";
import { InputErrorTextDemo } from "@demo/components/demo/input";

## Overview

Input is a single-line text field for collecting short values such as names, emails, passwords, and numbers. It wraps a native `<input>`, so it accepts all standard input attributes (`type`, `placeholder`, `value`, `disabled`, `min`/`max`, and so on) and forwards a `ref` to the underlying element.

Beyond the native element, it adds `prefix` and `suffix` slots for placing an icon or other content inside the field — useful for a leading search icon or a trailing password-reveal toggle. It also owns its supplementary copy: `helpText` and `errorText` render below the field, so every form in the product spaces and styles those messages the same way. Pair the input with a [Label](/components/label) (via a shared `id`/`htmlFor`) so it has an accessible caption, or use it inside a [Form](/components/form) field.

## Usage

Import the component:

```tsx
import { Input } from "@eqtylab/equality";
```

Basic usage:

```tsx
<Input type="text" placeholder="Enter text here..." />
```

Controlled, driving the value yourself:

```tsx
const [value, setValue] = useState("");

<Input value={value} onChange={(e) => setValue(e.target.value)} />;
```

## States

### Default

<Input
  id="input-default"
  type="text"
  placeholder="Enter text here..."
  defaultValue="Default input"
/>

### Disabled

<Input
  id="input-disabled"
  type="text"
  placeholder="Disabled input"
  disabled
  defaultValue="Disabled value"
/>

### Error

Pass `errorText` to put the field in its error state. The border turns red, the input is marked `aria-invalid`, and the message is rendered below the field.

<Input
  client:load
  id="input-error"
  type="text"
  placeholder="Invalid input"
  defaultValue="Invalid value"
  errorText="Enter a value between 1 and 100."
/>

## Help & Error Text

The component renders one message slot below the field, so spacing and styling stay consistent across every form. The two props share that slot: **the error replaces the help text** rather than stacking under it, and the help text returns once the field is valid again. A field never shows two competing lines of small print.

### Help Text

`helpText` is supplementary copy shown whenever the field is not in error. Use it for the technical constraints a user cannot infer from the label — character limits, password requirements, accepted formats, units. Keep it short and write it before the user makes a mistake, not after.

<Input
  id="input-help-text"
  type="text"
  placeholder="acme-production"
  helpText="Lowercase letters, numbers, and hyphens. Maximum 32 characters."
/>

### Error Text

`errorText` appears only when the field is in the error state. Tell the user what is wrong and how to fix it — "Enter a valid email address", not "Invalid input". Leave the prop `undefined` (or empty) when the field is valid.

Because the error hides the help text, an error about a stated constraint should restate it: "Name must be 32 characters or fewer", not "Too long". Otherwise the user loses the requirement at the moment they need it.

A validator that reports several problems at once can pass them as an array — `errorText={["Code is duplicated.", "Use letters, numbers, hyphens, and periods."]}` — and each gets its own row, deduped. Pass a single string whenever you can; a stack of red rows is harder to act on than one clear sentence.

How the message enters depends on whether there is help text:

- **With help text** — the slot is already occupied, so the error swaps in place. Nothing moves, and there is no animation to sit through before the message is readable.
- **Without help text** — the error collapses open and closed, so validating on change never makes the layout jump.

The first field below has help text, the second does not. Type a short password into each.

<InputErrorTextDemo client:only="react" />

### Usage

```tsx
<Input
  helpText="Lowercase letters, numbers, and hyphens. Maximum 32 characters."
  errorText={nameError}
/>
```

Whichever message is showing is wired to the input with `aria-describedby`, and the error carries `role="alert"`, so a screen reader announces it as soon as it appears. Do not repeat the error in a separate element — that double-announces it.

When the constraint is a character limit, pair the help text with a counter beside the label (`0/80 characters`) rather than spending the help text on the number.

## Input Types

Because the underlying element is a native `<input>`, set the `type` prop to any standard value.

### Email

<Input id="input-email" type="email" placeholder="user@example.com" />

### Password

<Input id="input-password" type="password" placeholder="Enter password" />

### Number

<Input
  id="input-number"
  type="number"
  placeholder="Enter number"
  min="0"
  max="100"
/>

### Usage

```tsx
<Input type="email" placeholder="user@example.com" />
<Input type="password" placeholder="Enter password" />
<Input type="number" min="0" max="100" />
```

## Prefix & Suffix

Use the `prefix` and `suffix` props to render content inside the field, before or after the text. Any node works, though an icon is the most common.

### With Prefix Icon

{(() => {
const searchIcon = React.createElement(Search);
return (

<div className="flex flex-col gap-2">
  <Input
    id="input-prefix-search"
    type="text"
    placeholder="Search..."
    prefix={searchIcon}
  />
  <Input
    id="input-prefix-user"
    type="text"
    placeholder="Username"
    prefix={React.createElement(User)}
  />
  <Input
    id="input-prefix-mail"
    type="email"
    placeholder="Email address"
    prefix={React.createElement(Mail)}
  />
</div>
); })()}

### With Suffix Icon

{(() => {
const eyeIcon = React.createElement(Eye);
return (

<div className="flex flex-col gap-2">
  <Input
    id="input-suffix-password"
    type="password"
    placeholder="Password"
    suffix={eyeIcon}
  />
  <Input
    id="input-suffix-check"
    type="text"
    placeholder="Verified input"
    suffix={React.createElement(Check)}
    defaultValue="john@example.com"
  />
</div>
); })()}

### Usage

```tsx
import { Search, Eye } from "lucide-react";

<Input placeholder="Search..." prefix={<Search />} />
<Input type="password" placeholder="Password" suffix={<Eye />} />
```

## Props

Input accepts all standard `<input>` attributes (`type`, `placeholder`, `value`, `defaultValue`, `onChange`, `disabled`, `min`, `max`, etc.) in addition to the props below.

| Name        | Description                                                                                                                                   | Type                      | Default | Required |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ------- | -------- |
| `prefix`    | Content rendered inside the field, before the text.                                                                                           | `ReactNode`               | —       | ❌       |
| `suffix`    | Content rendered inside the field, after the text.                                                                                            | `ReactNode`               | —       | ❌       |
| `helpText`  | Supplementary copy below the field. Hidden while an error is showing.                                                                         | `ReactNode`               | —       | ❌       |
| `errorText` | Error message below the field. Setting it puts the field in its error state and replaces the help text. An array renders one row per message. | `ReactNode` \| `string[]` | —       | ❌       |
| `type`      | The native input type.                                                                                                                        | `string`                  | `text`  | ❌       |