Skip to content

API Reference

This section provides automatically generated documentation from the FastStrap source code. It is useful for looking up exact parameter names and types.

Core Utilities

faststrap.core.theme.resolve_defaults(component, **kwargs)

Resolve component attributes by merging defaults with user arguments.

Priority (highest to lowest): 1. Explicit user arguments (if not None) 2. Global component defaults (set via set_component_defaults)

Parameters:

Name Type Description Default
component str

Component name (e.g., "Button")

required
**kwargs Any

Arguments passed by the user

{}

Returns:

Type Description
dict[str, Any]

Dict of resolved attributes

Example

set_component_defaults("Button", variant="secondary") resolve_defaults("Button", variant=None, size="lg")

Source code in src/faststrap/core/theme.py
def resolve_defaults(component: str, **kwargs: Any) -> dict[str, Any]:
    """Resolve component attributes by merging defaults with user arguments.

    Priority (highest to lowest):
    1. Explicit user arguments (if not None)
    2. Global component defaults (set via set_component_defaults)

    Args:
        component: Component name (e.g., "Button")
        **kwargs: Arguments passed by the user

    Returns:
        Dict of resolved attributes

    Example:
        >>> set_component_defaults("Button", variant="secondary")
        >>> resolve_defaults("Button", variant=None, size="lg")
        {"variant": "secondary", "size": "lg"}
    """
    global _COMPONENT_DEFAULTS_LOCKED
    _COMPONENT_DEFAULTS_LOCKED = True

    defaults = get_component_defaults(component)
    resolved = defaults.copy()

    for key, value in kwargs.items():
        if value is not None:
            resolved[key] = value

    return resolved

faststrap.core.base.merge_classes(*class_lists)

Merge multiple class strings or lists, removing duplicates.

Source code in src/faststrap/core/base.py
def merge_classes(*class_lists: Any) -> str:
    """Merge multiple class strings or lists, removing duplicates."""
    classes: list[str] = []
    seen = set()

    def _process(item: Any) -> None:
        if not item:
            return
        if isinstance(item, (list, tuple)):
            for sub in item:
                _process(sub)
        elif isinstance(item, str):
            for cls in item.split():
                cls = cls.strip()
                if cls and cls not in seen:
                    classes.append(cls)
                    seen.add(cls)

    for item in class_lists:
        _process(item)

    return " ".join(classes)

Attributes Helper

faststrap.utils.attrs.convert_attrs(kwargs)

Convert Python kwargs to HTML attributes (hx_get -> hx-get).

Rules: - None values are dropped - boolean False values are dropped (except aria_*, which are preserved) - boolean True values are kept (FastHTML will serialize appropriately) - style may be a str or a dict (dict is serialized) - css_vars may be a dict and will be merged into style - data={...} expands to data-* - aria={...} expands to aria-*

Parameters:

Name Type Description Default
kwargs dict[str, Any]

Python-style keyword arguments

required

Returns:

Type Description
dict[str, Any]

HTML-style attributes with hyphens

Source code in src/faststrap/utils/attrs.py
def convert_attrs(kwargs: dict[str, Any]) -> dict[str, Any]:
    """Convert Python kwargs to HTML attributes (hx_get -> hx-get).

    Rules:
    - `None` values are dropped
    - boolean `False` values are dropped (except `aria_*`, which are preserved)
    - boolean `True` values are kept (FastHTML will serialize appropriately)
    - `style` may be a `str` or a `dict` (dict is serialized)
    - `css_vars` may be a dict and will be merged into `style`
    - `data={...}` expands to `data-*`
    - `aria={...}` expands to `aria-*`

    Args:
        kwargs: Python-style keyword arguments

    Returns:
        HTML-style attributes with hyphens
    """
    converted: dict[str, Any] = {}

    # ---- Extract style/css_vars/data/aria ---------------------------------
    style_val = kwargs.get("style")
    css_vars_val = kwargs.get("css_vars")
    data_val = kwargs.get("data")
    aria_val = kwargs.get("aria")

    style_str: str | None = None

    if isinstance(style_val, dict):
        style_str = _style_to_string(style_val)
    elif isinstance(style_val, str):
        style_str = style_val.strip() or None
    elif style_val is None:
        style_str = None

    if isinstance(css_vars_val, dict):
        css_style: dict[str, Any] = {}
        for k, v in css_vars_val.items():
            if v is None:
                continue
            key = str(k)
            if not key.startswith("--"):
                key = f"--{_css_key(key)}"
            css_style[key] = v
        style_str = _merge_style(style_str, _style_to_string(css_style))

    # ---- Convert regular attributes ---------------------------------------
    for k, v in kwargs.items():
        if k in {"style", "css_vars", "data", "aria"}:
            continue

        # Keep cls as-is (FastHTML convention)
        if k == "cls":
            converted[k] = v
            continue

        # Normalize aria booleans as strings (preferred for HTML output)
        if k.startswith("aria_") and isinstance(v, bool):
            converted[_to_kebab(k)] = "true" if v else "false"
            continue

        # Drop None and False
        if v is None:
            continue
        if isinstance(v, bool) and v is False:
            continue

        converted[_to_kebab(k)] = v

    # ---- Expand structured data/aria dicts --------------------------------
    if isinstance(data_val, dict):
        for dk, dv in data_val.items():
            if dv is None:
                continue
            if isinstance(dv, bool):
                converted[f"data-{_to_kebab(str(dk))}"] = "true" if dv else "false"
            else:
                converted[f"data-{_to_kebab(str(dk))}"] = _stringify_attr_value(dv)

    if isinstance(aria_val, dict):
        for ak, av in aria_val.items():
            if av is None:
                continue
            if isinstance(av, bool):
                converted[f"aria-{_to_kebab(str(ak))}"] = "true" if av else "false"
            else:
                converted[f"aria-{_to_kebab(str(ak))}"] = _stringify_attr_value(av)

    # ---- Apply merged style if present ------------------------------------
    if style_str:
        converted["style"] = style_str

    return converted

Application Setup

faststrap.core.assets.add_bootstrap(app, theme=None, mode='light', use_cdn=None, mount_static=True, static_url='/static', force_static_url=False, include_favicon=True, favicon_url=None, font_family=None, font_weights=None, components=None)

Enhance FastHTML app with Bootstrap and FastStrap assets.

Parameters:

Name Type Description Default
app Any

FastHTML application instance

required
theme str | Theme | None

Color theme - either a built-in name (e.g., "green-nature", "purple-magic"), a Theme instance created via create_theme(), or a community theme

None
mode ModeType

Color mode for light/dark backgrounds: - "light": Light background, dark text (default) - "dark": Dark background, light text - "auto": Follows user's system preference (prefers-color-scheme)

'light'
use_cdn bool | None

When True, ALL assets (Bootstrap CSS/JS, Bootstrap Icons, Faststrap CSS files, favicon) are served from CDN. No local StaticFiles are mounted. Required for serverless deployments (Vercel, AWS Lambda, Google Cloud Run). Default: False.

None
mount_static bool

Auto-mount static directory

True
static_url str

Preferred URL prefix for static files

'/static'
force_static_url bool

Force use of this URL even if already mounted

False
include_favicon bool

Include default FastStrap favicon

True
favicon_url str | None

Custom favicon URL (overrides default)

None
font_family str | None

Google Font name (e.g., "Inter", "Roboto", "Poppins")

None
font_weights list[int] | None

Font weights to load (default: [400, 500, 700])

None
components list[Any] | None

Optional list of Faststrap component functions used in the app. When provided, Bootstrap JS is only injected if at least one component has requires_js=True in its registry metadata. Components without @register() metadata are treated as requires_js=False. When None (default), JS is always injected.

None

Returns:

Type Description
Any

Modified app instance

Example

Basic setup with light mode

add_bootstrap(app)

Dark mode with a color theme

add_bootstrap(app, theme="purple-magic", mode="dark")

Auto mode (follows system preference)

add_bootstrap(app, theme="green-nature", mode="auto")

Custom theme with dark mode

from faststrap import create_theme my_theme = create_theme(primary="#7BA05B", secondary="#48C774") add_bootstrap(app, theme=my_theme, mode="dark")

Built-in theme with custom font

add_bootstrap(app, theme="green-nature", font_family="Inter")

Custom theme with custom font

my_theme = create_theme(primary="#7BA05B") add_bootstrap(app, theme=my_theme, font_family="Roboto", font_weights=[400, 600, 700])

Font only, no theme

add_bootstrap(app, font_family="Poppins")

CDN mode for production

add_bootstrap(app, theme="blue-ocean", mode="auto", use_cdn=True)

