CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-python-fasthtml

The fastest way to create HTML apps - a next-generation Python web framework for building fast, scalable web applications with minimal code

Pending
Overview
Eval results
Files

htmx-integration.mddocs/

HTMX Integration and Dynamic Interactions

Built-in HTMX support for creating dynamic, interactive web applications without writing JavaScript. FastHTML provides seamless integration with HTMX attributes, response handling, and event management.

Capabilities

HTMX Request Headers

Access HTMX-specific request information through structured headers dataclass.

class HtmxHeaders:
    """
    HTMX request headers dataclass.
    
    Provides access to HTMX-specific request information.
    """
    hx_request: str          # Indicates HTMX request
    hx_target: str           # Target element ID or selector
    hx_trigger: str          # Element that triggered the request
    hx_trigger_name: str     # Name of the triggering element
    hx_current_url: str      # Current page URL
    hx_history_restore_request: str  # History restore request indicator
    hx_prompt: str           # User input from hx-prompt
    hx_boosted: str          # Boosted request indicator

Enhanced HTML with HTMX

Create HTML elements with built-in HTMX functionality and attributes.

def ft_hx(tag: str, *c, **kw):
    """
    Create HTML element with HTMX support.
    
    Automatically processes HTMX attributes and provides enhanced
    functionality for dynamic interactions.
    
    Args:
        tag: HTML tag name
        *c: Child content
        **kw: HTML attributes including HTMX attributes
        
    Returns:
        HTML element with HTMX functionality
    """

HTMX Response Processing

Handle HTMX-specific response requirements and content swapping.

def is_full_page(request) -> bool:
    """
    Check if response should be full page or partial.
    
    Determines whether to return a complete HTML page or
    just the requested fragment based on HTMX headers.
    
    Args:
        request: HTTP request object
        
    Returns:
        bool: True if full page response needed
    """

def flat_xt(xt):
    """
    Flatten XML tree structure for HTMX responses.
    
    Args:
        xt: XML tree or FastHTML element structure
        
    Returns:
        Flattened structure suitable for HTMX consumption
    """

HTMX Constants and Configuration

Pre-defined HTMX constants and configuration options.

htmx_hdrs: dict
"""HTMX header mappings for request processing."""

htmx_resps: dict
"""HTMX response type mappings."""

htmx_exts: dict
"""Available HTMX extensions."""

htmxsrc: str
"""HTMX JavaScript source URL."""

def def_hdrs(htmx: bool = True, surreal: bool = True) -> list:
    """
    Get default headers with HTMX/Surreal support.
    
    Args:
        htmx: Include HTMX JavaScript library
        surreal: Include Surreal.js library
        
    Returns:
        list: Default header elements
    """

HTMX Attributes

FastHTML supports all HTMX attributes as keyword arguments. Here are the most commonly used:

Core HTMX Attributes

# HTTP request attributes
hx_get="/path"           # GET request to path
hx_post="/path"          # POST request to path
hx_put="/path"           # PUT request to path
hx_patch="/path"         # PATCH request to path
hx_delete="/path"        # DELETE request to path

# Target and swapping
hx_target="#element-id"   # Target element for response
hx_swap="innerHTML"       # How to swap content (innerHTML, outerHTML, etc.)
hx_select="#selector"     # Select part of response

# Triggering
hx_trigger="click"        # Event that triggers request
hx_trigger="click delay:1s"  # Trigger with delay
hx_trigger="every 5s"     # Periodic trigger

# Loading states
hx_indicator="#spinner"   # Loading indicator element
hx_disabled_elt="this"    # Disable element during request

# Form handling
hx_include="#form-data"   # Include additional form data
hx_params="*"            # Which parameters to include

# History and navigation
hx_push_url="true"       # Push URL to browser history
hx_replace_url="true"    # Replace current URL

# Validation and confirmation
hx_confirm="Are you sure?"  # Confirmation dialog
hx_validate="true"       # Client-side validation

Usage Examples

Basic HTMX Interactions

from fasthtml.common import *

app, rt = fast_app()

