# Input (/ui/components/react/input)




## Usage [#usage]

```tsx
import { Input } from '@appica/ui-react/input'
```

```tsx
<Input type="email" placeholder="you@example.com" />
```

`Input` is a styled text field built on [Base UI's Input](https://base-ui.com/react/components/input). Use it like a native `<input>` - every native attribute (`type`, `value`, `onChange`, `name`, …) passes straight through. Give it a label the usual way: an external `<label htmlFor>` pointing at its `id`, or an `aria-label`.

Add an icon or prefix with `startSlot` / `endSlot`, and a clear button with `clearable`. When any of those are present, `Input` renders a wrapper around the field so the adornments sit inside the same frame; otherwise it renders the bare input.

## Examples [#examples]

### Variants [#variants]

`variant` switches the appearance between a bordered `outline` (default) and a filled `soft` field.

```tsx
import { Input } from '@appica/ui-react/input'

export default function InputVariants() {
  return (
    <div className="flex w-full max-w-70 flex-col gap-4">
      <Input variant="outline" placeholder="Outline" aria-label="Outline" />
      <Input variant="soft" placeholder="Soft" aria-label="Soft" />
    </div>
  )
}
```

### Sizes [#sizes]

`inputSize` scales the height, padding, and text - `sm`, `md` (default), or `lg`. (It's named `inputSize` so it doesn't collide with the native `size` attribute.)

```tsx
import { Input } from '@appica/ui-react/input'

export default function InputSizes() {
  return (
    <div className="flex w-full max-w-70 flex-col gap-4">
      <Input inputSize="sm" placeholder="Small" aria-label="Small" />
      <Input inputSize="md" placeholder="Medium" aria-label="Medium" />
      <Input inputSize="lg" placeholder="Large" aria-label="Large" />
    </div>
  )
}
```

### Start & end slots [#start--end-slots]

`startSlot` and `endSlot` take any node (not just icons) and render it inside the field frame. Use them for a leading icon with a static text suffix, an [`Avatar`](/ui/components/react/avatar), or an interactive control like a password-visibility [`Button`](/ui/components/react/button).

```tsx
'use client'

import { useState } from 'react'
import { Input } from '@appica/ui-react/input'
import { Avatar, AvatarFallback } from '@appica/ui-react/avatar'
import { Button } from '@appica/ui-react/button'
import { Mail, Lock, Eye, EyeOff, UserFilled } from '@appica/icons-react'

export default function InputSlots() {
  const [show, setShow] = useState(false)
  return (
    <div className="flex w-full max-w-70 flex-col gap-4">
      <Input
        startSlot={<Mail />}
        endSlot={<span className="text-foreground-muted text-sm">@example.com</span>}
        placeholder="yourname"
        aria-label="Email username"
      />
      <Input
        startSlot={
          <Avatar size="2xs">
            <AvatarFallback>
              <UserFilled />
            </AvatarFallback>
          </Avatar>
        }
        placeholder="Display name"
        aria-label="Display name"
      />
      <Input
        type={show ? 'text' : 'password'}
        startSlot={<Lock />}
        defaultValue="hidden@password"
        endSlot={
          <Button
            className="-me-1.5"
            variant="ghost"
            size="icon-sm"
            aria-label={show ? 'Hide password' : 'Show password'}
            onClick={() => setShow((v) => !v)}
          >
            {show ? <EyeOff /> : <Eye />}
          </Button>
        }
        aria-label="Password"
      />
    </div>
  )
}
```

### Clearable [#clearable]

Set `clearable` to add a clear (✕) button that appears once the field has a value and empties it on click. Pair it with `onClear` to react to the reset.

```tsx
import { Input } from '@appica/ui-react/input'
import { Search } from '@appica/icons-react'

export default function InputClearable() {
  return (
    <Input
      className="max-w-70"
      clearable
      startSlot={<Search />}
      defaultValue="Wireless headphones"
      aria-label="Search products"
    />
  )
}
```

### Disabled, read-only & error states [#disabled-read-only--error-states]

`disabled` grays out the field and blocks interaction; `readOnly` keeps it focusable and selectable but prevents edits. Set `aria-invalid` to paint the error state - it's mirrored to `data-invalid`. Inside a [`Field`](/ui/components/react/field), the input also inherits the field's `disabled` and `invalid` state, which is the better choice when you're rendering a validation message.

```tsx
import { Input } from '@appica/ui-react/input'

export default function InputStates() {
  return (
    <div className="flex w-full max-w-70 flex-col gap-4">
      <Input placeholder="Disabled" disabled aria-label="Disabled" />
      <Input defaultValue="Read-only value" readOnly aria-label="Read-only" />
      <Input defaultValue="Input with error" aria-invalid aria-label="Input with error" />
    </div>
  )
}
```

## RTL [#rtl]

Every Appica UI component supports right-to-left layouts out of the box. Set the `dir` attribute on a container (commonly your `<html>` element) so CSS logical properties resolve correctly, and wrap your tree in `DirectionProvider` so direction-aware behavior (roving focus, popup placement, and the like) follows the same direction.

```tsx
import { DirectionProvider } from '@appica/ui-react/providers/direction-provider'

export default function RootLayout({ children }) {
  return (
    <html lang="ar" dir="rtl">
      <body>
        <DirectionProvider dir="rtl">{children}</DirectionProvider>
      </body>
    </html>
  )
}
```

The text aligns to the start edge and the `startSlot`/`endSlot` adornments swap sides to follow the resolved direction. For setup details and caveats, see the [RTL guide](/ui/docs/react/rtl).


## API reference [#api-reference]

`Input` wraps [Base UI's Input](https://base-ui.com/react/components/input) and forwards every remaining native `<input>` prop (`type`, `name`, `onChange`, `aria-*`, …); the `variant`, `inputSize`, `clearable`, `startSlot`, `endSlot`, `onClear`, and `inputProps` props are Appica additions. Inside a Base UI [`Field`](/ui/components/react/field) it picks up the field's `disabled` and `invalid` state, and exposes `data-disabled` / `data-invalid` for styling.

When adornments or `clearable` are present the field renders inside a wrapper, so `className` lands on that wrapper. Use `inputProps` to reach the inner `<input>` itself (for example to give it its own `className`).

| Prop            | Type                   | Default     | Description                                                                                                                                                        |
| --------------- | ------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `variant`       | `'outline' \| 'soft'`                              | `'outline'` | Field appearance - bordered or filled.                                                                                                                                                                    |
| `inputSize`     | `'sm' \| 'md' \| 'lg'`                             | `'md'`      | Scales height, padding, and text. Named `inputSize` to avoid the native `size` attribute.                                                                                                                 |
| `startSlot`     | `ReactNode`                                                   | -           | Adornment rendered before the field, inside the frame.                                                                                                                                                    |
| `endSlot`       | `ReactNode`                                                   | -           | Adornment rendered after the field, inside the frame.                                                                                                                                                     |
| `clearable`     | `boolean`                                                     | `false`     | Show a clear (✕) button once the field has a value.                                                                                                                                                       |
| `onClear`       | `() => void`                                       | -           | Called when the clear button is pressed.                                                                                                                                                                  |
| `inputProps`    | `ComponentProps\<'input'>`                         | -           | Props for the inner `<input>` (its own `className`, handlers, the native `size` attribute, …). `size` here is the character count the control is intrinsically wide, not the scale - that is `inputSize`. |
| `aria-invalid`  | `boolean \| 'true' \| 'false'`                     | -           | Marks the error state; mirrored to `data-invalid`.                                                                                                                                                        |
| `disabled`      | `boolean`                                                     | `false`     | Disables the field and removes it from the tab order.                                                                                                                                                     |
| `placeholder`   | `string`                                                      | `' '`       | Placeholder text.                                                                                                                                                                                         |
| `value`         | `string`                                                      | -           | Controlled value. Pair with `onChange`.                                                                                                                                                                   |
| `defaultValue`  | `string`                                                      | -           | Uncontrolled initial value.                                                                                                                                                                               |
| `ref`           | `Ref\<HTMLInputElement>`                           | -           | Ref to the underlying `<input>`.                                                                                                                                                                          |
| `render`        | `ReactElement \| ((props, state) => ReactElement)` | -           | Replace the underlying element, or compose it with another component.                                                                                                                                     |
| `onValueChange` | `function`                                                    | -           | Fires when the value changes. Use when controlled.                                                                                                                                                        |
| `className`     | `string`                                                      | -           | Extra classes, merged via `tailwind-merge` (lands on the wrapper when adornments are used).                                                                                                               |

## Accessibility [#accessibility]

* Renders a native `<input>` (`role="textbox"`). Give it an accessible name - an external `<label htmlFor>` pointing at its `id`, or an `aria-label`.
* The clear button is labeled **Clear input**, kept out of the tab order (`tabIndex={-1}`), and appears only when the field has a value.
* `aria-invalid` is bridged to `data-invalid` so the error state styles with or without a wrapping `Field`.
* `disabled` removes the field from the tab order and exposes `data-disabled` for styling.
