Loading...

Infinite UI Demo

Infinite UI is a collection of reusable components for building elegant user interfaces in Go.
It is built with a-h/templ, Alpine.js, Tailwind CSS, Phosphor Icons and the occasional additional JavaScript libraries when necessary.

GitHub Repository GitHub Discussions Reddit Subreddit Quality Gate Status License

@uiForm

uiForm is a collection of components that make it easy to create forms.

.CheckboxInput

A checkbox selects one value, bound to a boolean or an array state path.

Usage

@uiForm.CheckboxInput(uiForm.CheckboxInputSettings{
	Label:           "Accept the terms",
	TwoWayStatePath: "hasAcceptedTerms",

	// OptionalFields
	InputName: "hasAcceptedTerms",
})
Alpine.js Parent State (x-data)
<div x-data="{hasAcceptedTerms: false}"></div>
<div x-data="{selectedFruits: ['apple']}"></div>

Live Example

A bound checkbox and the select-all pattern with an indeterminate parent.

Boolean state

Value:

Indeterminate state

Values:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Shapes

Loading example...

Sizes

Loading example...

Label Position

Loading example...

States

Loading example...

Array and Indeterminate Binding

Loading example...

Error States

Loading example...

Checked Colors

Loading example...

.InlineCheckboxGroup

An inline checkbox group presents several checkboxes under one notched legend, in a row or a column, bound to one array state path.

Usage

@uiForm.InlineCheckboxGroup(uiForm.InlineCheckboxGroupSettings{
	Label: "Fruits",
	InputSettings: []uiForm.CheckboxInputSettings{
		{Label: "Apple", Value: "apple", TwoWayStatePath: "selectedFruits", InputName: "selectedFruits"},
		{Label: "Banana", Value: "banana", TwoWayStatePath: "selectedFruits", InputName: "selectedFruits"},
		{Label: "Orange", Value: "orange", TwoWayStatePath: "selectedFruits", InputName: "selectedFruits"},
	},
})
Alpine.js Parent State (x-data)
<div x-data="{selectedFruits: ['banana']}"></div>

Live Example

A horizontal group bound to one Alpine array. InputName makes every checked value part of the form submission.

Fruits

Values:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Vertical Orientation

Loading example...

Max Visible Options

Loading example...

Sizes and Shapes

Loading example...

Colors

Loading example...

Floating Button Style

Loading example...

Hidden Floating Buttons

Loading example...

.InlineRadioGroup

An inline radio group displays a set of radio options in a horizontal layout.

Usage

@uiForm.InlineRadioGroup(uiForm.InlineRadioGroupSettings{
	Label: "Select an option",
	InputSettings: []uiForm.RadioInputSettings{
		{
			Label:           "Option 1",
			StateValue:      "option1",
			TwoWayStatePath: "groupSelection",
			InputId:         "groupOption1",
			InputName:       "group1",
		},
	},

	// OptionalFields
	TwoWayStatePath: "groupSelection",
})
Alpine.js Parent State (x-data)
<div x-data="{groupSelection: 'option2'}"></div>

Live Example

A horizontal radio group bound to one shared Alpine state path.

Select an option

Value:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Vertical Orientation

Loading example...

Max Visible Options

Loading example...

Sizes

Loading example...

.InputField

An input field is a basic form element that allows users to enter text.

Usage

@uiForm.InputField(uiForm.InputFieldSettings{
	InputType: uiForm.InputTypeText,
	InputName: "name",
	Label:     "Name",

	// OptionalFields
	TwoWayStatePath: "name",
})
Alpine.js Parent State (x-data)
<div x-data="{name: ''}"></div>

Live Example

A labeled text field bound to Alpine state.

Name

Value:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Value & Read-Only

Loading example...

Number Constraints

Loading example...

Affixes

Loading example...

Icon Left

Loading example...

Affix Width

Loading example...

Sizes

Loading example...

Label Case

Loading example...

Hints

Loading example...

Hint Icon Styles

Loading example...

.PasswordInput

A password input adds a reveal toggle, a random password generator, and a strength meter to the standard field. The meter shows a percentage bar plus a criteria checklist that updates as the user types.

Usage

