T templcn/ui
Componentradio-group

Radio Group

A set of checkable buttons—known as radio buttons—where no more than one button can be checked at a time.

Preview

A set of checkable buttons—known as radio buttons—where no more than one button can be checked at a time.

Installation

Add this component to your project using the CLI.

templcn add radio-group

Usage

1. Import the component in your Go or templ file:

import "your-module/ui"

2. Use the component in your template:

@ui.RadioGroup(ui.RadioGroupProps{Name: "option", DefaultValue: "option-one"}) {
  <div class="flex items-center space-x-2">
    @ui.RadioGroupItem(ui.RadioGroupItemProps{ID: "r1", Name: "option", Value: "option-one", Label: "Option One"})
    @ui.Label(ui.LabelProps{For: "r1"}) { Option One }
  </div>
  <div class="flex items-center space-x-2">
    @ui.RadioGroupItem(ui.RadioGroupItemProps{ID: "r2", Name: "option", Value: "option-two", Label: "Option Two"})
    @ui.Label(ui.LabelProps{For: "r2"}) { Option Two }
  </div>
}

Composition

Use these parts together to build the component.

RadioGroup
├── RadioGroupItem
├── Label

Examples

Named examples and common variations.

Three options

A radio group for plan selection.

API Reference
PropTypeDefaultDescription
Namestring""HTML name shared by all radio inputs.
DefaultValuestring""Pre-selected option value.
DisabledboolfalseDisables all options.
View source
package ui

import (
	"context"
	"io"

	"github.com/a-h/templ"
)

type RadioGroupProps struct {
	DOMProps
	Name         string
	Value        string
	DefaultValue string
	Disabled     bool
	Required     bool
}

type radioGroupRenderState struct {
	name     string
	value    string
	disabled bool
}

type radioGroupRenderStateKey struct{}

func RadioGroup(props RadioGroupProps) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
		value := props.Value
		if value == "" {
			value = props.DefaultValue
		}
		attrs := attrsFromDOMProps(props.DOMProps, "radio-group", "grid gap-3")
		attrs["role"] = "radiogroup"
		if props.Name != "" {
			attrs["data-name"] = props.Name
		}
		if value != "" {
			attrs["data-value"] = value
		}
		if props.DefaultValue != "" {
			attrs["data-default-value"] = props.DefaultValue
		}
		if props.Disabled {
			attrs["data-disabled"] = "true"
		}
		if props.Required {
			attrs["aria-required"] = "true"
		}
		ctx = context.WithValue(ctx, radioGroupRenderStateKey{}, radioGroupRenderState{name: props.Name, value: value, disabled: props.Disabled})
		return renderElement(ctx, w, "div", attrs, templ.GetChildren(ctx))
	})
}

type RadioGroupItemProps struct {
	DOMProps
	Name     string
	Value    string
	Disabled bool
	Label    string
}

func RadioGroupItem(props RadioGroupItemProps) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
		ctx, ownChildren := childrenFromContext(ctx)
		className := "flex items-center gap-2 text-sm"
		attrs := attrsFromDOMProps(props.DOMProps, "radio-group-item", className)
		state, _ := ctx.Value(radioGroupRenderStateKey{}).(radioGroupRenderState)
		name := props.Name
		if name == "" {
			name = state.name
		}
		disabled := props.Disabled || state.disabled
		checked := props.Value != "" && state.value == props.Value
		attrs["data-state"] = map[bool]string{true: "checked", false: "unchecked"}[checked]
		attrs["aria-checked"] = map[bool]string{true: "true", false: "false"}[checked]
		if disabled {
			attrs["data-disabled"] = "true"
		}
		children := templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
			inputAttrs := templ.Attributes{
				"type":  "radio",
				"class": "aspect-square size-4 shrink-0 rounded-full border border-input text-primary shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
				"value": props.Value,
			}
			if name != "" {
				inputAttrs["name"] = name
			}
			if checked {
				inputAttrs["checked"] = true
			}
			if disabled {
				inputAttrs["disabled"] = true
			}
			if err := renderVoidElement(ctx, w, "input", inputAttrs); err != nil {
				return err
			}
			if props.Label != "" {
				_, err := io.WriteString(w, templ.EscapeString(props.Label))
				return err
			}
			return renderChildren(ctx, w, ownChildren)
		})
		return renderElement(ctx, w, "label", attrs, children)
	})
}