Skip to content
Gx
GitHub

Date Picker

A button that opens a calendar in a popover.

<datepicker.DatePicker id="date-empty" name="day" />

Installation

Run the command in the app module.

Terminal
gx add date-picker

The command also installs calendar and popover.

The command writes these files.

File Path in the app
DatePicker.gx ui/date-picker/DatePicker.gx
DatePicker.fixtures.go ui/date-picker/DatePicker.fixtures.go
styles.go ui/date-picker/styles.go

Install calendar and popover first.

Copy each file to its path in the app. Change each import of a registry package to the path of that package in the app.

ui/date-picker/DatePicker.gx
package datepicker

import "github.com/alternayte/gx/registry/calendar"
import "github.com/alternayte/gx/registry/popover"

props {
  // Id is the id of the date input. A label points at it.
  Id          string
  // Name is the form field name. The form sends the day as YYYY-MM-DD.
  Name        string = ""
  // Value is the chosen day of the first render, as YYYY-MM-DD.
  Value       string = ""
  // Min is the first day that a user can choose, as YYYY-MM-DD.
  Min         string = ""
  // Max is the last day that a user can choose, as YYYY-MM-DD.
  Max         string = ""
  // Placeholder is the text of the trigger with no chosen day.
  Placeholder string = "Pick a date"
  // Label is the accessible name of the date input.
  Label       string = "Date"
  // WeekStart is the first day of the week: calendar.Sunday or calendar.Monday.
  WeekStart   calendar.WeekStart = calendar.Sunday
  // Locale is the language of the month and day names, as a BCP 47 tag. Empty means the language of the page.
  Locale      string = ""
  // Class adds classes to the root element.
  Class       string = ""
  // Attrs adds HTML attributes to the date input.
  Attrs       gx.Attrs = nil
}

<div data-slot="date-picker" class={gx.Cx("inline-block", p.Class)}>
  <popover.PopoverTrigger id={p.popoverID()} class={triggerClass}>
    <span id={p.displayID()} data-empty={p.empty()} class={displayClass}>{p.text()}</span>
  </popover.PopoverTrigger>
  <popover.Popover id={p.popoverID()} align={popover.Start} class="w-auto p-0">
    <calendar.Calendar id={p.Id} name={p.Name} value={p.Value} min={p.Min} max={p.Max} label={p.Label} weekStart={p.WeekStart} locale={p.Locale} display={p.displayID()} popover={p.popoverID()} class="border-0" attrs={p.Attrs} />
  </popover.Popover>
</div>
ui/date-picker/DatePicker.fixtures.go
package datepicker

import (
	"github.com/alternayte/gx"
	"github.com/alternayte/gx/registry/calendar"
)

var DatePickerFixtures = gx.Fixtures[DatePickerProps]{
	"Empty":   {Id: "date-empty", Name: "day"},
	"Chosen":  {Id: "date-chosen", Name: "day", Value: "2026-10-14"},
	"Limited": {Id: "date-limited", Name: "day", Value: "2026-10-14", Min: "2026-10-10", Max: "2026-10-20", WeekStart: calendar.Monday},
}
ui/date-picker/styles.go
// Package datepicker is a button that opens a calendar in a popover.
package datepicker

// triggerClass is the classes of the trigger button: as wide as a date and
// with its text at the start.
const triggerClass = "w-64 justify-start font-normal"

// displayClass is the classes of the text of the trigger. The placeholder
// is muted.
const displayClass = "truncate data-[empty=true]:text-muted-foreground"

// popoverID returns the id of the popover.
func (p DatePickerProps) popoverID() string { return p.Id + "-popover" }

// displayID returns the id of the element that shows the chosen day.
func (p DatePickerProps) displayID() string { return p.Id + "-display" }

// empty reports, as an attribute value, whether no day is chosen.
func (p DatePickerProps) empty() string {
	if p.Value == "" {
		return "true"
	}
	return "false"
}

// text returns the text of the trigger for the first render: the day as
// the server has it, or the placeholder. The calendar island writes the day
// in the language of the user.
func (p DatePickerProps) text() string {
	if p.Value != "" {
		return p.Value
	}
	// A props value that Go code makes has no default.
	if p.Placeholder == "" {
		return "Pick a date"
	}
	return p.Placeholder
}

The theme must define these tokens: --muted-foreground.

Usage

<label for="due">Due date</label>
<datepicker.DatePicker id="due" name="due" value={p.Due} />
<datepicker.DatePicker id="start" name="start" min="2026-10-10" max="2026-10-20" placeholder="Start date" />

The component is a popover with a calendar. The calendar holds a native <input type="date">, so a form sends the day as YYYY-MM-DD. The button shows the chosen day in the language of the user. A choice closes the popover. With no script the button opens the popover, and the popover shows the date input of the browser.

Examples

Empty

<datepicker.DatePicker id="date-empty" name="day" />

Chosen

<datepicker.DatePicker id="date-chosen" name="day" value="2026-10-14" />

Limited

<datepicker.DatePicker id="date-limited" name="day" value="2026-10-14" min="2026-10-10" max="2026-10-20" weekStart={calendar.Monday} />

API reference

A tag sets a prop by its name with a lower-case first letter: Class is class.

datepicker.DatePicker

Prop Type Default Description
Id string Required Id is the id of the date input. A label points at it.
Name string "" Name is the form field name. The form sends the day as YYYY-MM-DD.
Value string "" Value is the chosen day of the first render, as YYYY-MM-DD.
Min string "" Min is the first day that a user can choose, as YYYY-MM-DD.
Max string "" Max is the last day that a user can choose, as YYYY-MM-DD.
Placeholder string "Pick a date" Placeholder is the text of the trigger with no chosen day.
Label string "Date" Label is the accessible name of the date input.
WeekStart calendar.WeekStart calendar.Sunday WeekStart is the first day of the week: calendar.Sunday or calendar.Monday.
Locale string "" Locale is the language of the month and day names, as a BCP 47 tag. Empty means the language of the page.
Class string "" Class adds classes to the root element.
Attrs gx.Attrs nil Attrs adds HTML attributes to the date input.

Do and do not

Do

  • Give the date picker a <label> for its Id.
  • Set Min and Max for a range of valid days. The server checks the value too.
  • Use a Placeholder that names the date, for example "Start date".

Don't

  • Do not use a date picker for a date of birth. Use three inputs or one text input.
  • Do not put a date picker in a popover.

Keyboard

Key Action
Tab Moves focus to the button.
Enter, Space Opens the calendar from the button, or chooses the day that has the focus.
Arrow keys Moves one day or one week in the calendar.
Page Up, Page Down Moves one month back or forward.
Escape Closes the calendar and moves focus to the button.