@uiForm.PasswordInput(uiForm.PasswordInputSettings{
	InputName:       "password",
	Label:           "Password",
	TwoWayStatePath: "password",

	// OptionalFields
	ShouldShowGenerateButton: true,
	ShouldShowStrengthMeter:  true,
})
Alpine.js Parent State (x-data)
<div x-data="{password: ''}"></div>

Live Example

Type a password to watch the meter, reveal it with the eye toggle, or generate a strong one with the binary button.

Password
  • Between 6 and 64 characters
  • At least 1 number
  • At least 1 uppercase letter
  • At least 1 lowercase letter
  • At least 1 special character

Value:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Custom Rules

Loading example...

Generate Button Only

Loading example...

Strength Meter Only

Loading example...

Strength Meter Colors

Loading example...

Sizes

Loading example...

Hints

Loading example...

Hint Icon Styles

Loading example...

Action Button Styles

Loading example...

.SearchableSelectInput

A searchable select adds a filter box to a dropdown. The filter narrows the options in place, the list caps its height and scrolls, and a clear button empties the selection. Multi mode binds an array and shows the selected labels; SelectionDisplay tags shows them as removable tags, with optional typed custom values. Use it past roughly ten options, where a native select becomes hard to scan.

Usage

@uiForm.SearchableSelectInput(uiForm.SearchableSelectInputSettings{
	InputName: "country",
	Label:     "Country",

	// OptionalFields
	FlatOptions:     []string{"Argentina", "Australia", "Brazil", "Canada", "Chile", "China", "France", "Germany", "India", "Japan", "Mexico", "Portugal"},
	TwoWayStatePath: "country",
})
Alpine.js Parent State (x-data)
<div x-data="{country: 'Brazil'}"></div>

Live Example

A single select over twelve countries. Open the field and type to narrow the list.

Country
  • No matches

Value:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Multi-Select

Loading example...

Selected as Tags

Loading example...

Tag Colors

Loading example...

Tags Options Only

Loading example...

Tags with LabelValueOptions

Loading example...

LabelValueOptions

Loading example...

No Matches

Loading example...

Dropdown Height

Loading example...

Sizes

Loading example...

Hints

Loading example...

Hint Icon Styles

Loading example...

OnChangeFunc

Loading example...

Disabled Binding

Loading example...

.RemoteSearchableSelectInput

A remote searchable select fetches its options from a URL as the user types. It debounces the requests, waits for the minimum query length, discards stale responses, and shows loading, empty, and error states. The endpoint receives the query in the parameter named by OptionsQueryParam and returns { body: [{ label, value }] }.

Usage

@uiForm.RemoteSearchableSelectInput(uiForm.RemoteSearchableSelectInputSettings{
	InputName:       "country",
	Label:           "Country",
	OptionsUrl:      "/api/countries/search",
	TwoWayStatePath: "country",
})
Alpine.js Parent State (x-data)
<div x-data="{country: ''}"></div>

Live Example

A single select over a demo endpoint that answers after a short delay, so the loading state is visible.

Type at least three characters, for example "bra", and wait for the loading spinner.

Country
  • Loading...
  • Could not load options.
  • No matches

Value:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Multi-Select

Loading example...

Preset Value

Loading example...

Query Settings

Loading example...

Error State

Loading example...

Hints

Loading example...

Hint Icon Styles

Loading example...

Dropdown Height

Loading example...

.RadioInput

A radio input allows users to select one option from a set of options.

Usage

@uiForm.RadioInput(uiForm.RadioInputSettings{
	Label:           "Option 1",
	StateValue:      "option1",
	TwoWayStatePath: "selectedOption",
	InputId:         "radioOption1",
	InputName:       "options",
})
Alpine.js Parent State (x-data)
<div x-data="{selectedOption: 'option2'}"></div>

Live Example

A radio group bound to one shared Alpine state path.

Value:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Sizes

Loading example...

.SelectInput

A select input allows users to select one or more options from a dropdown list.

Usage

@uiForm.SelectInput(uiForm.SelectInputSettings{
	InputName: "country",
	Label:     "Country",

	// OptionalFields
	FlatOptions:              []string{"Argentina", "Brazil", "Chile", "Côte d'Ivoire"},
	TwoWayStatePath:          "country",
	ShouldIncludeBlankOption: true,
})
Alpine.js Parent State (x-data)
<div x-data="{country: 'Brazil'}"></div>