Source code in src/faststrap/core/assets.py
def add_bootstrap(
    app: Any,
    theme: str | Theme | None = None,
    mode: ModeType = "light",
    use_cdn: bool | None = None,
    mount_static: bool = True,
    static_url: str = "/static",
    force_static_url: bool = False,
    include_favicon: bool = True,
    favicon_url: str | None = None,
    font_family: str | None = None,
    font_weights: list[int] | None = None,
    components: list[Any] | None = None,
) -> Any:
    """Enhance FastHTML app with Bootstrap and FastStrap assets.

    Args:
        app: FastHTML application instance
        theme: Color theme - either a built-in name (e.g., "green-nature", "purple-magic"),
               a Theme instance created via create_theme(), or a community theme
        mode: Color mode for light/dark backgrounds:
              - "light": Light background, dark text (default)
              - "dark": Dark background, light text
              - "auto": Follows user's system preference (prefers-color-scheme)
        use_cdn: When True, ALL assets (Bootstrap CSS/JS, Bootstrap Icons,
                 Faststrap CSS files, favicon) are served from CDN. No local
                 StaticFiles are mounted. Required for serverless deployments
                 (Vercel, AWS Lambda, Google Cloud Run). Default: False.
        mount_static: Auto-mount static directory
        static_url: Preferred URL prefix for static files
        force_static_url: Force use of this URL even if already mounted
        include_favicon: Include default FastStrap favicon
        favicon_url: Custom favicon URL (overrides default)
        font_family: Google Font name (e.g., "Inter", "Roboto", "Poppins")
        font_weights: Font weights to load (default: [400, 500, 700])
        components: Optional list of Faststrap component functions used in the app.
            When provided, Bootstrap JS is only injected if at least one
            component has requires_js=True in its registry metadata.
            Components without @register() metadata are treated as
            requires_js=False. When None (default), JS is always injected.

    Returns:
        Modified app instance

    Example:
        # Basic setup with light mode
        add_bootstrap(app)

        # Dark mode with a color theme
        add_bootstrap(app, theme="purple-magic", mode="dark")

        # Auto mode (follows system preference)
        add_bootstrap(app, theme="green-nature", mode="auto")

        # Custom theme with dark mode
        from faststrap import create_theme
        my_theme = create_theme(primary="#7BA05B", secondary="#48C774")
        add_bootstrap(app, theme=my_theme, mode="dark")

        # Built-in theme with custom font
        add_bootstrap(app, theme="green-nature", font_family="Inter")

        # Custom theme with custom font
        my_theme = create_theme(primary="#7BA05B")
        add_bootstrap(app, theme=my_theme, font_family="Roboto", font_weights=[400, 600, 700])

        # Font only, no theme
        add_bootstrap(app, font_family="Poppins")

        # CDN mode for production
        add_bootstrap(app, theme="blue-ocean", mode="auto", use_cdn=True)
    """
    if getattr(app, "_faststrap_bootstrap_added", False):
        raise RuntimeError(
            "add_bootstrap() has already been called on this app. "
            "It should only be called once during app setup. "
            "If you are using fast_app(), call add_bootstrap() after "
            "fast_app() returns the app object."
        )
    if use_cdn is None:
        use_cdn = environ.get("FASTSTRAP_USE_CDN", "false").lower() == "true"
    include_js = True if components is None else _any_requires_js(components)

    # 1. Determine where to mount static files
    actual_static_url = static_url
    if not use_cdn and mount_static:
        if force_static_url:
            actual_static_url = static_url
        else:
            # Only resolve and mount if not already done
            if hasattr(app, "_faststrap_static_url"):
                actual_static_url = app._faststrap_static_url
            else:
                actual_static_url = resolve_static_url(app, static_url)

    # 2. Collect favicon links FIRST (before Bootstrap assets)
    favicon_links: list[Any] = []
    if favicon_url:
        favicon_links = create_favicon_links(favicon_url)
    elif include_favicon:
        if not use_cdn:
            default_favicon = get_default_favicon_url(False, actual_static_url)
            favicon_links = create_favicon_links(default_favicon)

    # 3. Get Bootstrap assets with theme, mode, and font
    bootstrap_assets = get_assets(
        use_cdn=use_cdn,
        include_custom=True,
        static_url=actual_static_url if not use_cdn else None,
        theme=theme,
        mode=mode,
        font_family=font_family,
        font_weights=font_weights,
        include_js=include_js,
        include_favicon=use_cdn and include_favicon and favicon_url is None,
    )

    # 4. Idempotent Header Management
    # Remove any existing FastStrap headers to prevent accumulation
    new_fs_hdrs = list(favicon_links) + list(bootstrap_assets)
    old_fs_hdrs = getattr(app, "_faststrap_hdrs", [])

    current_hdrs = list(getattr(app, "hdrs", []))

    # Remove old items by identity if possible
    filtered_hdrs = [h for h in current_hdrs if h not in old_fs_hdrs]

    # Prepend new ones
    app.hdrs = new_fs_hdrs + filtered_hdrs
    app._faststrap_hdrs = new_fs_hdrs

    # 5. Apply data-bs-theme attribute for non-auto modes
    if mode in {"light", "dark"}:
        existing_htmlkw = getattr(app, "htmlkw", {}) or {}
        existing_htmlkw.update({"data-bs-theme": mode})
        app.htmlkw = existing_htmlkw

    # 6. Mount static files (once only)
    if not use_cdn and mount_static and not hasattr(app, "_faststrap_static_url"):
        try:
            static_path = get_static_path()
            # Use insert(0) to ensure static route takes precedence over catch-all routes (like fast_app's /{path})
            app.routes.insert(
                0,
                Mount(
                    actual_static_url,
                    StaticFiles(directory=str(static_path)),
                    name="faststrap_static",
                ),
            )
            app._faststrap_static_url = actual_static_url
        except Exception as e:
            # Check if this is a "already mounted" error (which is fine)
            error_msg = str(e).lower()
            if any(
                keyword in error_msg for keyword in ["already", "duplicate", "mounted", "exists"]
            ):
                # Static files already mounted by another call, just mark it
                app._faststrap_static_url = actual_static_url
            else:
                # Real error - fall back to CDN
                caution = f"""
            FastStrap: Could not mount local static files ({e}).
            Falling back to CDN mode. You can explicitly set use_cdn=True.
            """
                warnings.warn(caution, RuntimeWarning, stacklevel=2)
                use_cdn = True
                if favicon_url:
                    fallback_favicon_links = create_favicon_links(favicon_url)
                else:
                    fallback_favicon_links = []

                fallback_bootstrap_assets = get_assets(
                    use_cdn=True,
                    include_custom=True,
                    static_url=None,
                    theme=theme,
                    mode=mode,
                    font_family=font_family,
                    font_weights=font_weights,
                    include_js=include_js,
                    include_favicon=include_favicon and favicon_url is None,
                )
                fallback_fs_hdrs = list(fallback_favicon_links) + list(fallback_bootstrap_assets)
                app.hdrs = fallback_fs_hdrs + filtered_hdrs
                app._faststrap_hdrs = fallback_fs_hdrs

    app._faststrap_bootstrap_added = True
    return app

