# Input group

> Static text, a button or a native select welded to a field, sharing one edge.

URL: https://apogee.avaui.dev/docs/input-group
Last updated: 2026-10-15

Static text, a button or a native select welded to a field, sharing one edge.

Last updated October 15, 2026

```astro
<InputGroup id="site" label="Site">
<InputGroupAddon slot="start">https://</InputGroupAddon>
<Input id="site" grouped placeholder="example.com" />
<Button slot="end" variant="outline" color="neutral">Copy</Button>
</InputGroup>
```

## Add

```sh
npx @ava-ui/apogee add input-group
```

Depends on `Field`. Zero JS. Put an `Input` (with `grouped`), a `Button` or a native `<select>` in the slots.

## Usage

```astro
---
import InputGroup from '@/components/apogee/InputGroup/InputGroup.astro'
import InputGroupAddon from '@/components/apogee/InputGroup/InputGroupAddon.astro'
import Input from '@/components/apogee/Input/Input.astro'
---
<InputGroup id="price" label="Price" hint="Per month, before tax.">
  <InputGroupAddon slot="start">€</InputGroupAddon>
  <Input id="price" grouped type="number" aria-describedby="price-hint" />
  <InputGroupAddon slot="end">/mo</InputGroupAddon>
</InputGroup>
```

The group renders the hint and error with the ids `<id>-hint` and `<id>-error`; pass them in `aria-describedby` on the Input so they are read with the field.

## Anatomy

A Field (label, hint, error) around a flex row. Each direct child of the row loses its inner corners and overlaps its neighbour by 1px; the child that holds focus lifts above the seam. The Input inside renders `grouped`, which drops its own label and wrapper so the group’s label is the only one. A static InputGroupAddon is a `<label>` that the group points at the field: a click on “https://” focuses the input, and screen readers hear it with the label.

## API

`@composable` — exposed to Content Editors in AVA.

### `InputGroup`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | — | Required. The id of the Input inside; the group’s label points at it. |
| `label` | `string` | — | Visible label. |
| `hint` | `string` | — | Helper text under the row. |
| `error` | `string` | — | Error message. |
| `required` | `boolean` | `false` | Asterisk on the label; set `required` on the Input too. |
| `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` | Label and hint size; give the Input and the addons the same size. |
| `class` | `string` | — | Extra classes on the Field root. |

Slots: `start`, default (the field), `end`.

### `InputGroupAddon`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` | Height and text, matching the field. |
| `class` | `string` | — | Extra classes. |

### `Input`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `grouped` | `boolean` | `false` | Renders without label, hint or error; for use inside InputGroup. |

An addon on one side only keeps the field’s outer corners:

```astro
<InputGroup id="amount" label="Amount">
<InputGroupAddon slot="start">€</InputGroupAddon>
<Input id="amount" grouped type="number" />
</InputGroup>
```

## Variants

Every `size`:

```astro
<InputGroup id="s" label="Small" size="small">
<InputGroupAddon slot="start" size="small">$</InputGroupAddon>
<Input id="s" grouped size="small" />
</InputGroup>
… medium, large, xlarge …
```

## States

-   **Focus** — the focused child takes a higher `z-index`, so its ring is drawn over both seams.
-   **Error** — pass `error` to the group and `hasError` to the Input.
-   **Disabled** — disable the Input and the addon Button individually.

## Examples

A native select as the start addon:

```astro
<InputGroup id="phone" label="Phone">
<select slot="start" aria-label="Country code" class="h-9 rounded-md border border-neutral bg-neutral-faded px-2 text-sm text-neutral">
  <option>+40</option><option>+44</option>
</select>
<Input id="phone" grouped type="tel" placeholder="700 000 000" />
</InputGroup>
```

## Accessibility

-   One label for the whole group, pointing at the Input through `id`.
-   An addon Button needs its own name (text or `aria-label`); a native select needs an `aria-label` since the group’s label names the Input.
-   Static addons are plain text; a screen reader does not read them with the field. Repeat the unit in the label or hint when it matters (“Price in euros”).

## Tokens

| Token | Used for |
| --- | --- |
| `--border-neutral` / `--background-neutral-faded` | Addon border and fill |
| `--foreground-neutral-faded` | Addon text |

## Do / Don’t

-   **Do** use an addon for a fixed prefix or suffix the user never types (“https://”, “/mo”).
-   **Do** keep every child the same `size`.
-   **Don’t** put a label on the Input inside; the group has one.
-   **Don’t** chain more than one field in a group; that is a form row, not a group.

## Content

-   Addon text is a symbol or a short token: “€”, “https://”, “kg”. Never a sentence.

## Related

[

InputA labeled text field with hint/error messaging, wired for accessibility — plus a pure-CSS floating label.

](https://apogee.avaui.dev/docs/input)[

FieldThe labeled-field scaffold — label, control, and hint/error — wired for accessibility.

](https://apogee.avaui.dev/docs/field)[

ButtonTriggers an action. Renders a <button>, or an <a> when href is set. Hue, fill and height are three separate props.

](https://apogee.avaui.dev/docs/button)[

SelectA labeled, native <select> — full keyboard and mobile picker support for free — or a custom listbox with rich options over the same form control.

](https://apogee.avaui.dev/docs/select)