Live Example

A select bound to Alpine state, with a blank option and flat option labels.

Country
Country

Value:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

LabelValueOptions

Loading example...

Multi-Select

Loading example...

Multi-Select LabelValueOptions

Loading example...

HTML Option Labels

Loading example...

Hints

Loading example...

Hint Icon Styles

Loading example...

OnChangeFunc

Loading example...

Sizes

Loading example...

Text Case

Loading example...

Dropdown Background

Loading example...

Dropdown Height

Loading example...

Required

Loading example...

Disabled Binding

Loading example...

.TextArea

A textarea is a form element that allows users to enter multiple lines of text.

Usage

@uiForm.TextArea(uiForm.TextAreaSettings{
	InputName: "description",
	Label:     "Description",

	// OptionalFields
	TwoWayStatePath: "description",
})
Alpine.js Parent State (x-data)
<div x-data="{description: ''}"></div>

Live Example

A labeled textarea bound to Alpine state.

Description

Value:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Value & Read-Only

Loading example...

Code Mode

Loading example...

Hints

Loading example...

Hint State Path

Loading example...

Sizes

Loading example...

Text Case

Loading example...

Required

Loading example...

Hidden Floating Buttons

Loading example...

.ToggleSwitch

A switch toggles a boolean state or adds and removes a custom value from an array.

Usage

@uiForm.ToggleSwitch(uiForm.ToggleSwitchSettings{
	Label:           "Enable notifications",
	TwoWayStatePath: "isNotificationsEnabled",

	// OptionalFields
	InputName: "notificationsEnabled",
})
Alpine.js Parent State (x-data)
<div x-data="{isNotificationsEnabled: false}"></div>
<div x-data="{selectedChannels: ['email']}"></div>

Live Example

The two binding modes: a boolean toggle and channel switches bound to an array.

Boolean state

Value:

Array state

Values:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Sizes

Loading example...

Label Position

Loading example...

Color Variants

Loading example...

Required & Disabled

Loading example...

Custom Values

Loading example...

@uiDisplay

uiDisplay is a collection of components for displaying content.

.Accordion

An accordion is a vertically stacked list of items that can be expanded or collapsed to reveal content.

Usage

@uiDisplay.Accordion(uiDisplay.AccordionSettings{
	Items: []uiDisplay.AccordionItemSettings{
		{Title: "Section 1", Content: AccordionDemoSection1()},
		{Title: "Section 2", Content: AccordionDemoSection2()},
		{Title: "Section 3", Content: AccordionDemoSection3()},
	},
})

Live Example

Three titled sections, each holding its own content.

Section 1

This is the content for section 1. You can put any content here.

Section 2

This is the content for section 2. You can put any content here.

Section 3

This is the content for section 3. You can put any content here.

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Item Icons

Loading example...

Surface & Padding

Loading example...

Single Open

Loading example...

First Item Open

Loading example...

Text Case

Loading example...

.Alert

An alert is a notification component that displays important information to users.

Usage

@uiDisplay.Alert(uiDisplay.AlertSettings{
	Title:       "Alert Title",
	Description: "This is an alert message.",
	Variation:   uiDisplay.AlertVariationInfo,
	Size:        uiDisplay.AlertSizeMd,
	IsCloseable: true,
})

Live Example

The four tone variations: success, warning, error, and info.

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Sizes

Loading example...

Content Html Slot

Loading example...

Icons

Loading example...

Close & Timeout

Loading example...

Custom Surface

Loading example...

State-Bound Content

Loading example...

.CloakLoading (pre-Alpine/HTMX)

A cloak loading screen that appears only before the JavaScript libraries are loaded to prevent FOUC (Flash of Unstyled Content).

Usage

@uiDisplay.CloakLoading(uiDisplay.CloakLoadingSettings{})
Set colors as rgb(a), hex, or hsl(a) values: they are used in the inline style.

Live Example

Since the demo is already loaded with Alpine.js, the CloakLoading component will behave like a regular loading overlay. In a real application, this would be the first thing that appears when the page starts loading.

The page content will be covered by the cloak loading screen when triggered.

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Custom Text

Loading example...

Icon & Animation

Loading example...

Hide Delay

Loading example...

.ConfirmationDialog

Confirmation dialog presets: confirm, warning, critical, and delete. Each preset supplies its icon, tone, and confirmation copy. Delete and critical gate the confirm action behind typing the target name.

Usage

@uiDisplay.DeleteDialog(uiDisplay.ConfirmationDialogSettings{
	IsVisibleTwoWayStatePath: "isDeleteDialogVisible",
	OnConfirmFunc:            "deleteRecord()",

	// OptionalFields
	TargetNameStatePath: "demoTargetName",
	TargetIdStatePath:   "demoTargetId",
	WidthPercent:        32,
	MinHeightPercent:    42,
})
Alpine.js Parent State (x-data)
<div x-data="{
  isDeleteDialogVisible: false,
  demoTargetName: 'primary-database',
  demoTargetId: '42',
}"></div>

Live Example

The four presets against one target. Delete and critical require typing the target name.

Target: (id: ) · Confirmed: nothing yet

Presets

Confirm Action

Are you sure you want to proceed with # ?

This action needs your confirmation.

Warning

Are you sure you want to proceed with # ?

Review the effects before you continue.

Critical Action

Are you sure you want to proceed with # ?

This action cannot be undone.

Type the name to confirm

Delete

Are you sure you want to delete # ?

This process cannot be undone. This will permanently delete the element(s).

Type the name to confirm

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Type-to-Confirm

Loading example...

Uncloseable

Loading example...

Button Labels & Icons

Loading example...

Custom Message & Header

Loading example...

Icon On Left

Loading example...

Header Text Styling

Loading example...

Sizing Bounds

Loading example...

Header Actions

Loading example...

.LoadingOverlay (htmx-ready)

A loading overlay displays a loading indicator over content to show that an operation is in progress.

Usage

@uiDisplay.LoadingOverlay(uiDisplay.LoadingOverlaySettings{
	IsLoadingOneWayStatePath: "isLoading",
})
Alpine.js Parent State (x-data)
<div x-data="{isLoading: false}"></div>

Live Example

An overlay driven by one boolean state path. Trigger it to cover the content below.

The page content will be covered by the loading overlay when triggered.

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Icon & Animation

Loading example...

Background Color

Loading example...

.Tag

A tag is a small label that can be used to categorize or identify content.

Usage

@uiDisplay.Tag(uiDisplay.TagSettings{
	OuterLeftIcon:  "ph-info",
	OuterLeftLabel: "Info",
	InnerLabel:     "Warning",
})

Live Example

The common shapes: an outer label with an inner label, and an outer label with a bound inner value.

Info
Warning
count

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Sizes

Loading example...

Radii

Loading example...

Outer Ring Colors

Loading example...

Outer Background Colors

Loading example...

Inner Colors

Loading example...

Side Element Actions

Loading example...

Removable Tags

Loading example...

Text Case

Loading example...

.Toast

A toast is a temporary notification displayed to provide feedback to users.

Usage

@uiDisplay.Toast(uiDisplay.ToastSettings{
	// OptionalFields
	AutoDismissSeconds: 6,
	TextColor:          "neutral-50",
	Size:               uiDisplay.ToastSizeMd,
	RingThickness:      uiToolset.RingThicknessMd,
	Radius:             uiToolset.BorderRadiusMd,
})
@uiControl.Button(uiControl.ButtonSettings{
	OnClickFunc: "$store.toast.displayToast('Saved!', 'success')",
})

Live Example

One toast mount serves every message. Trigger the success, warning, and error tones below; each tone colors the ring.

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

API Response Toasts

Loading example...

Mount Settings

Loading example...

@uiControl

uiControl is a collection of components for controlling content.

.Button

A button is a clickable element that can be used to trigger an action.

Usage

@uiControl.Button(uiControl.ButtonSettings{
	Label:       "Click me",
	IconLeft:    "ph-info",
	OnClickFunc: "alert('Button clicked!')",
})

Live Example

The action buttons an app reaches for: a primary action, a quiet secondary, and a destructive one.

New Record clicks:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Sizes

Loading example...

Shapes

Loading example...

Icons

