Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/button" "github.com/axadrn/shadcn-templ/v2/components/checkbox" "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/input" selectcomp "github.com/axadrn/shadcn-templ/v2/components/select" "github.com/axadrn/shadcn-templ/v2/components/textarea") templ FieldDemo() { <div class="w-full max-w-md"> <form> @field.Group() { @field.Set() { @field.Legend() { Payment Method } @field.Description() { All transactions are secure and encrypted } @field.Group() { @field.Field() { @field.Label(field.LabelProps{For: "checkout-7j9-card-name-43j"}) { Name on Card } @input.Input(input.Props{ ID: "checkout-7j9-card-name-43j", Placeholder: "Axel Adrian", }) } @field.Field() { @field.Label(field.LabelProps{For: "checkout-7j9-card-number-uw1"}) { Card Number } @input.Input(input.Props{ ID: "checkout-7j9-card-number-uw1", Placeholder: "1234 5678 9012 3456", }) @field.Description() { Enter your 16-digit card number } } <div class="grid grid-cols-3 gap-4"> @field.Field() { @field.Label(field.LabelProps{For: "checkout-exp-month-ts6"}) { Month } @selectcomp.Select() { @selectcomp.Trigger(selectcomp.TriggerProps{ID: "checkout-exp-month-ts6"}) { @selectcomp.Value(selectcomp.ValueProps{Placeholder: "MM"}) } @selectcomp.Content() { @selectcomp.Item(selectcomp.ItemProps{Value: "01"}) { 01 } @selectcomp.Item(selectcomp.ItemProps{Value: "02"}) { 02 } @selectcomp.Item(selectcomp.ItemProps{Value: "03"}) { 03 } @selectcomp.Item(selectcomp.ItemProps{Value: "04"}) { 04 } @selectcomp.Item(selectcomp.ItemProps{Value: "05"}) { 05 } @selectcomp.Item(selectcomp.ItemProps{Value: "06"}) { 06 } @selectcomp.Item(selectcomp.ItemProps{Value: "07"}) { 07 } @selectcomp.Item(selectcomp.ItemProps{Value: "08"}) { 08 } @selectcomp.Item(selectcomp.ItemProps{Value: "09"}) { 09 } @selectcomp.Item(selectcomp.ItemProps{Value: "10"}) { 10 } @selectcomp.Item(selectcomp.ItemProps{Value: "11"}) { 11 } @selectcomp.Item(selectcomp.ItemProps{Value: "12"}) { 12 } } } } @field.Field() { @field.Label(field.LabelProps{For: "checkout-7j9-exp-year-f59"}) { Year } @selectcomp.Select() { @selectcomp.Trigger(selectcomp.TriggerProps{ID: "checkout-7j9-exp-year-f59"}) { @selectcomp.Value(selectcomp.ValueProps{Placeholder: "YYYY"}) } @selectcomp.Content() { @selectcomp.Item(selectcomp.ItemProps{Value: "2024"}) { 2024 } @selectcomp.Item(selectcomp.ItemProps{Value: "2025"}) { 2025 } @selectcomp.Item(selectcomp.ItemProps{Value: "2026"}) { 2026 } @selectcomp.Item(selectcomp.ItemProps{Value: "2027"}) { 2027 } @selectcomp.Item(selectcomp.ItemProps{Value: "2028"}) { 2028 } @selectcomp.Item(selectcomp.ItemProps{Value: "2029"}) { 2029 } } } } @field.Field() { @field.Label(field.LabelProps{For: "checkout-7j9-cvv"}) { CVV } @input.Input(input.Props{ ID: "checkout-7j9-cvv", Placeholder: "123", }) } </div> } } @field.Separator() @field.Set() { @field.Legend() { Billing Address } @field.Description() { The billing address associated with your payment method } @field.Group() { @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @checkbox.Checkbox(checkbox.Props{ ID: "checkout-7j9-same-as-shipping-wgm", Checked: true, }) @field.Label(field.LabelProps{ For: "checkout-7j9-same-as-shipping-wgm", Class: "font-normal", }) { Same as shipping address } } } } @field.Set() { @field.Group() { @field.Field() { @field.Label(field.LabelProps{For: "checkout-7j9-optional-comments"}) { Comments } @textarea.Textarea(textarea.Props{ ID: "checkout-7j9-optional-comments", Placeholder: "Add any additional comments", Class: "resize-none", }) } } } @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @button.Button(button.Props{Type: button.TypeSubmit}) { Submit } @button.Button(button.Props{ Variant: button.VariantOutline, Type: button.TypeButton, }) { Cancel } } } </form> </div>}Installation
Usage
Composition
Field
A single control with label, helper text, and validation.
FieldGroup
Related fields in one group. Use field.Separator between sections when needed.
FieldSet
Semantic grouping with a legend and description, usually containing a field.Group.
Anatomy
The Field family is designed for composing accessible forms. A typical field is structured as follows:
Fieldis the core wrapper for a single field.field.Contentis a flex column that groups label and description. Not required if you have no description.- Wrap related fields with
field.Group, and usefield.Setwithfield.Legendfor semantic grouping.
Form
Fields compose with plain HTML forms, the controls submit their native values.
Input
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/input") templ FieldInput() { @field.Set(field.SetProps{Class: "w-full max-w-xs"}) { @field.Group() { @field.Field() { @field.Label(field.LabelProps{For: "username"}) { Username } @input.Input(input.Props{ ID: "username", Type: "text", Placeholder: "axadrn", }) @field.Description() { Choose a unique username for your account. } } @field.Field() { @field.Label(field.LabelProps{For: "password"}) { Password } @field.Description() { Must be at least 8 characters long. } @input.Input(input.Props{ ID: "password", Type: "password", Placeholder: "••••••••", }) } } }}Textarea
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/textarea") templ FieldTextarea() { @field.Set(field.SetProps{Class: "w-full max-w-xs"}) { @field.Group() { @field.Field() { @field.Label(field.LabelProps{For: "feedback"}) { Feedback } @textarea.Textarea(textarea.Props{ ID: "feedback", Placeholder: "Your feedback helps us improve...", Rows: 4, }) @field.Description() { Share your thoughts about our service. } } } }}Select
Select your department or area of work.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/field" selectcomp "github.com/axadrn/shadcn-templ/v2/components/select") templ FieldSelect() { @field.Field(field.Props{Class: "w-full max-w-xs"}) { @field.Label() { Department } @selectcomp.Select() { @selectcomp.Trigger() { @selectcomp.Value(selectcomp.ValueProps{Placeholder: "Choose department"}) } @selectcomp.Content() { @selectcomp.Item(selectcomp.ItemProps{Value: "engineering"}) { Engineering } @selectcomp.Item(selectcomp.ItemProps{Value: "design"}) { Design } @selectcomp.Item(selectcomp.ItemProps{Value: "marketing"}) { Marketing } @selectcomp.Item(selectcomp.ItemProps{Value: "sales"}) { Sales } @selectcomp.Item(selectcomp.ItemProps{Value: "support"}) { Customer Support } @selectcomp.Item(selectcomp.ItemProps{Value: "hr"}) { Human Resources } @selectcomp.Item(selectcomp.ItemProps{Value: "finance"}) { Finance } @selectcomp.Item(selectcomp.ItemProps{Value: "operations"}) { Operations } } } @field.Description() { Select your department or area of work. } }}Slider
Set your budget range ($200 - 800).
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/slider") templ FieldSlider() { @field.Field(field.Props{Class: "w-full max-w-xs"}) { @field.Title() { Price Range } @field.Description() { Set your budget range ($<span id="field-slider-min" class="font-medium tabular-nums">200</span> - <span id="field-slider-max" class="font-medium tabular-nums">800</span>). } @slider.Slider(slider.Props{ ID: "field-slider", Value: []float64{200, 800}, Min: 0, Max: 1000, Step: 10, Class: "mt-2 w-full", Attributes: templ.Attributes{ "aria-label": "Price Range", }, }) } <script> // The vanilla pendant of the demo's React state. (() => { const root = document.getElementById("field-slider"); root.addEventListener("slider-change", (e) => { document.getElementById("field-slider-min").textContent = e.detail.values[0]; document.getElementById("field-slider-max").textContent = e.detail.values[1]; }); })(); </script>}Fieldset
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/input") templ FieldFieldset() { @field.Set(field.SetProps{Class: "w-full max-w-sm"}) { @field.Legend() { Address Information } @field.Description() { We need your address to deliver your order. } @field.Group() { @field.Field() { @field.Label(field.LabelProps{For: "street"}) { Street Address } @input.Input(input.Props{ ID: "street", Type: "text", Placeholder: "123 Main St", }) } <div class="grid grid-cols-2 gap-4"> @field.Field() { @field.Label(field.LabelProps{For: "city"}) { City } @input.Input(input.Props{ ID: "city", Type: "text", Placeholder: "New York", }) } @field.Field() { @field.Label(field.LabelProps{For: "zip"}) { Postal Code } @input.Input(input.Props{ ID: "zip", Type: "text", Placeholder: "90502", }) } </div> } }}Checkbox
Your Desktop & Documents folders are being synced with iCloud Drive. You can access them from other devices.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/checkbox" "github.com/axadrn/shadcn-templ/v2/components/field") templ FieldCheckbox() { @field.Group(field.GroupProps{Class: "w-full max-w-xs"}) { @field.Set() { @field.Legend(field.LegendProps{Variant: field.LegendVariantLabel}) { Show these items on the desktop } @field.Description() { Select the items you want to show on the desktop. } @field.Group(field.GroupProps{Class: "gap-3"}) { @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @checkbox.Checkbox(checkbox.Props{ID: "finder-pref-9k2-hard-disks-ljj"}) @field.Label(field.LabelProps{ For: "finder-pref-9k2-hard-disks-ljj", Class: "font-normal", }) { Hard disks } } @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @checkbox.Checkbox(checkbox.Props{ID: "finder-pref-9k2-external-disks-1yg"}) @field.Label(field.LabelProps{ For: "finder-pref-9k2-external-disks-1yg", Class: "font-normal", }) { External disks } } @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @checkbox.Checkbox(checkbox.Props{ID: "finder-pref-9k2-cds-dvds-fzt"}) @field.Label(field.LabelProps{ For: "finder-pref-9k2-cds-dvds-fzt", Class: "font-normal", }) { CDs, DVDs, and iPods } } @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @checkbox.Checkbox(checkbox.Props{ID: "finder-pref-9k2-connected-servers-6l2"}) @field.Label(field.LabelProps{ For: "finder-pref-9k2-connected-servers-6l2", Class: "font-normal", }) { Connected servers } } } } @field.Separator() @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @checkbox.Checkbox(checkbox.Props{ ID: "finder-pref-9k2-sync-folders-nep", Checked: true, }) @field.Content() { @field.Label(field.LabelProps{For: "finder-pref-9k2-sync-folders-nep"}) { Sync Desktop & Documents folders } @field.Description() { Your Desktop & Documents folders are being synced with iCloud Drive. You can access them from other devices. } } } }}Radio
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/radiogroup") templ FieldRadio() { @field.Set(field.SetProps{Class: "w-full max-w-xs"}) { @field.Legend(field.LegendProps{Variant: field.LegendVariantLabel}) { Subscription Plan } @field.Description() { Yearly and lifetime plans offer significant savings. } <div data-slot="radio-group" class="grid gap-3"> @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @radiogroup.Item(radiogroup.ItemProps{ Name: "plan", Value: "monthly", ID: "plan-monthly", Checked: true, }) @field.Label(field.LabelProps{ For: "plan-monthly", Class: "font-normal", }) { Monthly ($9.99/month) } } @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @radiogroup.Item(radiogroup.ItemProps{ Name: "plan", Value: "yearly", ID: "plan-yearly", }) @field.Label(field.LabelProps{ For: "plan-yearly", Class: "font-normal", }) { Yearly ($99.99/year) } } @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @radiogroup.Item(radiogroup.ItemProps{ Name: "plan", Value: "lifetime", ID: "plan-lifetime", }) @field.Label(field.LabelProps{ For: "plan-lifetime", Class: "font-normal", }) { Lifetime ($299.99) } } </div> }}Switch
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/field" switchcomp "github.com/axadrn/shadcn-templ/v2/components/switch") templ FieldSwitch() { @field.Field(field.Props{ Orientation: field.OrientationHorizontal, Class: "w-fit", }) { @field.Label(field.LabelProps{For: "2fa"}) { Multi-factor authentication } @switchcomp.Switch(switchcomp.Props{ID: "2fa"}) }}Choice Card
Wrap field.Field components inside field.Label to create selectable field groups. This works with radio.Radio, checkbox.Checkbox and switchcomp.Switch components.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/radiogroup") templ FieldChoiceCard() { @field.Group(field.GroupProps{Class: "w-full max-w-xs"}) { @field.Set() { @field.Legend(field.LegendProps{Variant: field.LegendVariantLabel}) { Compute Environment } @field.Description() { Select the compute environment for your cluster. } <div data-slot="radio-group" class="grid gap-3"> @field.Label(field.LabelProps{For: "kubernetes-r2h"}) { @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @field.Content() { @field.Title() { Kubernetes } @field.Description() { Run GPU workloads on a K8s cluster. } } @radiogroup.Item(radiogroup.ItemProps{ Name: "compute", Value: "kubernetes", ID: "kubernetes-r2h", Checked: true, }) } } @field.Label(field.LabelProps{For: "vm-z4k"}) { @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @field.Content() { @field.Title() { Virtual Machine } @field.Description() { Access a cluster to run GPU workloads. } } @radiogroup.Item(radiogroup.ItemProps{ Name: "compute", Value: "vm", ID: "vm-z4k", }) } } </div> } }}Field Group
Stack field.Field components with field.Group. Add field.Separator to divide them.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/checkbox" "github.com/axadrn/shadcn-templ/v2/components/field") templ FieldGroupExample() { @field.Group(field.GroupProps{Class: "w-full max-w-xs"}) { @field.Set() { @field.Label() { Responses } @field.Description() { Get notified when ChatGPT responds to requests that take time, like research or image generation. } @field.Group(field.GroupProps{Attributes: templ.Attributes{"data-slot": "checkbox-group"}}) { @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @checkbox.Checkbox(checkbox.Props{ ID: "push", Checked: true, Disabled: true, }) @field.Label(field.LabelProps{ For: "push", Class: "font-normal", }) { Push notifications } } } } @field.Separator() @field.Set() { @field.Label() { Tasks } @field.Description() { Get notified when tasks you've created have updates. <a href="#">Manage tasks</a> } @field.Group(field.GroupProps{Attributes: templ.Attributes{"data-slot": "checkbox-group"}}) { @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @checkbox.Checkbox(checkbox.Props{ID: "push-tasks"}) @field.Label(field.LabelProps{ For: "push-tasks", Class: "font-normal", }) { Push notifications } } @field.Field(field.Props{Orientation: field.OrientationHorizontal}) { @checkbox.Checkbox(checkbox.Props{ID: "email-tasks"}) @field.Label(field.LabelProps{ For: "email-tasks", Class: "font-normal", }) { Email notifications } } } } }}Responsive Layout
- Vertical fields: Default orientation stacks label, control, and helper text—ideal for mobile-first layouts.
- Horizontal fields: Set
Orientation: field.OrientationHorizontalonfield.Fieldto align the label and control side-by-side. Pair withfield.Contentto keep descriptions aligned. - Responsive fields: Set
Orientation: field.OrientationResponsivefor automatic column layouts inside container-aware parents. Apply@container/field-groupclasses onfield.Groupto switch orientations at specific breakpoints.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/button" "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/input") templ FieldResponsive() { <div class="w-full max-w-lg"> <form> @field.Set() { @field.Legend() { Profile } @field.Description() { Fill in your profile information. } @field.Group() { @field.Field(field.Props{Orientation: field.OrientationResponsive}) { @field.Content() { @field.Label(field.LabelProps{For: "name"}) { Name } @field.Description() { Provide your full name for identification } } @input.Input(input.Props{ ID: "name", Placeholder: "Axel Adrian", }) } @field.Field(field.Props{Orientation: field.OrientationResponsive}) { @button.Button(button.Props{Type: button.TypeSubmit}) { Submit } @button.Button(button.Props{ Type: button.TypeButton, Variant: button.VariantOutline, }) { Cancel } } } } </form> </div>}Validation and Errors
- Add
data-invalidtofield.Fieldto switch the entire block into an error state. - Add
aria-invalidon the input itself for assistive technologies. - Render
field.Errorimmediately after the control or insidefield.Contentto keep error messages aligned with the field.
Accessibility
field.Setandfield.Legendkeep related controls grouped for keyboard and assistive tech users.Fieldoutputsrole="group"so nested controls inherit labeling fromfield.Labelandfield.Legendwhen combined.- Apply
field.Separatorsparingly to ensure screen readers encounter clear section boundaries.
API Reference
FieldSet
Container that renders a semantic fieldset with spacing presets.
FieldLegend
Legend element for a field.Set. Switch to the label variant to align with label sizing.
The field.Legend has two variants: legend and label. The label variant applies label sizing and alignment. Handy if you have nested field.Set.
FieldGroup
Layout wrapper that stacks field.Field components and enables container queries for responsive orientations.
Field
The core wrapper for a single field. Provides orientation control and spacing; mark states with data-invalid / data-disabled attributes.
FieldContent
Flex column that groups control and descriptions when the label sits beside the control. Not required if you have no description.
FieldLabel
Label styled for both direct inputs and nested field.Field children.
FieldTitle
Renders a title with label styling inside field.Content.
FieldDescription
Helper text slot that automatically balances long lines in horizontal layouts.
FieldSeparator
Visual divider to separate sections inside a field.Group. Accepts optional inline content.
FieldError
Accessible error container for validation messages.