@rt('/')
def homepage():
    return Titled("HTMX Demo",
        Div(
            H1("HTMX Integration Examples"),
            
            # Simple GET request
            Button(
                "Load Content",
                hx_get="/content",
                hx_target="#content-area"
            ),
            Div(id="content-area", "Content will load here"),
            
            # POST form with HTMX
            Form(
                Input(type="text", name="message", placeholder="Enter message"),
                Button("Send", type="submit"),
                hx_post="/send-message",
                hx_target="#messages",
                hx_swap="afterbegin"
            ),
            Div(id="messages")
        )
    )

@rt('/content')
def load_content():
    return Div(
        P("This content was loaded dynamically!"),
        Small(f"Loaded at {datetime.now()}"),
        style="padding: 1rem; border: 1px solid #ccc; margin: 1rem 0;"
    )

@rt('/send-message', methods=['POST'])
def send_message(message: str):
    return Div(
        Strong("New message: "),
        Span(message),
        style="padding: 0.5rem; background: #e8f5e8; margin: 0.5rem 0;"
    )

Advanced HTMX Patterns

from fasthtml.common import *

app, rt = fast_app()

@rt('/advanced-htmx')
def advanced_examples():
    return Titled("Advanced HTMX",
        Div(
            # Live search with debouncing
            Section(
                H2("Live Search"),
                Input(
                    type="text",
                    name="search",
                    placeholder="Search users...",
                    hx_get="/search",
                    hx_target="#search-results",
                    hx_trigger="keyup changed delay:300ms",
                    hx_indicator="#search-spinner"
                ),
                Div(id="search-spinner", "🔄", style="display: none;"),
                Div(id="search-results")
            ),
            
            # Infinite scroll
            Section(
                H2("Infinite Scroll"),
                Div(id="content-list",
                    # Initial content
                    *[Div(f"Item {i}", cls="list-item") for i in range(10)],
                    # Load more trigger
                    Div(
                        "Loading more...",
                        hx_get="/load-more?page=2",
                        hx_target="this",
                        hx_swap="outerHTML",
                        hx_trigger="revealed"
                    )
                )
            ),
            
            # Modal dialog
            Section(
                H2("Modal Dialog"),
                Button(
                    "Open Modal",
                    hx_get="/modal",
                    hx_target="body",
                    hx_swap="beforeend"
                )
            ),
            
            # Real-time updates
            Section(
                H2("Real-time Counter"),
                Div(id="counter", "0"),
                Button(
                    "Start Updates",
                    hx_get="/start-counter",
                    hx_target="#counter",
                    hx_trigger="click"
                )
            )
        )
    )

@rt('/search')
def search_users(search: str = ""):
    if not search:
        return Div("Enter search term")
    
    # Simulate user search
    users = [f"User {i}: {search}" for i in range(1, 6)]
    return Div(
        *[Div(user, cls="search-result") for user in users]
    )

@rt('/load-more')
def load_more(page: int = 1):
    start = (page - 1) * 10
    items = [Div(f"Item {start + i}", cls="list-item") for i in range(1, 11)]
    
    if page < 5:  # Simulate having more pages
        load_trigger = Div(
            "Loading more...",
            hx_get=f"/load-more?page={page + 1}",
            hx_target="this",
            hx_swap="outerHTML",
            hx_trigger="revealed"
        )
        items.append(load_trigger)
    
    return Div(*items)

@rt('/modal')
def show_modal():
    return Div(
        Div(
            H3("Modal Title"),
            P("This is modal content"),
            Button(
                "Close",
                onclick="this.closest('.modal-overlay').remove()"
            ),
            cls="modal-content"
        ),
        cls="modal-overlay",
        style="""
            position: fixed; top: 0; left: 0; right: 0; bottom: 0;
            background: rgba(0,0,0,0.5); display: flex;
            align-items: center; justify-content: center;
        """
    )

@rt('/start-counter')
def start_counter():
    return Div(
        "1",
        hx_get="/increment-counter?count=1",
        hx_trigger="load delay:1s",
        hx_swap="outerHTML"
    )

@rt('/increment-counter')
def increment_counter(count: int = 0):
    new_count = count + 1
    if new_count <= 10:
        return Div(
            str(new_count),
            hx_get=f"/increment-counter?count={new_count}",
            hx_trigger="load delay:1s",
            hx_swap="outerHTML"
        )
    return Div("Finished!")

Form Handling with HTMX

from fasthtml.common import *

app, rt = fast_app()

