Fieldset
A set of form controls optionally grouped under a common name.
Examples
The Fieldset component provides contexts such as invalid and disabled for form elements. While most Ark UI
components natively support these contexts, you can also use the Field component with standard HTML form elements.
Basic Usage
Learn how to use the Fieldset component in your project. Let's take a look at the most basic example:
import { Field } from '@ark-ui/react/field'
import { Fieldset } from '@ark-ui/react/fieldset'
export const Basic = () => {
return (
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Field.Input type="text" />
<Fieldset.HelperText>Helper text</Fieldset.HelperText>
<Fieldset.ErrorText>Error text</Fieldset.ErrorText>
</Fieldset.Root>
)
}
import { Field } from '@ark-ui/solid/field'
import { Fieldset } from '@ark-ui/solid/fieldset'
export const Basic = () => {
return (
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Field.Input type="text" />
<Fieldset.HelperText>Helper text</Fieldset.HelperText>
<Fieldset.ErrorText>Error text</Fieldset.ErrorText>
</Fieldset.Root>
)
}
<script setup lang="ts">
import { Field } from '@ark-ui/vue/field'
import { Fieldset } from '@ark-ui/vue/fieldset'
</script>
<template>
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Field.Input type="text" />
<Fieldset.HelperText>Helper text</Fieldset.HelperText>
<Fieldset.ErrorText>Error text</Fieldset.ErrorText>
</Fieldset.Root>
</template>
<script lang="ts">
import { Field } from '@ark-ui/svelte/field'
import { Fieldset } from '@ark-ui/svelte/fieldset'
</script>
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Field.Input type="text" />
<Fieldset.HelperText>Helper text</Fieldset.HelperText>
<Fieldset.ErrorText>Error text</Fieldset.ErrorText>
</Fieldset.Root>
Field
This example demonstrates how to use the Field component with a standard input field within a Fieldset.
import { Field } from '@ark-ui/react/field'
import { Fieldset } from '@ark-ui/react/fieldset'
export const WithField = () => {
return (
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Fieldset.HelperText>Fieldset Helper Text</Fieldset.HelperText>
<Fieldset.ErrorText>Fieldset Error Text</Fieldset.ErrorText>
<Field.Root>
<Field.Label>Label</Field.Label>
<Field.Input />
<Field.HelperText>Field Helper Text</Field.HelperText>
<Field.ErrorText>Field Error Text</Field.ErrorText>
</Field.Root>
</Fieldset.Root>
)
}
import { Field } from '@ark-ui/solid/field'
import { Fieldset } from '@ark-ui/solid/fieldset'
export const WithField = () => {
return (
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Fieldset.HelperText>Fieldset Helper Text</Fieldset.HelperText>
<Fieldset.ErrorText>Fieldset Error Text</Fieldset.ErrorText>
<Field.Root>
<Field.Label>Label</Field.Label>
<Field.Input />
<Field.HelperText>Field Helper Text</Field.HelperText>
<Field.ErrorText>Field Error Text</Field.ErrorText>
</Field.Root>
</Fieldset.Root>
)
}
<script setup lang="ts">
import { Field } from '@ark-ui/vue/field'
import { Fieldset } from '@ark-ui/vue/fieldset'
</script>
<template>
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Fieldset.HelperText>Fieldset Helper Text</Fieldset.HelperText>
<Fieldset.ErrorText>Fieldset Error Text</Fieldset.ErrorText>
<Field.Root>
<Field.Label>Label</Field.Label>
<Field.Input />
<Field.HelperText>Field Helper Text</Field.HelperText>
<Field.ErrorText>Field Error Text</Field.ErrorText>
</Field.Root>
</Fieldset.Root>
</template>
<script lang="ts">
import { Field } from '@ark-ui/svelte/field'
import { Fieldset } from '@ark-ui/svelte/fieldset'
</script>
<Fieldset.Root>
<Fieldset.Legend>Personal Information</Fieldset.Legend>
<Field.Root>
<Field.Label>First Name</Field.Label>
<Field.Input placeholder="Enter your first name" />
</Field.Root>
<Field.Root>
<Field.Label>Last Name</Field.Label>
<Field.Input placeholder="Enter your last name" />
</Field.Root>
<Field.Root>
<Field.Label>Email</Field.Label>
<Field.Input type="email" placeholder="Enter your email" />
<Field.HelperText>We'll never share your email</Field.HelperText>
</Field.Root>
<Fieldset.HelperText>Please fill out all required fields</Fieldset.HelperText>
</Fieldset.Root>
Checkbox
This example shows how to use the Fieldset component with other Ark UI form elements like Checkbox.
import { Checkbox } from '@ark-ui/react/checkbox'
import { Field } from '@ark-ui/react/field'
import { Fieldset } from '@ark-ui/react/fieldset'
export const WithCheckbox = () => {
return (
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Fieldset.HelperText>Fieldset Helper Text</Fieldset.HelperText>
<Fieldset.ErrorText>Fieldset Error Text</Fieldset.ErrorText>
<Field.Root>
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>✔️</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Field Heler Text</Field.HelperText>
<Field.ErrorText>Field Error Text</Field.ErrorText>
</Field.Root>
</Fieldset.Root>
)
}
import { Checkbox } from '@ark-ui/solid/checkbox'
import { Field } from '@ark-ui/solid/field'
import { Fieldset } from '@ark-ui/solid/fieldset'
export const WithCheckbox = () => {
return (
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Fieldset.HelperText>Fieldset Helper Text</Fieldset.HelperText>
<Fieldset.ErrorText>Fieldset Error Text</Fieldset.ErrorText>
<Field.Root>
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>✔️</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Field Heler Text</Field.HelperText>
<Field.ErrorText>Field Error Text</Field.ErrorText>
</Field.Root>
</Fieldset.Root>
)
}
<script setup lang="ts">
import { Checkbox } from '@ark-ui/vue/checkbox'
import { Field } from '@ark-ui/vue/field'
import { Fieldset } from '@ark-ui/vue/fieldset'
</script>
<template>
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Fieldset.HelperText>Fieldset Helper Text</Fieldset.HelperText>
<Fieldset.ErrorText>Fieldset Error Text</Fieldset.ErrorText>
<Field.Root>
<Checkbox.Root>
<Checkbox.Label>Checkbox</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>✔️</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Field Heler Text</Field.HelperText>
<Field.ErrorText>Field Error Text</Field.ErrorText>
</Field.Root>
</Fieldset.Root>
</template>
<script lang="ts">
import { Checkbox } from '@ark-ui/svelte/checkbox'
import { Field } from '@ark-ui/svelte/field'
import { Fieldset } from '@ark-ui/svelte/fieldset'
</script>
<Fieldset.Root>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Fieldset.HelperText>Helper text</Fieldset.HelperText>
<Fieldset.ErrorText>Error text</Fieldset.ErrorText>
<Field.Root>
<Checkbox.Root>
<Checkbox.Label>Label</Checkbox.Label>
<Checkbox.Control>
<Checkbox.Indicator>✔️</Checkbox.Indicator>
</Checkbox.Control>
<Checkbox.HiddenInput />
</Checkbox.Root>
<Field.HelperText>Field Heler Text</Field.HelperText>
<Field.ErrorText>Field Error Text</Field.ErrorText>
</Field.Root>
</Fieldset.Root>
Root Provider
The RootProvider component provides a context for the fieldset. It accepts the value of the useFieldset hook. You
can leverage it to access the component state and methods from outside the fieldset.
import { Fieldset, useFieldset } from '@ark-ui/react/fieldset'
export const RootProvider = () => {
const fieldset = useFieldset({
disabled: true,
})
return (
<Fieldset.RootProvider value={fieldset}>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Fieldset.HelperText>Helper text</Fieldset.HelperText>
<Fieldset.ErrorText>Error text</Fieldset.ErrorText>
</Fieldset.RootProvider>
)
}
import { Fieldset, useFieldset } from '@ark-ui/solid/fieldset'
export const RootProvider = () => {
const fieldset = useFieldset({
disabled: true,
})
return (
<Fieldset.RootProvider value={fieldset}>
<Fieldset.Legend>Legend</Fieldset.Legend>
<Fieldset.HelperText>Helper text</Fieldset.HelperText>
<Fieldset.ErrorText>Error text</Fieldset.ErrorText>
</Fieldset.RootProvider>
)
}
<script setup lang="ts">
import { Fieldset, useFieldset } from '@ark-ui/vue/fieldset'
const fieldset = useFieldset()
</script>
<template>
<Fieldset.RootProvider :value="fieldset">
<Fieldset.Legend>Contact Information</Fieldset.Legend>
<div>
<label for="name">Name</label>
<input id="name" type="text" required />
</div>
<div>
<label for="email">Email</label>
<input id="email" type="email" required />
</div>
<Fieldset.HelperText>Please fill out all required fields</Fieldset.HelperText>
</Fieldset.RootProvider>
</template>
<script lang="ts">
import { Fieldset, useFieldset } from '@ark-ui/svelte/fieldset'
const fieldset = useFieldset({ disabled: true })
</script>
<Fieldset.RootProvider value={fieldset}>
<Fieldset.Legend>External State Management</Fieldset.Legend>
<input type="text" placeholder="This input inherits disabled state" />
<Fieldset.HelperText>Managed by external fieldset state</Fieldset.HelperText>
</Fieldset.RootProvider>
<Fieldset.Context>
{#snippet render(fieldset)}
<div style="margin-top: 1rem;">
<p>Fieldset disabled: {fieldset().disabled}</p>
<p>Fieldset invalid: {fieldset().invalid}</p>
</div>
{/snippet}
</Fieldset.Context>
If you're using the
RootProvidercomponent, you don't need to use theRootcomponent.
Input with Select
This example shows how to use the Fieldset component with Field.Input and Select to create a interactive phone
input component.
import { Field } from '@ark-ui/react/field'
import { Fieldset } from '@ark-ui/react/fieldset'
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { useRef } from 'react'
export const PhoneInput = () => {
const extensions = createListCollection({
items: ['+1', '+44', '+49', '+41'],
})
const inputRef = useRef<HTMLInputElement | null>(null)
const focusInput = () => {
setTimeout(() => {
inputRef.current?.focus()
})
}
return (
<Fieldset.Root style={{ border: '0', padding: '0' }}>
<Fieldset.Legend onClick={focusInput}>Mobile Number</Fieldset.Legend>
<div style={{ display: 'flex', alignItems: 'flex-start' }}>
<Field.Root>
<Select.Root collection={extensions} defaultValue={['+1']} onValueChange={focusInput}>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select" />
</Select.Trigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
{extensions.items.map((item) => (
<Select.Item key={item} item={item}>
<Select.ItemText>{item}</Select.ItemText>
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
</Field.Root>
<Field.Root>
<Field.Input ref={inputRef} />
</Field.Root>
</div>
</Fieldset.Root>
)
}
import { Field } from '@ark-ui/solid/field'
import { Fieldset } from '@ark-ui/solid/fieldset'
import { Select, createListCollection } from '@ark-ui/solid/select'
import { Index, createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
export const PhoneInput = () => {
const extensions = createListCollection({
items: ['+1', '+44', '+49', '+41'],
})
const [inputRef, setInputRef] = createSignal<HTMLInputElement | null>(null)
const focusInput = () => {
setTimeout(() => {
inputRef()?.focus()
})
}
return (
<Fieldset.Root style={{ border: '0', padding: '0' }}>
<Fieldset.Legend onClick={focusInput}>Mobile Number</Fieldset.Legend>
<div style={{ display: 'flex', 'align-items': 'flex-start' }}>
<Field.Root>
<Select.Root collection={extensions} defaultValue={['+1']} onValueChange={focusInput}>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select" />
</Select.Trigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Index each={extensions.items}>
{(item) => (
<Select.Item item={item}>
<Select.ItemText>{item()}</Select.ItemText>
</Select.Item>
)}
</Index>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
</Field.Root>
<Field.Root>
<Field.Input ref={setInputRef} />
</Field.Root>
</div>
</Fieldset.Root>
)
}
<script setup lang="ts">
import { Field } from '@ark-ui/vue/field'
import { Fieldset } from '@ark-ui/vue/fieldset'
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ref } from 'vue'
const extensions = createListCollection({
items: ['+1', '+44', '+49', '+41'],
})
const inputRef = ref<{ $el: HTMLInputElement | null } | null>(null)
const focusInput = () => {
setTimeout(() => {
inputRef.value?.$el?.focus()
})
}
</script>
<template>
<Fieldset.Root style="border: 0; padding: 0">
<Fieldset.Legend @click="focusInput">Mobile Number</Fieldset.Legend>
<div style="display: flex; align-items: flex-start">
<Field.Root>
<Select.Root :collection="extensions" :default-value="['+1']" @value-change="focusInput">
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select" />
</Select.Trigger>
</Select.Control>
<Select.Positioner>
<Select.Content>
<Select.Item v-for="item in extensions" :key="item" :item="item">
<Select.ItemText>{{ item }}</Select.ItemText>
</Select.Item>
</Select.Content>
</Select.Positioner>
<Select.HiddenSelect />
</Select.Root>
</Field.Root>
<Field.Root>
<Field.Input ref="inputRef" />
</Field.Root>
</div>
</Fieldset.Root>
</template>
<script lang="ts">
import { Field } from '@ark-ui/svelte/field'
import { Fieldset } from '@ark-ui/svelte/fieldset'
import { Portal } from '@ark-ui/svelte/portal'
import { Select, createListCollection } from '@ark-ui/svelte/select'
const extensions = createListCollection({
items: ['+1', '+44', '+49', '+41'],
})
let input: HTMLInputElement | null = null
const focusInput = () => {
setTimeout(() => {
input?.focus()
})
}
</script>
<Fieldset.Root>
<Fieldset.Legend onclick={focusInput}>Mobile Number</Fieldset.Legend>
<div style="display: flex; align-items: flex-start;">
<Field.Root>
<Select.Root collection={extensions} defaultValue={['+1']} onValueChange={focusInput}>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select" />
</Select.Trigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
{#each extensions.items as item}
<Select.Item {item}>
<Select.ItemText>{item}</Select.ItemText>
</Select.Item>
{/each}
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
</Field.Root>
<Field.Root>
<Field.Input bind:ref={input} />
</Field.Root>
</div>
</Fieldset.Root>
API Reference
Root
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
invalid | booleanIndicates whether the fieldset is invalid. |
ErrorText
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
HelperText
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Legend
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
RootProvider
| Prop | Default | Type |
|---|---|---|
value | { refs: { rootRef: RefObject<HTMLFieldSetElement | null>; }; disabled: boolean; invalid: boolean; getRootProps: () => Omit<DetailedHTMLProps<FieldsetHTMLAttributes<HTMLFieldSetElement>, HTMLFieldSetElement>, "ref">; getLegendProps: () => Omit<...>; getHelperTextProps: () => Omit<...>; getErrorTextProps: () => Omit<.... | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |