Skip to content

ExportButton

Standardized export action for CSV, Excel, JSON, or PDF downloads.


Quick Start

Live Preview
from faststrap import ExportButton

ExportButton("Export CSV", endpoint="/export", export_format="csv")

With DataTable State

from faststrap import DataTable, ExportButton

params = DataTable.export_params(sort="name", direction="asc", search="alice")
ExportButton("Export", endpoint="/export", extra_params=params)

GET vs POST

  • method="get" creates a link or HTMX request
  • method="post" creates a form submit
ExportButton(
    "Export",
    endpoint="/export",
    method="post",
    export_format="xlsx",
)

Custom Filename

ExportButton(
    "Export",
    endpoint="/export",
    filename="report.xlsx",
)

HTMX Exports

ExportButton(
    "Export",
    endpoint="/export",
    use_hx=True,
    hx_target="#status",
    hx_swap="innerHTML",
)

Security Notes

Validate export formats server-side and require auth. Avoid using untrusted filenames.


API Reference

faststrap.components.forms.export_button.ExportButton(label='Export', *, endpoint=None, export_format='csv', filename=None, method='get', use_hx=False, hx_target=None, hx_swap='none', push_url=False, variant=None, outline=True, icon='download', extra_params=None, **kwargs)

Export button for CSV/Excel/JSON/PDF downloads.

Source code in src/faststrap/components/forms/export_button.py
@register(category="forms")
@beta
def ExportButton(
    label: str = "Export",
    *,
    endpoint: str | None = None,
    export_format: ExportFormat = "csv",
    filename: str | None = None,
    method: ExportMethod = "get",
    use_hx: bool = False,
    hx_target: str | None = None,
    hx_swap: str | None = "none",
    push_url: bool = False,
    variant: VariantType | None = None,
    outline: bool = True,
    icon: str | None = "download",
    extra_params: dict[str, Any] | None = None,
    **kwargs: Any,
) -> Any:
    """Export button for CSV/Excel/JSON/PDF downloads."""
    cfg = resolve_defaults(
        "ExportButton",
        export_format=export_format,
        method=method,
        use_hx=use_hx,
        hx_swap=hx_swap,
        push_url=push_url,
        variant=variant,
        outline=outline,
    )
    c_format = cfg.get("export_format", export_format)
    c_method = cfg.get("method", method)
    c_use_hx = cfg.get("use_hx", use_hx)
    c_hx_swap = cfg.get("hx_swap", hx_swap)
    c_push_url = cfg.get("push_url", push_url)
    c_variant = cast(VariantType, cfg.get("variant", variant) or "secondary")
    c_outline = cfg.get("outline", outline)

    params: dict[str, Any] = {"format": c_format}
    if filename:
        params["filename"] = filename
    if extra_params:
        params.update(extra_params)

    user_cls = kwargs.pop("cls", "")
    attrs: dict[str, Any] = {"cls": merge_classes("faststrap-export-button", user_cls)}
    attrs.update(convert_attrs(kwargs))

    if endpoint:
        url = _build_url(endpoint, params)
    else:
        url = None

    if c_method == "get":
        if url:
            if c_use_hx:
                attrs.update(
                    {
                        "hx_get": url,
                        "hx_target": hx_target,
                        "hx_swap": c_hx_swap,
                    }
                )
                if c_push_url:
                    attrs["hx_push_url"] = "true"
            else:
                attrs["href"] = url
                if filename:
                    attrs["download"] = filename
        return Button(
            label,
            variant=c_variant,
            outline=c_outline,
            icon=icon,
            **attrs,
        )

    if c_use_hx:
        if url:
            attrs.update(
                {
                    "hx_post": url,
                    "hx_target": hx_target,
                    "hx_swap": c_hx_swap,
                }
            )
            if c_push_url:
                attrs["hx_push_url"] = "true"
        return Button(
            label,
            variant=c_variant,
            outline=c_outline,
            icon=icon,
            **attrs,
        )

    if not url:
        return Button(
            label,
            variant=c_variant,
            outline=c_outline,
            icon=icon,
            **attrs,
        )

    hidden_inputs = [
        FTInput(type="hidden", name="format", value=c_format),
    ]
    if filename:
        hidden_inputs.append(FTInput(type="hidden", name="filename", value=filename))
    if extra_params:
        for key, value in extra_params.items():
            normalized = _normalize_query_value(value)
            if normalized is None:
                continue
            if isinstance(normalized, list):
                hidden_inputs.extend(
                    FTInput(type="hidden", name=str(key), value=item) for item in normalized
                )
            else:
                hidden_inputs.append(FTInput(type="hidden", name=str(key), value=normalized))

    button = Button(
        label,
        variant=c_variant,
        outline=c_outline,
        icon=icon,
        type="submit",
        **attrs,
    )

    return FTForm(
        *hidden_inputs,
        button,
        method="post",
        action=endpoint,
    )