faststrap.utils.static_management.get_faststrap_static_url(app)

Get the URL where FastStrap static files are mounted for this specific app.

Parameters:

Name Type Description Default
app Any

The FastHTML app instance

required

Returns:

Type Description
str | None

The static URL if mounted, None otherwise

Source code in src/faststrap/utils/static_management.py
def get_faststrap_static_url(app: Any) -> str | None:
    """
    Get the URL where FastStrap static files are mounted for this specific app.

    Args:
        app: The FastHTML app instance

    Returns:
        The static URL if mounted, None otherwise
    """
    return getattr(app, "_faststrap_static_url", None)

Theme System

Notes: - add_bootstrap() supports font_family and font_weights for Google Fonts injection. - set_component_defaults() modifies process-global defaults. Configure it at application startup. - BaseComponent / Component are extension points for third-party class-based components; built-ins remain function-based.

faststrap.core.theme.create_theme(primary=None, secondary=None, success=None, danger=None, warning=None, info=None, **extra_vars)

Create a custom theme from color values.

Parameters:

Name Type Description Default
primary str | None

Primary color (e.g., "#7BA05B")

None
secondary str | None

Secondary color

None
success str | None

Success color (defaults to Bootstrap green)

None
danger str | None

Danger color (defaults to Bootstrap red)

None
warning str | None

Warning color (defaults to Bootstrap yellow)

None
info str | None

Info color (defaults to Bootstrap cyan)

None
**extra_vars str

Additional CSS variables

{}

Returns:

Type Description
Theme

Theme instance

Example

theme = create_theme( ... primary="#7BA05B", ... secondary="#48C774", ... ) add_bootstrap(app, theme=theme, mode="dark")

Source code in src/faststrap/core/theme.py
def create_theme(
    primary: str | None = None,
    secondary: str | None = None,
    success: str | None = None,
    danger: str | None = None,
    warning: str | None = None,
    info: str | None = None,
    **extra_vars: str,
) -> Theme:
    """Create a custom theme from color values.

    Args:
        primary: Primary color (e.g., "#7BA05B")
        secondary: Secondary color
        success: Success color (defaults to Bootstrap green)
        danger: Danger color (defaults to Bootstrap red)
        warning: Warning color (defaults to Bootstrap yellow)
        info: Info color (defaults to Bootstrap cyan)
        **extra_vars: Additional CSS variables

    Returns:
        Theme instance

    Example:
        >>> theme = create_theme(
        ...     primary="#7BA05B",
        ...     secondary="#48C774",
        ... )
        >>> add_bootstrap(app, theme=theme, mode="dark")
    """
    variables: dict[str, str] = {}

    # Add provided colors with auto-generated RGB values
    color_map = {
        "primary": primary,
        "secondary": secondary,
        "success": success,
        "danger": danger,
        "warning": warning,
        "info": info,
    }

    for name, color in color_map.items():
        if color:
            variables[f"--bs-{name}"] = color
            # Auto-generate RGB value from hex
            rgb = _hex_to_rgb(color)
            if rgb:
                variables[f"--bs-{name}-rgb"] = rgb

    # Add any extra variables
    for key, value in extra_vars.items():
        # Normalize key to CSS variable format
        if not key.startswith("--"):
            key = f"--bs-{key.replace('_', '-')}"
        variables[key] = value

    return Theme(variables)

faststrap.core.theme.set_component_defaults(component, **defaults)

Set default values for a component globally.

This updates process-global state shared by all requests. Configure defaults during application startup.

Parameters:

Name Type Description Default
component str

Component name (e.g., "Button")

required
**defaults Any

Default values to set

{}
Example

set_component_defaults("Button", variant="outline-primary", size="sm")

Now all Button() calls use these defaults unless overridden

Source code in src/faststrap/core/theme.py
def set_component_defaults(component: str, **defaults: Any) -> None:
    """Set default values for a component globally.

    This updates process-global state shared by all requests.
    Configure defaults during application startup.

    Args:
        component: Component name (e.g., "Button")
        **defaults: Default values to set

    Example:
        >>> set_component_defaults("Button", variant="outline-primary", size="sm")
        >>> # Now all Button() calls use these defaults unless overridden
    """
    global _COMPONENT_DEFAULTS_WARNED

    if _COMPONENT_DEFAULTS_LOCKED and not _COMPONENT_DEFAULTS_WARNED:
        warnings.warn(
            "set_component_defaults() updates process-global state. "
            "Prefer calling it during application startup before handling requests.",
            RuntimeWarning,
            stacklevel=2,
        )
        _COMPONENT_DEFAULTS_WARNED = True

    if component not in _COMPONENT_DEFAULTS:
        _COMPONENT_DEFAULTS[component] = {}
    _COMPONENT_DEFAULTS[component].update(defaults)

faststrap.core.theme.reset_component_defaults(component=None)

Reset component defaults to original values.

Parameters:

Name Type Description Default
component str | None

Component name to reset, or None to reset all

None
Source code in src/faststrap/core/theme.py
def reset_component_defaults(component: str | None = None) -> None:
    """Reset component defaults to original values.

    Args:
        component: Component name to reset, or None to reset all
    """
    global _COMPONENT_DEFAULTS, _COMPONENT_DEFAULTS_LOCKED, _COMPONENT_DEFAULTS_WARNED

    if component is None:
        # Reset all components to original defaults
        _COMPONENT_DEFAULTS = {k: v.copy() for k, v in _DEFAULT_COMPONENT_DEFAULTS.items()}
        _COMPONENT_DEFAULTS_LOCKED = False
        _COMPONENT_DEFAULTS_WARNED = False
    elif component in _DEFAULT_COMPONENT_DEFAULTS:
        # Reset specific component to original default
        _COMPONENT_DEFAULTS[component] = _DEFAULT_COMPONENT_DEFAULTS[component].copy()