Loading example...

Colors

Loading example...

Rings

Loading example...

Tooltips

Loading example...

Disabled Binding

Loading example...

Full-Width & Submit

Loading example...

Text Case

Loading example...

.RangeSlider

A range slider allows users to select a value from a range by dragging a thumb along a track.

Usage

@uiControl.RangeSlider(uiControl.RangeSliderSettings{
	ThumbValueTwoWayStatePath: "sliderValue",

	// OptionalFields
	TrackStartValue: "0",
	TrackEndValue:   "100",
})
Alpine.js Parent State (x-data)
<div x-data="{sliderValue: 50}"></div>

Live Example

A slider bound to one numeric Alpine state path. Drag the thumb or use the arrow keys.

Value:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Sizes

Loading example...

Thumb Shapes

Loading example...

Thumb Colors & Icons

Loading example...

Value Bubble

Loading example...

Track Labels

Loading example...

Track Colors & Icons

Loading example...

Steps & Ticks

Loading example...

Track Tooltip

Loading example...

Dual-Thumb Mode

Loading example...

Static Thumb Values

Loading example...

Initial State Normalization

Loading example...

@uiStructural

uiStructural is a collection of components for navigating and displaying structured data.

.Card

A card is a surface container with an optional page-style heading (header title, icon, sub-heading) and customizable edges, shadow, ring, and padding.

Usage

@uiStructural.Card(uiStructural.CardSettings{
	MiddleContent: CardDemoBody(),

	// OptionalFields
	HeaderTitle:      "Card Title",
	HeaderSubHeading: "Card sub-heading",
	HeaderIcon:       "ph-cube",
})

Live Example

The complete card: header with icon and sub-heading, a body, and a footer with actions.

Default Card

Rounded edges and an icon heading

Cards group related content on a surface. This body is the MiddleContent, so the card only styles the container and the optional heading.

Card footer content sits below the body.

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Surface & Edges

Loading example...

Header Icon Styles

Loading example...

Content Gap

Loading example...

Content-Only Card

Loading example...

State-Bound Header

Loading example...

Custom Header Slot

Loading example...

Header Styling

Loading example...

.DataTable

A data table renders rows from column definitions with sorting, selection, filters, search, and pagination. Every change refreshes the table from the server through the URL template. The examples below cover the full settings surface.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns:          columns,
	Rows:             records,
	QueryUrlTemplate: "/records?page={pageNumber}&sort={sortKey}&direction={sortDirection}&search={search}",

	// OptionalFields
	Filters:              filters,
	RowIdResolver:        func(record Record) string { return record.Id },
	RefreshOnEvents:      []string{"refresh:records-table"},
	RefreshDebounceMs:    300,
	MaxVisibleRows:       4,
	InitialSortKey:       "name",
	InitialSortDirection: uiStructural.DataTableSortDirectionAsc,
})

Live Example

The complete server-driven table: filters, search, sorting, selection, bulk and header actions, and pagination.

Name
Status
Status
CPU min
CPU max
Name
Status
CPU
Search

Status
alpharunning22026-09-01
bravorunning42026-09-02
charliestopped12026-09-03
deltarunning82026-09-04
echorunning22026-09-05

Advanced Examples

The panels below hold static tables. They send no requests, so you can inspect the layout and the configuration without a server. Open a panel to see the settings and the rendered result.

Column Configuration
Column Configuration

A column sets its alignment, width, and sortability. WidthPercent shares the row width, MaxWidthClass caps a long cell, and MinWidthClass holds a floor. A column with a SortKey renders a sort button; InitialSortKey and InitialSortDirection seed the active sort indicator.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns: []uiStructural.DataTableColumnSettings[Record]{
		{Label: "Name", SortKey: "name", WidthPercent: 20, CellRenderer: nameCell},
		{Label: "Description", MaxWidthClass: "max-w-72 truncate", CellRenderer: descriptionCell},
		{Label: "Status", Alignment: uiStructural.TextAlignmentCenter, WidthPercent: 15, CellRenderer: statusCell},
		{Label: "CPU cores", SortKey: "cpuCores", Alignment: uiStructural.TextAlignmentRight, WidthPercent: 12, CellRenderer: cpuCell},
		{Label: "Created at", MinWidthClass: "min-w-28", CellRenderer: createdAtCell},
	},
	Rows: records,

	// OptionalFields
	InitialSortKey:       "name",
	InitialSortDirection: uiStructural.DataTableSortDirectionAsc,
})

