

<Preview name="TextareaStatesExample" />

## Overview [#overview]

The `Textarea` component is a multi-line form control for longer text entries like notes, comments, descriptions, and messages. It supports size variants, automatic height adjustment, manual resize modes, and URL state synchronization.

## Usage [#usage]

```tsx
import { Textarea } from "@tilt-legal/cubitt-components/textarea";
```

```tsx
<Textarea placeholder="Enter your message..." />
```

<Accordions type="single">
  <Accordion title="URL State Management">
    Sync the textarea value with the URL by providing `paramName`:

    ```tsx
    <Textarea
      paramName="message"
      paramDebounce={300}
      placeholder="Synced with ?message=..."
    />
    ```
  </Accordion>
</Accordions>

## Examples [#examples]

### States [#states]

Textarea fields in their default, error, disabled, and readonly states.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="TextareaStatesExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { useState } from "react";
    import { Textarea } from "@tilt-legal/cubitt-components/textarea";
    import {
      ToggleGroup,
      ToggleGroupItem,
    } from "@tilt-legal/cubitt-components/toggle-group";

    const states = ["default", "error", "disabled", "readonly"] as const;

    export default function Example() {
      const [state, setState] =
        useState<(typeof states)[number]>("default");

      return (
        <>
          <div className="w-full max-w-sm">
            <Textarea
              aria-invalid={state === "error" ? true : undefined}
              defaultValue={
                state === "readonly" ? "This textarea is read-only." : undefined
              }
              disabled={state === "disabled"}
              key={state}
              placeholder={
                state === "readonly"
                  ? "Readonly"
                  : `${state[0]?.toUpperCase()}${state.slice(1)}`
              }
              readOnly={state === "readonly"}
            />
          </div>
          <PreviewFooter>
            <ToggleGroup
              aria-label="Textarea state"
              groupVariant="segmented"
              multiple={false}
              onValueChange={(value) => {
                if (states.includes(value as (typeof states)[number])) {
                  setState(value as (typeof states)[number]);
                }
              }}
              value={state}
            >
              <ToggleGroupItem value="default">Default</ToggleGroupItem>
              <ToggleGroupItem value="error">Error</ToggleGroupItem>
              <ToggleGroupItem value="disabled">Disabled</ToggleGroupItem>
              <ToggleGroupItem value="readonly">Readonly</ToggleGroupItem>
            </ToggleGroup>
          </PreviewFooter>
        </>
      );
    }
    ```
  </Tab>
</Tabs>

### Sizes [#sizes]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="TextareaSizesExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Label } from "@tilt-legal/cubitt-components/label";
    import { Textarea } from "@tilt-legal/cubitt-components/textarea";

    export default function Example() {
      return (
        <div className="flex w-full max-w-sm flex-col gap-6">
          <Label>
            Small
            <Textarea placeholder="Small size" size="sm" />
          </Label>
          <Label>
            Default
            <Textarea placeholder="Default size" />
          </Label>
          <Label>
            Large
            <Textarea placeholder="Large size" size="lg" />
          </Label>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### Auto-resize [#auto-resize]

Set a minimum row count while keeping automatic height growth.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="TextareaRowsExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Textarea } from "@tilt-legal/cubitt-components/textarea";

    export default function Example() {
      return (
        <div className="w-full max-w-sm">
          <Textarea minRows={3} placeholder="Minimum 3 rows..." />
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### Inline [#inline]

Use `variant="inline"` when the textarea should read like editable text, such as an inline note, file name, or table cell.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="InlineTextareaExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Textarea } from "@tilt-legal/cubitt-components/textarea";

    export default function Example() {
      return (
        <div className="w-full max-w-sm">
          <Textarea
            className="text-fg-2"
            defaultValue="Review the uploaded materials and confirm each document is attached to the correct matter before continuing."
            minRows={3}
            placeholder="Inline paragraph..."
            variant="inline"
          />
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### URL State [#url-state]

Sync longer text values to the URL with `paramName`. `paramDebounce` delays the URL write without slowing the textarea value.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="URLStateTextareaExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Textarea } from "@tilt-legal/cubitt-components/textarea";

    export default function Example() {
      return (
        <div className="w-full max-w-sm">
          <Textarea
            defaultValue=""
            paramDebounce={300}
            paramName="note"
            placeholder="Type to update the URL"
          />
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

***

## API Reference [#api-reference]

### Textarea [#textarea]

The textarea component with auto-resize and URL state management.

| Prop           | Type                                                                                        | Default     | Description                                                   |
| -------------- | ------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------- |
| `value`        | `string`                                                                                    | —           | The controlled value of the textarea.                         |
| `defaultValue` | `string`                                                                                    | —           | The default value when uncontrolled.                          |
| `onChange`     | `(event: ChangeEvent<HTMLTextAreaElement>) => void`                                         | —           | Callback fired when the value changes.                        |
| `placeholder`  | `string`                                                                                    | —           | Placeholder text when empty.                                  |
| `size`         | `"sm"` \| `"md"` \| `"lg"`                                                                  | `"md"`      | The size variant of the textarea.                             |
| `variant`      | `"default"` \| `"inline"`                                                                   | `"default"` | The visual variant of the textarea.                           |
| `minRows`      | `number`                                                                                    | `2`         | Minimum number of visible text lines.                         |
| `maxRows`      | `number`                                                                                    | —           | Maximum number of visible text lines.                         |
| `resize`       | `true` \| `"none"` \| `"both"` \| `"horizontal"` \| `"vertical"` \| `"block"` \| `"inline"` | `"none"`    | Resize behavior of the textarea.                              |
| `disabled`     | `boolean`                                                                                   | `false`     | Whether the textarea is disabled.                             |
| `readOnly`     | `boolean`                                                                                   | `false`     | Whether the textarea is read-only.                            |
| `aria-invalid` | `boolean`                                                                                   | —           | Indicates validation state and applies error styling.         |
| `spellCheck`   | `boolean`                                                                                   | `false`     | Enable spell checking.                                        |
| `className`    | `string`                                                                                    | —           | Additional CSS classes.                                       |
| `style`        | `CSSProperties`                                                                             | —           | Additional inline styles, merged with textarea resize styles. |

### TextareaWrapper [#textareawrapper]

Use `TextareaWrapper` for embedded textarea compositions that need a shared field surface around inline content.

| Prop        | Type                       | Default | Description                                     |
| ----------- | -------------------------- | ------- | ----------------------------------------------- |
| `children`  | `ReactNode`                | —       | Textarea and embedded content to render inside. |
| `size`      | `"sm"` \| `"md"` \| `"lg"` | `"md"`  | The wrapper size, matching `Textarea` sizing.   |
| `className` | `string`                   | —       | Additional CSS classes for the wrapper element. |

#### URL State Props [#url-state-props]

When provided with a `paramName`, the textarea syncs its value with URL parameters via TanStack Router search params.

| Prop                  | Type                              | Default | Description                                                                  |
| --------------------- | --------------------------------- | ------- | ---------------------------------------------------------------------------- |
| `paramName`           | `string`                          | —       | URL parameter name for syncing state with URL. Enables URL state management. |
| `paramValue`          | `string`                          | —       | Controlled value for the URL parameter.                                      |
| `onUrlValueChange`    | `(value: string \| null) => void` | —       | Callback when URL parameter value changes.                                   |
| `paramClearOnDefault` | `boolean`                         | `true`  | Remove URL param when value equals default.                                  |
| `paramDebounce`       | `number`                          | —       | Debounce URL updates in milliseconds.                                        |
| `paramThrottle`       | `number`                          | —       | Throttle URL updates in milliseconds.                                        |
