Fieldset

Group related form controls with legends, descriptions, and actions

Usage

import { Fieldset } from '@hareui/vue'
Profile SettingsUpdate your profile information.
Minimum 10 characters
<script setup lang="ts">import { Icon } from '@iconify/vue'import { Button, Fieldset, Form, Input, TextArea, TextField } from '@hareui/vue'function onSubmit(e: Event) {  e.preventDefault()  alert('Form submitted successfully!')}const validateName = (value: string | number) => String(value).length < 3 ? 'Name must be at least 3 characters' : nullconst validateBio = (value: string | number) => String(value).length < 10 ? 'Bio must be at least 10 characters' : null</script><template>  <Form class="w-full max-w-96" @submit="onSubmit">    <Fieldset legend="Profile Settings" description="Update your profile information.">      <TextField required name="name" label="Name" :validate="validateName">        <Input placeholder="John Doe" />      </TextField>      <TextField required name="email" type="email" label="Email">        <Input placeholder="[email protected]" />      </TextField>      <TextField required name="bio" label="Bio" description="Minimum 10 characters" :validate="validateBio">        <TextArea placeholder="Tell us about yourself..." />      </TextField>    <template #actions>      <Button type="submit">        <Icon icon="gravity-ui:floppy-disk" />        Save changes      </Button>      <Button type="reset" variant="secondary">        Cancel      </Button>    </template>    </Fieldset>  </Form></template>

Anatomy

HeroUI's compound parts (Fieldset.Legend, Fieldset.Group, Fieldset.Actions) become props and named slots, rendered in a fixed order: legend, description, field group, then actions.

<Fieldset legend="…" description="…">
  <template #legend /> <!-- or: replaces the legend text -->
  <template #description /> <!-- or: replaces the description text -->
  <!-- default slot = form fields, wrapped in the field group -->
  <template #actions /> <!-- action buttons or helper text -->
</Fieldset>
  • Each part renders only when its prop or slot is provided.
  • disabled sets the native disabled attribute and data-disabled="true", and disables HareUI controls inside (such as Button and Link) the same way HeroUI's fieldset does.

ui keys: base, legend, description, fieldGroup, actions.

Examples

In Surface

When used inside a Surface component, use variant="secondary" on form controls (Input, TextArea, etc.) to apply the lower emphasis variant suitable for surface backgrounds.

Profile SettingsUpdate your profile information.
Minimum 10 characters
<script setup lang="ts">import { Icon } from '@iconify/vue'import { Button, Fieldset, Form, Input, Surface, TextArea, TextField } from '@hareui/vue'function onSubmit(e: Event) {  e.preventDefault()  alert('Form submitted successfully!')}const validateName = (value: string | number) => String(value).length < 3 ? 'Name must be at least 3 characters' : nullconst validateBio = (value: string | number) => String(value).length < 10 ? 'Bio must be at least 10 characters' : null</script><template>  <div class="flex items-center justify-center rounded-3xl bg-surface p-6">    <Surface class="w-full min-w-[380px]">      <Form @submit="onSubmit">        <Fieldset class="w-full" legend="Profile Settings" description="Update your profile information.">          <TextField required name="name" label="Name" :validate="validateName">            <Input placeholder="John Doe" variant="secondary" />          </TextField>          <TextField required name="email" type="email" label="Email">            <Input placeholder="[email protected]" variant="secondary" />          </TextField>          <TextField required name="bio" label="Bio" description="Minimum 10 characters" :validate="validateBio">            <TextArea placeholder="Tell us about yourself..." variant="secondary" />          </TextField>          <template #actions>            <Button type="submit">              <Icon icon="gravity-ui:floppy-disk" />              Save changes            </Button>            <Button type="reset" variant="tertiary">              Cancel            </Button>          </template>        </Fieldset>      </Form>    </Surface>  </div></template>

Customization

Tailwind CSS

Profile SettingsUpdate your profile information.
Minimum 10 characters
<script setup lang="ts">import { Icon } from '@iconify/vue'import { Button, Fieldset, Form, Input, TextArea, TextField } from '@hareui/vue'function onSubmit(e: Event) {  e.preventDefault()  alert('Form submitted successfully!')}const validateName = (value: string | number) => String(value).length < 3 ? 'Name must be at least 3 characters' : nullconst validateBio = (value: string | number) => String(value).length < 10 ? 'Bio must be at least 10 characters' : nullconst shell = 'rounded-xl border border-border/70 bg-linear-to-b from-neutral-50/90 to-white p-4 ring-1 ring-black/5 dark:from-neutral-900/80 dark:to-neutral-900 dark:ring-white/10'const field = 'rounded-xl border border-border/80 bg-surface shadow-sm ring-1 ring-black/5 transition-[box-shadow,border-color] focus-visible:ring-2 focus-visible:ring-neutral-400/25 dark:ring-white/10 dark:focus-visible:ring-neutral-500/30'</script><template>  <Form class="w-full max-w-96" @submit="onSubmit">    <Fieldset      :class="shell"      legend="Profile Settings"      description="Update your profile information."      :ui="{ legend: 'font-medium text-neutral-800 dark:text-neutral-100', description: 'text-neutral-600 dark:text-neutral-400' }"    >      <TextField required name="name" label="Name" :validate="validateName">        <Input :class="field" placeholder="John Doe" />      </TextField>      <TextField required name="email" type="email" label="Email">        <Input :class="field" placeholder="[email protected]" />      </TextField>      <TextField required name="bio" label="Bio" description="Minimum 10 characters" :validate="validateBio">        <TextArea :class="field" placeholder="Tell us about yourself..." />      </TextField>    <template #actions>      <Button type="submit">        <Icon icon="gravity-ui:floppy-disk" />        Save changes      </Button>      <Button type="reset" variant="secondary">        Cancel      </Button>    </template>    </Fieldset>  </Form></template>

Global theme override

Override the fieldset theme app-wide with createHareUI:

app.use(createHareUI({
  ui: {
    fieldset: {
      slots: {
        base: 'gap-5 rounded-xl border border-border/60 bg-surface p-6 shadow-field',
        legend: 'text-lg font-semibold',
        fieldGroup: 'gap-3 md:grid md:grid-cols-2',
        actions: 'flex justify-end gap-2 pt-2',
      },
    },
  },
}))

Styling Reference

Each rendered element carries the same data-slot string as HeroUI, so selectors translate 1:1.

ui keydata-slotDescription
basefieldsetRoot container
legendfieldset-legendLegend element
descriptiondescriptionDescription below the legend
fieldGroupfieldset-field-groupWrapper for grouped fields
actionsfieldset-actionsAction bar below the fields

API Reference

Props

PropTypeDefaultDescription
asAsTag | Component"fieldset"The element or component to render as.
legendstring-The legend text.
descriptionstring-Help text rendered below the legend.
disabledbooleanfalseDisables every control inside, like a native <fieldset disabled>, including div-based ones.
uiComponentSlots<{ slots: { base: string; legend: string; description: string; fieldGroup: string; actions: string; }; }>-Per-slot class overrides.

Slots

SlotPropsDescription
legendanyThe legend content. Replaces the legend prop.
descriptionanyThe description content. Replaces the description prop.
defaultanyForm fields, rendered inside the field group.
actionsanyAction buttons or helper text below the fields.

On this page

No Headings