Description Status Created at
alphaHandles the public API traffic for the primary region and drains connections during rolling deploys.running22026-09-01
bravoRuns the nightly batch jobs, the reporting pipeline, and the weekly archive export.running42026-09-02
charlieHosts the internal dashboard, the metrics collector, and the alerting rules engine.stopped12026-09-03
deltaServes the static assets, the image resizing service, and the signed download links.running82026-09-04
Rows Configuration
Rows Configuration

RowIdResolver adds the selection column and identifies each row. RowLabelResolver names the row checkbox for screen readers. CheckboxShape, CheckboxSize, CheckboxCheckedColor, and CheckboxUncheckedColor change the selection checkbox. Select a row to see the colors change.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns:              columns,
	Rows:                 records,
	RowIdResolver:        func(record Record) string { return record.Id },
	RowLabelResolver:     func(record Record) string { return record.Name },
	CheckboxShape:        uiToolset.ShapeRounded,
	CheckboxSize:         uiForm.CheckboxInputSizeSm,
	CheckboxCheckedColor: "emerald-500",
	CheckboxUncheckedColor: "sky-500",
})

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8
Filters
Filters

Each filter renders an editor and an active chip. InitialFilterValues seeds the starting values. FilterDropdownBackgroundColor paints the dropdown panel. A filter change updates the chips and the filter values.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns:                       columns,
	Rows:                          records,
	Filters:                       filters,
	FilterDropdownBackgroundColor: "neutral-800",
	InitialFilterValues:           map[string]any{"status": "running"},
})
Name
Status
Status
CPU min
CPU max
Name
Status
CPU

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8
echorunning2
Chip Styling
Chip Styling

FilterChipStyle forwards to the shared FilterBar, so the active-filter chip matches the page palette. Every field falls back to the neutral default, and TextCase defaults to lowercase.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns: columns,
	Rows:    records,
	Filters: filters,
	FilterChipStyle: uiStructural.FilterChipStyle{
		TextCase:             uiToolset.TextCaseUpper,
		OuterBackgroundColor: "secondary-500/20",
		OuterRingColor:       "secondary-500/40",
		OuterTextColor:       "neutral-50",
		InnerBackgroundColor: "secondary-500/40",
		InnerTextColor:       "neutral-50",
	},
	InitialFilterValues: map[string]any{"status": "running"},
})
Name
Status
Status
CPU min
CPU max
Name
Status
CPU

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8
echorunning2
Search Box
Search Box

The table renders a default search box when the query template carries the search placeholder. The box stretches to the available space by default; IsSearchBoxCompact fixes it to a narrow width so SearchBoxAlignment can place it left, center, or right. InitialSearchQuery seeds the starting text. Pass SearchBox to replace the default box with any component that binds to searchQuery.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns:          columns,
	Rows:             records,
	QueryUrlTemplate: "/records?search={search}",

	// OptionalFields
	InitialSearchQuery: "alpha",
	IsSearchBoxCompact: true,
	SearchBoxAlignment: uiStructural.HorizontalAlignmentCenter,

	// Or replace the default box
	SearchBox: CustomSearchBox(),
})

Default box, compact and centered

Custom SearchBox, full width

Custom search

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8
echorunning2
Header Actions
Header Actions

HeaderActions renders in the toolbar. The button here dispatches an event the panel counts.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns: columns,
	Rows:    records,
	HeaderActions: uiControl.Button(uiControl.ButtonSettings{
		Label:       "Sync",
		IconLeft:    "ph-arrows-clockwise",
		OnClickFunc: "$dispatch('demo:header-action')",
		Size:        uiControl.ButtonSizeSm,
	}),
})

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8

Header action clicks:

Bulk Actions
Bulk Actions

