Skip to content

Error Page

The ErrorPage component provides beautiful, full-page error displays for common HTTP errors (404, 500, 403) and custom error scenarios. It returns a tuple of (Title, Content) ready for FastHTML route rendering, with sensible defaults and full customization support.

Goal

By the end of this guide, you'll be able to create professional error pages that handle both standard HTTP errors and backend exceptions, with zero HTML/CSS knowledge required.


Quick Start

Here's the simplest way to create a 404 error page.

Live Preview

404

Page Not Found

The page you're looking for doesn't exist or has been moved.

Go Home
@app.get("/404")
def not_found():
    return ErrorPage(404)

Visual Examples & Use Cases

1. Standard HTTP Errors

ErrorPage comes with beautiful defaults for the most common HTTP errors.

404 - Page Not Found

Page Not Found

The page you're looking for doesn't exist or has been moved.

# Automatic title, message, and icon
ErrorPage(404)
500 - Server Error

Server Error

Something went wrong on our end. We're working to fix it.

# Different icon and message for 500
ErrorPage(500)
403 - Access Denied

Access Denied

You don't have permission to access this resource.

# Locked shield icon for 403
ErrorPage(403)

2. Backend Error Messages

The killer feature: pass backend exceptions directly to the error page.

Live Preview

Server Error

Database connection timeout after 30 seconds

@app.get("/data")
def get_data():
    try:
        data = db.query(...)
    except Exception as e:
        # Pass the exception message directly!
        return ErrorPage(500, message=str(e))

Real-World Example:

@app.post("/profile/save")
def save_profile(req):
    try:
        user = req.session.get("user")
        if not user:
            return ErrorPage(403, message="Please log in to continue")

        # ... save logic ...
        db.commit()
        return hx_redirect("/profile")

    except DatabaseError as e:
        return ErrorPage(500, message=f"Database error: {e}")
    except ValidationError as e:
        return ErrorPage(400, message=f"Invalid data: {e}")

3. Custom Error Pages

Create branded error pages for your app.

Live Preview

I'm a teapot

This server refuses to brew coffee

Try Tea Instead
# Custom 418 error (yes, it's real!)
ErrorPage(
    418,
    title="I'm a teapot",
    message="This server refuses to brew coffee",
    icon="cup-hot",
    action_text="Try Tea Instead",
    action_href="/tea"
)

Practical Functionality

Hiding the Error Code

Sometimes you don't want to show the big error number.

# No giant "404" display
ErrorPage(404, show_code=False)

Custom Action Buttons

Change where the button goes or what it says.

# Go to dashboard instead of home
ErrorPage(
    404,
    action_text="Back to Dashboard",
    action_href="/dashboard"
)

# Hide the button entirely
ErrorPage(500, action_text=None)

Premium Feature Paywall

Use 403 errors creatively for feature gating.

@app.get("/premium-feature")
@require_auth()
def premium_feature(req):
    user = req.session.get("user")

    if not user.is_premium:
        return ErrorPage(
            403,
            title="Premium Feature",
            message="Upgrade your plan to access this feature",
            action_text="View Plans",
            action_href="/pricing"
        )

    return render_premium_feature()

Integration Patterns

With FastHTML Routes

ErrorPage returns a tuple for direct route rendering.

from fasthtml.common import *
from faststrap import ErrorPage

app = FastHTML()

@app.get("/404")
def not_found():
    # Returns (Title(...), Div(...))
    return ErrorPage(404)

@app.get("/500")
def server_error():
    return ErrorPage(500)

With Exception Handlers

Create a global error handler.

@app.exception_handler(404)
def handle_404(req, exc):
    return ErrorPage(404)

@app.exception_handler(500)
def handle_500(req, exc):
    # Log the error
    logger.error(f"Server error: {exc}")

    # Show user-friendly message in production
    if app.debug:
        return ErrorPage(500, message=str(exc))
    else:
        return ErrorPage(500)  # Use default message

With HTMX Requests

Detect HTMX requests and return inline errors instead.

@app.get("/data")
def get_data(req):
    try:
        data = fetch_data()
        return render_data(data)
    except Exception as e:
        # Check if it's an HTMX request
        if req.headers.get("HX-Request"):
            # Return inline error dialog
            from faststrap import ErrorDialog
            return ErrorDialog(message=str(e))
        else:
            # Return full error page
            return ErrorPage(500, message=str(e))

Parameter Reference

Parameter Type Default Description
code int Required HTTP error code (404, 500, 403, or custom)
title str \| None Auto Custom error title (uses default if None)
message str \| None Auto Custom error message (supports backend errors)
icon str \| None Auto Bootstrap icon name (e.g., "exclamation-triangle")
action_text str \| None "Go Home" Text for action button (None to hide)
action_href str "/" URL for action button
show_code bool True Whether to display large error code
**kwargs Any - Additional HTML attributes for container

Default Messages

Code Title Message Icon
404 Page Not Found The page you're looking for doesn't exist... exclamation-triangle
500 Server Error Something went wrong on our end... x-circle
403 Access Denied You don't have permission... shield-lock
Other Error An error occurred. exclamation-circle

Best Practices

✅ Do This

# Pass backend errors in development
if app.debug:
    return ErrorPage(500, message=str(exception))

# Use semantic error codes
return ErrorPage(403)  # Not authorized
return ErrorPage(404)  # Not found
return ErrorPage(500)  # Server error

# Customize action buttons
return ErrorPage(404, action_text="Search", action_href="/search")

❌ Don't Do This

