Async Loading Container

Pattern for asynchronous content loading with skeleton loaders and loading indicators

Loading Indicator Types

The LoadingType enum defines the types of loading indicators available.


source

LoadingType


def LoadingType(
    args:VAR_POSITIONAL, kwds:VAR_KEYWORD
):

Types of loading indicators for async content.

AsyncLoadingContainer Function

The AsyncLoadingContainer function creates a container that displays a loading indicator and asynchronously loads content from a URL using HTMX.

Key features: - Displays loading indicator initially - Loads content via HTMX when triggered - Replaces itself with loaded content (outerHTML swap) - Customizable loading indicators - Support for skeleton loaders - Optional custom styling


source

AsyncLoadingContainer


def AsyncLoadingContainer(
    container_id:str, # HTML ID for the container
    load_url:str, # URL to fetch content from
    loading_type:LoadingType=<LoadingType.SPINNER: 'spinner'>, # Type of loading indicator
    loading_size:str='lg', # Size of loading indicator (xs, sm, md, lg)
    loading_message:Optional=None, # Optional message to display while loading
    skeleton_content:Optional=None, # Optional skeleton/placeholder content
    trigger:str='load', # HTMX trigger event (default: load on page load)
    swap:str='outerHTML', # HTMX swap method (default: replace entire container)
    container_cls:Optional=None, # Additional CSS classes for container
    kwargs:VAR_KEYWORD
)->FT: # Div element with async loading configured

Create a container that asynchronously loads content from a URL.

Usage Examples

Here are complete examples showing different use cases:

from cjm_fasthtml_daisyui.components.data_display.card import card
from cjm_fasthtml_daisyui.utilities.semantic_colors import bg_dui

# Example 1: Simple spinner loader for a dashboard card
simple_loader = AsyncLoadingContainer(
    container_id="stats-card",
    load_url="/api/dashboard/stats",
    loading_message="Loading statistics...",
    container_cls=str(combine_classes(card, bg_dui.base_100))
)

print("Example 1: Simple spinner loader")
print(simple_loader)
Example 1: Simple spinner loader
stats-card
# Example 2: Skeleton loader for card content
from cjm_fasthtml_daisyui.components.feedback.skeleton import skeleton

skeleton_card = Div(
    Div(cls=combine_classes(skeleton, "h-32", "w-full", m.b(4))),
    Div(cls=combine_classes(skeleton, "h-4", "w-full", m.b(2))),
    Div(cls=combine_classes(skeleton, "h-4", "w-3/4")),
    cls=str(p(4))
)

skeleton_loader = AsyncLoadingContainer(
    container_id="user-profile",
    load_url="/api/user/profile",
    loading_type=LoadingType.NONE,
    skeleton_content=skeleton_card,
    container_cls=str(combine_classes(card, bg_dui.base_100))
)

print("Example 2: Skeleton loader")
print(skeleton_loader)
Example 2: Skeleton loader
user-profile
# Example 3: Different loading indicator styles
print("Example 3: Different loading styles")

for loading_style in [LoadingType.SPINNER, LoadingType.DOTS, LoadingType.RING]:
    loader = AsyncLoadingContainer(
        container_id=f"content-{loading_style.value}",
        load_url="/api/content",
        loading_type=loading_style,
        loading_size="md"
    )
    print(f"  {loading_style.value}: Created")
Example 3: Different loading styles
  spinner: Created
  dots: Created
  ring: Created
# Example 4: Lazy loading with intersection observer
lazy_loader = AsyncLoadingContainer(
    container_id="lazy-content",
    load_url="/api/heavy-content",
    trigger="intersect once",  # Load only when scrolled into view
    loading_type=LoadingType.SPINNER,
    loading_message="Loading when visible..."
)

print("Example 4: Lazy loading")
print(lazy_loader)
Example 4: Lazy loading
lazy-content

Server-Side Response

The endpoint specified in load_url should return the complete content that will replace the loading container. Since the default swap is outerHTML, the returned content should include the container with the same ID:

@app.get("/api/dashboard/stats")
def get_stats():
    # Load your data
    stats = get_statistics()
    
    # Return complete card with same ID
    return Div(
        H3("Statistics"),
        P(f"Total: {stats['total']}"),
        P(f"Active: {stats['active']}"),
        id="stats-card",  # Same ID as the loading container
        cls=combine_classes(card, card_body, bg_dui.base_100)
    )

Alternatively, use swap="innerHTML" to replace only the container’s contents:

# Container
AsyncLoadingContainer(
    container_id="stats-card",
    load_url="/api/dashboard/stats",
    swap="innerHTML",
    container_cls=str(combine_classes(card, card_body, bg_dui.base_100))
)

# Endpoint - no need to include container
@app.get("/api/dashboard/stats")
def get_stats():
    stats = get_statistics()
    return Div(
        H3("Statistics"),
        P(f"Total: {stats['total']}"),
        P(f"Active: {stats['active']}")
    )