BulkActions renders in the toolbar only while at least one row is selected. Select a row to reveal the action.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns: columns,
	Rows:    records,
	RowIdResolver: func(record Record) string { return record.Id },
	BulkActions: uiControl.Button(uiControl.ButtonSettings{
		Label:           "Archive",
		IconLeft:        "ph-archive",
		OnClickFunc:     "selectedRowIds = []",
		Size:            uiControl.ButtonSizeSm,
		BackgroundColor: "red-500/20",
		TextColor:       "red-200",
	}),
})

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8
Row and Column Styling
Row and Column Styling

StickyHeaderBackgroundColor paints the pinned header row. ItemsPerPageDropdownBackgroundColor paints the items-per-page menu. IsStriped adds a zebra stripe. CellClass styles one column, and RowClassResolver styles each row from its data. A cell component that sets its own color overrides the row color.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns: []uiStructural.DataTableColumnSettings[Record]{
		{Label: "Name", CellClass: "font-bold", CellRenderer: nameCell},
		{Label: "Status", CellRenderer: statusCell},
		{Label: "CPU cores", Alignment: uiStructural.TextAlignmentRight, CellRenderer: cpuCell},
	},
	Rows: records,

	// OptionalFields
	StickyHeaderBackgroundColor:         "neutral-50/5",
	ItemsPerPageDropdownBackgroundColor: "emerald-900",
	IsStriped:                           true,
	RowClassResolver: func(record Record) string {
		if record.Status != "running" {
			return "text-neutral-400"
		}
		return ""
	},
})

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8
Density and Header Text Case
Density and Header Text Case

Density changes the cell padding. TextCase changes the header capitalization. Both tables use the same columns and rows. The last column holds an icon button with a tooltip.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns:  columns,
	Rows:     records,
	Density:  uiStructural.DataTableDensityDense,
	TextCase: uiToolset.TextCaseUpper,
})

Comfortable (default), headers as typed

Name Status CPU cores Actions
alpharunning2
bravorunning4
charliestopped1

Dense with uppercase headers

Name Status CPU cores Actions
alpharunning2
bravorunning4
charliestopped1
Static Header
Static Header

The header pins to the top of the scroll viewport by default; IsHeaderStatic lets it scroll away with the body. MinWidthClass and MaxWidthClass size the table surface. MinHeightClass and MaxHeightClass size the scroll viewport. MaxVisibleRows measures the header and the tallest row and caps the viewport to that row count instead; the live table above uses it.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns:        columns,
	Rows:           records,
	IsHeaderStatic: true,
	MinWidthClass:  "min-w-96",
	MaxWidthClass:  "max-w-2xl",
	MinHeightClass: "min-h-24",
	MaxHeightClass: "max-h-40",
})

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8
echorunning2
foxtrotstopped16
golfrunning2
hotelrunning4
indiastopped1
juliettrunning8
kilorunning2
limastopped16
mikerunning2
novemberrunning4
oscarstopped1
paparunning8
quebecrunning2
romeostopped16
sierrarunning2
tangorunning4
uniformstopped1
victorrunning8
whiskeyrunning2
xraystopped16
yankeerunning2
Pagination Display
Pagination Display

Page numbers are zero-based by default; ShouldUseOneBasedPageDisplay shifts the labels to start at 1. IsPaginationHiddenWhenSinglePage hides the page controls when a single page holds every row. PaginationAriaLabel names the footer navigation for screen readers.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns: columns,
	Rows:    records,

	// OptionalFields
	ShouldUseOneBasedPageDisplay:   true,
	IsPaginationHiddenWhenSinglePage: true,
	PaginationAriaLabel:            "Servers",
})

One-based labels

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8

Hidden on a single page

Name Status CPU cores
alpharunning2
bravorunning4
charliestopped1
deltarunning8
Empty States
Empty States

With no rows the table renders the EmptyState slot. Without the slot it renders the default message.

Usage

@uiStructural.DataTable(uiStructural.DataTableSettings[Record]{
	Columns:    columns,
	Rows:       nil,
	EmptyState: DataTableDemoEmptyState(),
})

Custom EmptyState

Name Status CPU cores

No servers yet

Create a server to see it listed here.

Default empty state

Name Status CPU cores
No records found.

.FilterBar

A filter bar renders one editor per declared filter and shows the active filters as removable chips.

Usage