# Don't expose sensitive errors in production
return ErrorPage(500, message=str(database_connection_string))

# Don't use wrong error codes
return ErrorPage(404)  # When user lacks permission (use 403)
return ErrorPage(500)  # For validation errors (use 400)

faststrap.components.feedback.error_page.ErrorPage(code, title=None, message=None, icon=None, action_text='Go Home', action_href='/', show_code=True, **kwargs)

Full-page error display for 404, 500, 403, and custom errors.

Renders a centered, styled error page with icon, title, message, and action button. Supports custom backend error messages.

Parameters:

Name Type Description Default
code ErrorCodeType | int

HTTP error code (404, 500, 403, or custom)

required
title str | None

Custom error title (uses default if None)

None
message str | None

Custom error message (uses default if None, supports backend errors)

None
icon str | None

Bootstrap icon name (uses default if None)

None
action_text str | None

Text for action button (None to hide button)

'Go Home'
action_href str

URL for action button

'/'
show_code bool

Whether to display the error code

True
**kwargs Any

Additional HTML attributes for the container

{}

Returns:

Type Description
tuple[Any, ...]

Tuple of (Title, ErrorPage Div) for FastHTML page rendering

Example

Default 404 page:

ErrorPage(404)

Custom 500 with backend error:

ErrorPage(500, message="Database connection timeout")

Custom 403 with custom action:

ErrorPage( ... 403, ... title="Premium Feature", ... message="Upgrade your plan to access this feature", ... action_text="View Plans", ... action_href="/pricing" ... )

Custom error code:

ErrorPage( ... 418, ... title="I'm a teapot", ... message="This server refuses to brew coffee", ... icon="cup-hot" ... )

Hide action button:

ErrorPage(500, action_text=None)

Note

Returns a tuple for use in FastHTML routes:

@app.get("/404")
def not_found():
    return ErrorPage(404)

For backend errors, pass the error message directly:

try:
    # ... operation ...
except Exception as e:
    return ErrorPage(500, message=str(e))

Source code in src/faststrap/components/feedback/error_page.py
@register(category="feedback")
def ErrorPage(
    code: ErrorCodeType | int,
    title: str | None = None,
    message: str | None = None,
    icon: str | None = None,
    action_text: str | None = "Go Home",
    action_href: str = "/",
    show_code: bool = True,
    **kwargs: Any,
) -> tuple[Any, ...]:
    """Full-page error display for 404, 500, 403, and custom errors.

    Renders a centered, styled error page with icon, title, message, and action button.
    Supports custom backend error messages.

    Args:
        code: HTTP error code (404, 500, 403, or custom)
        title: Custom error title (uses default if None)
        message: Custom error message (uses default if None, supports backend errors)
        icon: Bootstrap icon name (uses default if None)
        action_text: Text for action button (None to hide button)
        action_href: URL for action button
        show_code: Whether to display the error code
        **kwargs: Additional HTML attributes for the container

    Returns:
        Tuple of (Title, ErrorPage Div) for FastHTML page rendering

    Example:
        Default 404 page:
        >>> ErrorPage(404)

        Custom 500 with backend error:
        >>> ErrorPage(500, message="Database connection timeout")

        Custom 403 with custom action:
        >>> ErrorPage(
        ...     403,
        ...     title="Premium Feature",
        ...     message="Upgrade your plan to access this feature",
        ...     action_text="View Plans",
        ...     action_href="/pricing"
        ... )

        Custom error code:
        >>> ErrorPage(
        ...     418,
        ...     title="I'm a teapot",
        ...     message="This server refuses to brew coffee",
        ...     icon="cup-hot"
        ... )

        Hide action button:
        >>> ErrorPage(500, action_text=None)

    Note:
        Returns a tuple for use in FastHTML routes:
        ```python
        @app.get("/404")
        def not_found():
            return ErrorPage(404)
        ```

        For backend errors, pass the error message directly:
        ```python
        try:
            # ... operation ...
        except Exception as e:
            return ErrorPage(500, message=str(e))
        ```
    """
    # Get defaults for known error codes
    defaults = _ERROR_DEFAULTS.get(code, {})

    # Use custom values or defaults
    final_title = title or defaults.get("title", f"Error {code}")
    final_message = message or defaults.get("message", "An error occurred.")
    final_icon = icon or defaults.get("icon", "exclamation-circle")

    # Build error code display
    code_display = None
    if show_code:
        code_display = H1(
            str(code),
            cls="display-1 fw-bold text-muted opacity-25",
            style="font-size: 8rem;",
        )

    # Build action button
    action_button = None
    if action_text:
        from fasthtml.common import A

        action_button = A(
            action_text,
            href=action_href,
            cls="btn btn-primary btn-lg",
        )

    # Build classes
    base_classes = ["min-vh-100", "d-flex", "align-items-center", "justify-content-center"]
    user_cls = kwargs.pop("cls", "")
    all_classes = merge_classes(" ".join(base_classes), user_cls)

    # Build attributes
    attrs: dict[str, Any] = {"cls": all_classes}
    attrs.update(convert_attrs(kwargs))

    # Use EmptyState for the error display
    icon_component = Icon(final_icon) if isinstance(final_icon, str) else final_icon
    error_content = EmptyState(
        title=final_title,
        description=final_message,
        icon=icon_component,
        action=action_button,
        cls="text-center",
    )

    # Wrap in container with code display
    page_content = Div(
        code_display,
        error_content,
        **attrs,
    )

    # Return Title + Page for FastHTML
    page_title = Title(f"{code} - {final_title}")

    return (page_title, page_content)