# Fieldset

**A group of related fields with a legend, helper text and error text, that disables everything inside it at once.**

**Reference:** [View Source](https://github.com/jramke/fluid-primitives/tree/main/Resources/Private/Primitives/Fieldset) · [Zag.js Docs](https://zagjs.com/components/react/fieldset)

**FieldsetExamples.html**

```html
<ui:fieldset.root class="max-w-74">
    <ui:fieldset.legend>Shipping address</ui:fieldset.legend>
    <ui:fieldset.helperText>We only use it to send your order.</ui:fieldset.helperText>
    <ui:field.root name="street" required="{true}">
        <ui:input.root type="text" autocomplete="address-line1">
            <ui:input.label>Street</ui:input.label>
            <ui:input.input />
        </ui:input.root>
        <ui:field.errorText />
    </ui:field.root>
    <ui:field.root name="city" required="{true}">
        <ui:input.root type="text" autocomplete="address-level2">
            <ui:input.label>City</ui:input.label>
            <ui:input.input />
        </ui:input.root>
        <ui:field.errorText />
    </ui:field.root>
</ui:fieldset.root>

```

## Features

- Renders a native `<fieldset>`, so browsers and screen readers announce the group with the name of its `legend`
- `disabled` reaches every control inside it, and every [Field](/docs/components/field.md) with the primitives it wraps - also the parts that are no native control, like a checkbox's control or a slider's thumb
- Helper text and error text are linked to the fieldset with `aria-describedby`, the error text is only shown while the fieldset is `invalid`
- A fieldset inside a disabled fieldset is disabled as well

## Anatomy

```html
<primitives:fieldset.root>
    <primitives:fieldset.legend />
    <primitives:fieldset.helperText />
    <!-- fields, checkboxes, ... -->
    <primitives:fieldset.errorText />
</primitives:fieldset.root>
```

## Installation

```bash
typo3 ui:add fieldset
```

Or copy the files manually from GitHub (https://github.com/jramke/fluid-primitives/tree/main/Resources/Private/Registry/Fieldset) into your project.

Read more about installing [Components and Primitives](/docs/core-concepts/primitives.md).

## Examples

### Disabled

Set `disabled` on the root. The fieldset carries it to everything inside, whatever the props of the fields say, and the browser skips them when the form is submitted.

**FieldsetExamples.html**

```html
<ui:fieldset.root class="max-w-74" disabled="{true}">
    <ui:fieldset.legend>Account</ui:fieldset.legend>
    <ui:fieldset.helperText>Everything inside is disabled, whatever its own props say.</ui:fieldset.helperText>
    <ui:field.root name="email">
        <ui:input.root type="email" defaultValue="ada@example.com">
            <ui:input.label>Email</ui:input.label>
            <ui:input.input />
        </ui:input.root>
    </ui:field.root>
    <ui:field.root name="newsletter">
        <ui:checkbox.root defaultChecked="{true}">
            <ui:checkbox.control />
            <ui:checkbox.label>Send me the newsletter</ui:checkbox.label>
        </ui:checkbox.root>
    </ui:field.root>
    <ui:field.root name="notifications">
        <ui:switch.root>
            <ui:switch.label>Notifications</ui:switch.label>
            <ui:switch.control />
        </ui:switch.root>
    </ui:field.root>
</ui:fieldset.root>

```

### Invalid

`invalid` marks the group as a whole, e.g. "choose at least one". It is not passed on to the fields inside, they keep validating themselves.

**FieldsetExamples.html**

```html
<ui:fieldset.root class="max-w-74" invalid="{true}">
    <ui:fieldset.legend>Notifications</ui:fieldset.legend>
    <ui:fieldset.helperText>Choose how we reach you.</ui:fieldset.helperText>
    <ui:checkbox.root name="channels[]" value="email">
        <ui:checkbox.control />
        <ui:checkbox.label>Email</ui:checkbox.label>
    </ui:checkbox.root>
    <ui:checkbox.root name="channels[]" value="sms">
        <ui:checkbox.control />
        <ui:checkbox.label>Text message</ui:checkbox.label>
    </ui:checkbox.root>
    <ui:fieldset.errorText>Choose at least one way to reach you.</ui:fieldset.errorText>
</ui:fieldset.root>

```

### Toggling Disabled at Runtime

Fields watch the `disabled` attribute of the fieldset they are in, so changing it afterwards reaches them too. Mount the fieldset yourself (`autoMount="{false}"`) to get hold of the instance, and update its props:

**FieldsetExamples.html**

```html
<ui:exposeToClient />

<div class="max-w-74 space-y-4">
    <ui:switch.root autoMount="{false}" rootId="edit-mode">
        <ui:switch.label>Edit mode</ui:switch.label>
        <ui:switch.control />
    </ui:switch.root>

    <ui:fieldset.root disabled="{true}" autoMount="{false}" rootId="edit-fieldset">
        <ui:fieldset.legend>Profile</ui:fieldset.legend>
        <ui:field.root name="displayName">
            <ui:input.root type="text" defaultValue="Ada Lovelace">
                <ui:input.label>Display name</ui:input.label>
                <ui:input.input />
            </ui:input.root>
        </ui:field.root>
        <ui:field.root name="public">
            <ui:checkbox.root>
                <ui:checkbox.control />
                <ui:checkbox.label>Show my profile publicly</ui:checkbox.label>
            </ui:checkbox.root>
        </ui:field.root>
    </ui:fieldset.root>
</div>

<vite:asset entry="EXT:docs/Resources/Private/Components/ui/FieldsetExamples/ToggleDisabled.entry.ts" />

```

**ToggleDisabled.entry.ts**

```ts
import { mount, mountAll } from 'fluid-primitives';
import { Fieldset } from 'fluid-primitives/fieldset';
import { Switch } from 'fluid-primitives/switch';

mountAll('ui:fieldsetExamples.toggleDisabled', () => {
    const fieldset = mount('ui:fieldset', 'edit-fieldset', ({ props }) => {
        const instance = new Fieldset(props);
        instance.init();
        return instance;
    });

    mount('ui:switch', 'edit-mode', ({ props }) => {
        const instance = new Switch({
            ...props,
            onCheckedChange: ({ checked }) => fieldset?.updateProps({ disabled: !checked }),
        });
        instance.init();
        return instance;
    });
});

```

## API Reference

### fieldset.root

Provides the fieldset state and wraps the group. Renders a `<fieldset>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `disabled` | `boolean` | No | `-` | Whether the fieldset is disabled. Native `<fieldset disabled>` semantics carry the disabled state to every control inside, and to every `Field` inside it. |
| `invalid` | `boolean` | No | `-` | Whether the fieldset is invalid. Not inherited by the fields inside it. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `rootId` | `string` | No | `-` | The root ID of the component, used for hydration and identification. |
| `ids` | `array` | No | `[]` | The IDs of of the component parts for composition. |
| `autoMount` | `boolean` | No | `true` | Whether the client initializes the component on its own. Set it to false to mount it yourself with `mount()` |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-fieldset-root` | <uid> |
| `data-disabled` | Present when disabled |
| `data-invalid` | Present when invalid |

### fieldset.legend

Names the group. Renders a `<legend>` element, which needs to be the first child of the root.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-fieldset-legend` | <uid> |
| `data-disabled` | Present when disabled |
| `data-invalid` | Present when invalid |

### fieldset.helperText

Explains the group. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-fieldset-helper-text` | <uid> |
| `data-disabled` | Present when disabled |
| `data-invalid` | Present when invalid |

### fieldset.errorText

Tells what is wrong with the group, hidden unless the fieldset is `invalid`. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-fieldset-error-text` | <uid> |
| `data-disabled` | Present when disabled |
| `data-invalid` | Present when invalid |

### Machine JavaScript API

| Name | Type | Description |
| --- | --- | --- |
| `ids` | `ResolvedElementIds` | The resolved element ids, for composition with other components. |
| `disabled` | `boolean` | Whether the fieldset is disabled (including inherited ancestor fieldset state). |
| `invalid` | `boolean` | Whether the fieldset is invalid. |
| `getRootState` | `() => RootState` | Returns the state of the root. |
| `getLegendState` | `() => LegendState` | Returns the state of the legend. |
| `getHelperTextState` | `() => HelperTextState` | Returns the state of the helper text. |
| `getErrorTextState` | `() => ErrorTextState` | Returns the state of the error text. |