@uiStructural.FilterBar(uiStructural.FilterBarSettings{
	Filters: []uiStructural.FilterSettings{
		{Key: "name", Label: "Name", Kind: uiStructural.FilterKindTextContains},
		{Key: "status", Label: "Status", Kind: uiStructural.FilterKindEnumSelect, Options: statusOptions},
		{Key: "cpu", Label: "CPU", Kind: uiStructural.FilterKindNumberRange},
	},
	ValuesTwoWayStatePath: "filterValues",

	// OptionalFields
	OnChangeFunc: "changeCount = changeCount + 1",
})

Live Example

All three filter kinds at once: text contains, enum select, and number range, with removable chips.

Name
Status
Status
CPU min
CPU max
Name
Status
CPU

Values: · OnChangeFunc calls:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Query Param Names

Loading example...

Enum Radio Group Prefix

Loading example...

Dropdown Background

Loading example...

Multi-Value Filter

Loading example...

Date Range Filter

Loading example...

Change Callback

Loading example...

Chip Styling

Loading example...

.Pagination

Pagination splits records into pages and lets the user move between them.

Usage

@uiStructural.Pagination(uiStructural.PaginationSettings{
	PageNumberTwoWayStatePath:   "pageNumber",
	ItemsPerPageTwoWayStatePath: "itemsPerPage",
	ItemsTotal:                  240,

	// OptionalFields
	PagesTotal:   24,
	OnChangeFunc: "changeCount = changeCount + 1",
})

Live Example

Page navigation and a page-size select bound to Alpine state.

pageNumber: · itemsPerPage: · OnChangeFunc calls:

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Page-Size Choices

Loading example...

Disabled Binding

Loading example...

Hidden On Single Page

Loading example...

One-Based Labels

Loading example...

Change Callback

Loading example...

.Tabs

Tabs split content into a header row and matching panels. Each tab carries a label, a value, and an optional icon and badge count. The selected value binds to an Alpine state path, and the panels switch with x-show.

Usage

@uiStructural.Tabs(uiStructural.TabSettings{
    Items: []uiStructural.TabItemSettings{
        {Label: "General", Value: "general", Icon: "ph-gear", Content: TabsDemoGeneralPanel()},
        {Label: "Security", Value: "security", Icon: "ph-shield-check", BadgeCount: 3, Content: TabsDemoSecurityPanel()},
        {Label: "Advanced", Value: "advanced", Icon: "ph-sliders", Content: TabsDemoAdvancedPanel()},
    },
    SelectedValueTwoWayStatePath: "selectedTab",
})
Alpine.js Parent State (x-data)
<div x-data="{
  selectedTab: 'general',
}"></div>

Live Example

Horizontal tabs with icons and a badge. Click a tab, or use the arrow keys, Home, and End.

General

Workspace name, language, and timezone live here. The panel stays in the DOM so its inputs keep their state while you switch tabs.

Security

Two-factor authentication and session controls. This tab carries a badge count.

Advanced

Danger zone and developer options. Arrow keys, Home, and End move between the tabs.

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Vertical

Loading example...

Right Side

Loading example...

Vertical Alignment

Loading example...

Icon Position

Loading example...

Border Radius

Loading example...

Surface Customization

Loading example...

Content Surface

Loading example...

Badge Counts

Loading example...

Deep Linking

Loading example...

Overflow Scroll

Loading example...

Vertical Overflow Scroll

Loading example...

.PageHeading

The page heading block for both page titles and section headings: header title, optional icon and description, and a right-aligned action slot. One component with a Level setting so pages stop hand-rolling headers.

Usage

@uiStructural.PageHeading(uiStructural.PageHeadingSettings{
	HeaderTitle: "Records",
	Level:       uiStructural.PageHeadingLevelPage,

	// OptionalFields
	Description:    "Manage the server records",
	HeaderIcon:     "ph-table",
	ActionsContent: PageHeadingDemoActions(),
})

Live Example

The complete page-level heading: title, icon, description, and actions.

Records

Manage the server records and their lifecycle.

Advanced Examples

Open a panel to see one variation with its settings and the rendered result.

Title Only

Loading example...

Levels & Sizes

Loading example...

Icon Styles

Loading example...

State-Bound Title

Loading example...

Text Case

Loading example...

Description and Actions

Loading example...