T templcn/ui
Componentcollapsible

Collapsible

An interactive component which expands/collapses a panel.

Preview

An interactive component which expands/collapses a panel.

Installation

Add this component to your project using the CLI.

templcn add collapsible

Usage

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

import "your-module/ui"

2. Use the component in your template:

@ui.Collapsible(ui.CollapsibleProps{DOMProps: ui.DOMProps{Class: "w-[350px] space-y-2"}}) {
  <div class="flex items-center justify-between space-x-4 px-4">
    <h4 class="text-sm font-semibold">@peduarte starred 3 repositories</h4>
    @ui.CollapsibleTrigger(ui.DOMProps{}) {
      @ui.Button(ui.ButtonProps{Variant: ui.ButtonVariantGhost, Size: ui.ButtonSizeSM}) { Toggle }
    }
  </div>
  @ui.CollapsibleContent(ui.DOMProps{Class: "space-y-2"}) {
    <div class="rounded-md border px-4 py-2 font-mono text-sm shadow-sm">
      @radix-ui/primitives
    </div>
  }
}

Composition

Use these parts together to build the component.

Collapsible
├── CollapsibleTrigger
├── Button
├── CollapsibleContent

Examples

Named examples and common variations.

Default

A collapsible panel with trigger and content.

API Reference
PropTypeDefaultDescription
OpenboolfalseControls the open state.
DefaultOpenboolfalseDefault open state (uncontrolled).
DisabledboolfalseDisables toggling.
View source
package ui

import (
	"context"
	"io"

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

type CollapsibleProps struct {
	DOMProps
	Open        bool
	DefaultOpen bool
	Disabled    bool
}

type collapsibleRenderState struct {
	open bool
}

type collapsibleRenderStateKey struct{}

func Collapsible(props CollapsibleProps) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
		open := props.Open || props.DefaultOpen
		attrs := attrsFromDOMProps(props.DOMProps, "collapsible", "")
		attrs["data-state"] = openState(open)
		if open {
			attrs["data-open"] = "true"
			attrs["open"] = true
		}
		if props.DefaultOpen {
			attrs["data-default-open"] = "true"
		}
		if props.Disabled {
			attrs["data-disabled"] = "true"
		}
		ctx = context.WithValue(ctx, collapsibleRenderStateKey{}, collapsibleRenderState{open: open})
		return renderElement(ctx, w, "details", attrs, templ.GetChildren(ctx))
	})
}

func CollapsibleTrigger(props DOMProps) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
		attrs := attrsFromDOMProps(props, "collapsible-trigger", "")
		if state, ok := ctx.Value(collapsibleRenderStateKey{}).(collapsibleRenderState); ok {
			attrs["aria-expanded"] = map[bool]string{true: "true", false: "false"}[state.open]
		}
		return renderElement(ctx, w, "summary", attrs, templ.GetChildren(ctx))
	})
}

func CollapsibleContent(props DOMProps) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
		attrs := attrsFromDOMProps(props, "collapsible-content", "")
		open := false
		if state, ok := ctx.Value(collapsibleRenderStateKey{}).(collapsibleRenderState); ok {
			open = state.open
		}
		if _, ok := attrs["data-state"]; !ok {
			attrs["data-state"] = openState(open)
		}
		if !open {
			attrs["hidden"] = true
		}
		return renderElement(ctx, w, "div", attrs, templ.GetChildren(ctx))
	})
}