Skip to content

Select

The Select component creates dropdown selection menus with full Bootstrap styling, validation support, and HTMX integration.

Quick Start

Select("country", ("us", "United States"), ("uk", "United Kingdom"), ("ca", "Canada"), ("au", "Australia"), label="Country")

Usage Scenarios

Sizes

Select("priority", ("high", "High"), ("medium", "Medium"), label="Priority Level", size="lg")
Select("category", ("tech", "Technology"), ("design", "Design"), label="Category")
Select("status", ("active", "Active"), ("inactive", "Inactive"), label="Status", size="sm")

Multiple Selection

Select("skills", ("python", "Python"), ("javascript", "JavaScript"), ("html", "HTML/CSS"), ("sql", "SQL"), ("docker", "Docker"), label="Skills", help_text="Hold Ctrl/Cmd to select multiple", multiple=True)

Pre-selected Options

Select("theme", ("light", "Light Mode"), ("dark", "Dark Mode", True), ("auto", "Auto (System)"), label="Theme Preference")

Validation States

Select("plan", ("premium", "Premium Plan", True), label="Valid Selection", cls="is-valid")

Parameter Reference

Parameter Type Default Description
name str Required Form field name attribute
*options tuple Required Options as (value, label) or (value, label, selected)
label str \| None None Label text above select
help_text str \| None None Helper text below select
size "sm" \| "lg" \| None None Select size (default is medium)
disabled bool \| None None Whether select is disabled
required bool \| None None Whether select is required
multiple bool \| None None Allow multiple selections
cls str "" Additional CSS classes
**kwargs Any - Additional HTML attributes (id, hx-, data-, aria-*)

Accessibility

  • Labels are linked to selects via for and id attributes.
  • Required fields are marked with required attribute and a red asterisk in the label.
  • Help text is linked via aria-describedby.

API Reference

faststrap.components.forms.select.Select(name, *options, label=None, help_text=None, size=UNSET, disabled=UNSET, required=UNSET, multiple=UNSET, **kwargs)

Bootstrap Select component for dropdown selections.

Parameters:

Name Type Description Default
name str

Select name attribute

required
*options tuple[str, str] | tuple[str, str, bool]

Options as (value, label) or (value, label, selected)

()
label str | None

Label text

None
help_text str | None

Helper text below select

None
size SizeType | None

Select size (sm, lg)

UNSET
disabled bool | None

Whether select is disabled

UNSET
required bool | None

Whether select is required

UNSET
multiple bool | None

Allow multiple selections

UNSET
**kwargs Any

Additional HTML attributes (cls, id, hx-, data-, etc.)

{}
Source code in src/faststrap/components/forms/select.py
@register(category="forms")
def Select(
    name: str,
    *options: tuple[str, str] | tuple[str, str, bool],
    label: str | None = None,
    help_text: str | None = None,
    size: SizeType | None = UNSET,
    disabled: bool | None = UNSET,
    required: bool | None = UNSET,
    multiple: bool | None = UNSET,
    **kwargs: Any,
) -> Div:
    """Bootstrap Select component for dropdown selections.

    Args:
        name: Select name attribute
        *options: Options as (value, label) or (value, label, selected)
        label: Label text
        help_text: Helper text below select
        size: Select size (sm, lg)
        disabled: Whether select is disabled
        required: Whether select is required
        multiple: Allow multiple selections
        **kwargs: Additional HTML attributes (cls, id, hx-*, data-*, etc.)
    """
    # Resolve API defaults
    cfg = resolve_defaults(
        "Select", size=size, disabled=disabled, required=required, multiple=multiple
    )

    c_size = cfg.get("size")
    c_disabled = cfg.get("disabled", False)
    c_required = cfg.get("required", False)
    c_multiple = cfg.get("multiple", False)

    # Ensure ID for label linkage
    select_id = kwargs.pop("id", name)

    # Build select classes
    classes = ["form-select"]
    if c_size:
        classes.append(f"form-select-{c_size}")

    user_cls = kwargs.pop("cls", "")
    cls = merge_classes(" ".join(classes), user_cls)

    # Build attributes
    attrs: dict[str, Any] = {
        "cls": cls,
        "name": name,
        "id": select_id,
    }

    if c_disabled:
        attrs["disabled"] = True
    if c_required:
        attrs["required"] = True
    if c_multiple:
        attrs["multiple"] = True

    # ARIA for help text
    if help_text:
        attrs["aria_describedby"] = f"{select_id}-help"

    # Convert remaining kwargs
    attrs.update(convert_attrs(kwargs))

    # Process options
    option_nodes: list[Any] = []
    for item in options:
        is_selected = False

        if len(item) == 3:
            value, label_text, is_selected = item
        elif len(item) == 2:
            value, label_text = item
        else:
            raise ValueError(
                f"Option must be (value, label) or (value, label, selected), got {item}"
            )

        opt_attrs: dict[str, Any] = {"value": value}
        if is_selected:
            opt_attrs["selected"] = True

        option_nodes.append(Option(label_text, **opt_attrs))

    # Create select element
    select_el = FTSelect(*option_nodes, **attrs)

    # If just select (no label/help), return select only
    if not label and not help_text:
        return select_el

    # Wrap in div with label and help text
    nodes: list[Any] = []

    if label:
        nodes.append(
            Label(
                label,
                " ",
                Small("*", cls="text-danger") if c_required else "",
                **{"for": select_id},
                cls="form-label",
            )
        )

    nodes.append(select_el)

    if help_text:
        help_id = f"{select_id}-help"
        nodes.append(Small(help_text, cls="form-text text-muted", id=help_id))

    return Div(*nodes, cls="mb-3")