@rt('/htmx-forms')
def htmx_forms():
    return Titled("HTMX Forms",
        Div(
            # Inline validation
            Form(
                H2("Registration Form"),
                Div(
                    Label("Username:", for_="username"),
                    Input(
                        type="text",
                        name="username",
                        id="username",
                        hx_post="/validate-username",
                        hx_target="#username-validation",
                        hx_trigger="blur"
                    ),
                    Div(id="username-validation"),
                    cls="form-group"
                ),
                Div(
                    Label("Email:", for_="email"),
                    Input(
                        type="email",
                        name="email",
                        id="email",
                        hx_post="/validate-email",
                        hx_target="#email-validation",
                        hx_trigger="blur"
                    ),
                    Div(id="email-validation"),
                    cls="form-group"
                ),
                Button("Register", type="submit"),
                hx_post="/register",
                hx_target="#form-result"
            ),
            Div(id="form-result"),
            
            # Dynamic form fields
            Form(
                H2("Dynamic Fields"),
                Div(id="field-container",
                    Div(
                        Input(type="text", name="field1", placeholder="Field 1"),
                        cls="dynamic-field"
                    )
                ),
                Button(
                    "Add Field",
                    type="button",
                    hx_post="/add-field",
                    hx_target="#field-container",
                    hx_swap="beforeend"
                ),
                Button("Submit", type="submit"),
                hx_post="/submit-dynamic",
                hx_target="#dynamic-result"
            ),
            Div(id="dynamic-result")
        )
    )

@rt('/validate-username', methods=['POST'])
def validate_username(username: str):
    if len(username) < 3:
        return Div("Username too short", style="color: red;")
    elif username.lower() in ['admin', 'root', 'user']:
        return Div("Username not available", style="color: red;")
    else:
        return Div("Username available", style="color: green;")

@rt('/validate-email', methods=['POST'])
def validate_email(email: str):
    if '@' not in email:
        return Div("Invalid email format", style="color: red;")
    else:
        return Div("Email format valid", style="color: green;")

@rt('/add-field', methods=['POST'])
def add_field():
    import random
    field_id = random.randint(1000, 9999)
    return Div(
        Input(
            type="text",
            name=f"field{field_id}",
            placeholder=f"Field {field_id}"
        ),
        Button(
            "Remove",
            type="button",
            onclick="this.closest('.dynamic-field').remove()"
        ),
        cls="dynamic-field"
    )

Error Handling and Loading States

from fasthtml.common import *

app, rt = fast_app()

@rt('/error-handling')
def error_handling():
    return Titled("Error Handling",
        Div(
            # Loading states
            Button(
                "Slow Request",
                hx_get="/slow-request",
                hx_target="#slow-result",
                hx_indicator="#loading-spinner",
                hx_disabled_elt="this"
            ),
            Div(id="loading-spinner", "Loading...", style="display: none;"),
            Div(id="slow-result"),
            
            # Error handling
            Button(
                "Request with Error",
                hx_get="/error-request",
                hx_target="#error-result"
            ),
            Div(id="error-result"),
            
            # Retry mechanism
            Button(
                "Unreliable Request",
                hx_get="/unreliable-request",
                hx_target="#retry-result"
            ),
            Div(id="retry-result")
        )
    )

@rt('/slow-request')
def slow_request():
    import time
    time.sleep(2)  # Simulate slow operation
    return Div("Request completed!", style="color: green;")

@rt('/error-request')
def error_request():
    # Simulate error condition
    return Div(
        "An error occurred!",
        Button(
            "Retry",
            hx_get="/error-request",
            hx_target="#error-result"
        ),
        style="color: red;"
    ), 500  # HTTP 500 error

@rt('/unreliable-request')
def unreliable_request():
    import random
    if random.random() < 0.5:
        return Div("Success!", style="color: green;")
    else:
        return Div(
            "Failed, try again",
            Button(
                "Retry",
                hx_get="/unreliable-request",
                hx_target="#retry-result"
            ),
            style="color: orange;"
        )

Install with Tessl CLI

npx tessl i tessl/pypi-python-fasthtml

docs

application-routing.md

authentication.md

css-styling.md

development-tools.md

form-handling.md

html-components.md

htmx-integration.md

index.md

javascript-integration.md

notifications.md

svg-components.md

tile.json