Skip to content

Tag

Tag renders an interactive chip or label for filtering, categorization, and selection. Unlike Badge, which is purely informational, Tag is designed to be interactive — it can be removed by the user and supports HTMX attributes for server-side removal.

Bootstrap Reference

Bootstrap 5 Badges


Quick Start

from faststrap import Tag

# Basic tag
Tag("Python")

# Removable tag
Tag("JavaScript", removable=True)

# Tag with icon
Tag("React", icon="react", variant="primary")

Visual Examples & Use Cases

1. Basic Tags

Live Preview
Python JavaScript Rust
Tag("Python", variant="secondary")
Tag("JavaScript", variant="primary")
Tag("Rust", variant="success")

2. Removable Tags

Live Preview
Python JavaScript
Tag("Python", removable=True)
Tag("JavaScript", removable=True)

3. Tags with Icons

Live Preview
React Crypto
Tag("React", icon="react", variant="primary")
Tag("Crypto", icon="currency-bitcoin", variant="warning")

4. Filter Tags with HTMX

Live Preview
Python
Tag(
    "Python",
    removable=True,
    on_remove="hx-delete='/filters/python' hx-target='#filter-list'",
)

Practical Functionality

1. Filter Bar

from faststrap import Tag, Form

def filter_bar(active_filters):
    tags = []
    for filter_name in active_filters:
        tags.append(
            Tag(
                filter_name,
                removable=True,
                on_remove=f"hx-delete='/filters/{filter_name}' hx-target='#results'",
            )
        )
    return Form(*tags, cls="d-flex flex-wrap gap-2")

2. Removable Tags with Animation

Tags include a data-fs-tag="true" attribute that the Faststrap initializer uses to add a fade-out animation when the remove button is clicked. The animation is handled automatically by faststrap-init.js.

Tag(
    "Draft",
    variant="warning",
    removable=True,
    on_remove="hx-delete='/tags/1' hx-swap='outerHTML'",
)

Tag vs Badge

Feature Tag Badge
Interactive Yes (removable) No
Use case Filters, labels, selections Status indicators, counts
Remove button Optional No
Animation Fade-out on remove No

Parameter Reference

Parameter Type Default Description
children str \| tuple required Tag text content.
variant VariantType "secondary" Bootstrap color variant.
size "sm" \| "md" "md" Tag size.
removable bool False Show a close/remove button.
icon str \| None None Optional leading Bootstrap icon name.
on_remove str \| None None HTMX attributes for removal (e.g. hx-delete="/tags/1").
**kwargs Any {} Additional HTML attributes (cls, id, hx-, data-, etc.).

API Reference

faststrap.components.display.tag.Tag(children, *, variant='secondary', size='md', removable=False, icon=None, on_remove=None, **kwargs)

Render a tag/chip component.

Different from Badge: Tags are interactive, often removable, and used for filtering/labeling. Badge is purely informational.

Parameters:

Name Type Description Default
children str | tuple

Tag text content

required
variant VariantType

Bootstrap color variant

'secondary'
size Literal['sm', 'md']

Tag size (sm or md)

'md'
removable bool

Show a close/remove button

False
icon str | None

Optional leading icon name (Remix Icon)

None
on_remove str | None

HTMX attribute for removal (e.g. hx-delete="/tags/1")

None
**kwargs Any

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

{}

Returns:

Type Description
Span

FastHTML Span element with tag/chip styling

Source code in src/faststrap/components/display/tag.py
@register(category="display")
@stable
def Tag(
    children: str | tuple,
    *,
    variant: VariantType = "secondary",
    size: Literal["sm", "md"] = "md",
    removable: bool = False,
    icon: str | None = None,
    on_remove: str | None = None,
    **kwargs: Any,
) -> Span:
    """Render a tag/chip component.

    Different from Badge: Tags are interactive, often removable, and used for
    filtering/labeling. Badge is purely informational.

    Args:
        children: Tag text content
        variant: Bootstrap color variant
        size: Tag size (sm or md)
        removable: Show a close/remove button
        icon: Optional leading icon name (Remix Icon)
        on_remove: HTMX attribute for removal (e.g. hx-delete="/tags/1")
        **kwargs: Additional HTML attributes (cls, id, hx-*, data-*, etc.)

    Returns:
        FastHTML Span element with tag/chip styling
    """
    user_cls = kwargs.pop("cls", "")

    classes = ["badge", "d-inline-flex", "align-items-center", "gap-1"]

    # Variant
    if variant:
        classes.append(f"text-bg-{variant}")

    # Size
    if size == "sm":
        classes.append("badge-sm")
    else:
        classes.extend(["px-2", "py-1"])

    # Interactive styling
    classes.append("user-select-none")

    attrs: dict[str, Any] = {
        "cls": merge_classes(" ".join(classes), user_cls),
        "data_fs_tag": "true",
    }
    attrs.update(convert_attrs(kwargs))

    parts: list[Any] = []

    # Leading icon
    if icon:
        parts.append(
            Span(
                Icon(icon, cls="align-middle", aria_hidden="true"),
                style="font-size: 0.85em;",
            )
        )

    # Label text
    parts.append(Span(children, cls="align-middle"))

    # Remove button
    if removable:
        remove_attrs: dict[str, Any] = {
            "type": "button",
            "cls": "btn-close btn-close-white ms-1",
            "aria_label": "Remove",
            "style": "font-size: 0.65em; filter: brightness(0) invert(1);",
        }
        if on_remove:
            # Parse the on_remove string as HTMX attributes
            # e.g. "hx-delete='/tags/1'" or "hx-trigger='click' hx-delete='/tags/1'"
            htmx_pairs = _parse_htmx_attrs(on_remove)
            remove_attrs.update(htmx_pairs)
        parts.append(Button(**remove_attrs))

    return Span(*parts, **attrs)