FastHTML Examples

repository·main·Indexed 21 days ago

https://github.com/answerdotai/fasthtml-example

A collection of reference applications built with FastHTML demonstrating web development patterns including WebSockets, database integration with SQLite via fastlite, AI model interaction, and custom UI styling using DaisyUI and Tailwind CSS. Includes implementation guides for a Game of Life simulation, a Todo app, and various chatbot demos featuring chunked transfer streaming and real-time synchronization.

Tokens
32.1K
Snippets
122
Records
148
Agent score
75%

What's inside fasthtml-example

  1. Overview of FastHTML example applications

    main

    This repository provides several reference implementations of web applications built with FastHTML to demonstrate different capabilities:

    • Game of Life: Demonstrates real-time updates and multi-client synchronization using WebSockets.
    • Todo App: Demonstrates dynamic UI updates and SQLite database integration.
    • Chatbot: Showcases custom styling using DaisyUI and different patterns for handling chat message updates.
    • Pictionary: Demonstrates integrating multi-modal AI models to process user drawings for continuous captioning.
    • Additional Examples: A collection of smaller proof-of-concept demos and utilities showcasing various FastHTML patterns and techniques.
  2. Overview of the Data Spot Check Demo

    main

    The Data Spot Check Demo is a utility application designed for rapid data inspection before performing model training (e.g., BERT). It allows users to download a sample of a candidate data mix from a Hugging Face dataset and inspect random, unrated samples.

    Users can rate samples as good, ok, or bad. These ratings can subsequently be downloaded as a CSV file. The primary goal is to provide a visual way to get a feel for the contents of a dataset.

  3. Overview of OAuth example implementations

    main

    The oauth_example directory contains three distinct implementations of OAuth patterns:

    • minimal.py: Initializes an OAuth client and retrieves the user's profile, displaying it in the browser after a successful login.
    • oa.py: Demonstrates using the OAuth class to gate access to specific routes (e.g., the homepage) based on user attributes, such as requiring an @answer.ai email address.
    • database.py: A legacy example used for historical OAuth explanations; this is not recommended for modern use.
  4. Code Editor features and limitations

    main

    The Code Editor is a minimal web-based editor implementation using FastHTML with a component-based architecture inspired by React.

    Features:

    • Syntax Highlighting: Supports JavaScript, Python, HTML, and CSS.
    • Autocompletions: Triggered on . (dot) and (space) key down events. Requires a Fireworks API key in your .env file.

    Limitations:

    • Saving: Saving code to a persistent store is not currently implemented.
  5. How CLI OAuth Authentication works with FastHTML

    main

    This implementation enables a seamless CLI-to-Browser authentication flow using a paircode to link the two environments.

    The Authentication Lifecycle

    1. Paircode Generation: The CLI client generates a unique paircode (e.g., using secrets.token_urlsafe(16)) to identify the session.
    2. Handshake: The client sends the paircode to the server's /cli_login endpoint. The server stores this code and returns a login URL containing the paircode as the state parameter.
    3. Browser Flow: The client opens the login URL in the user's browser. The user authenticates via an OAuth provider, which then redirects back to the server's redirect endpoint with an authorization code and the original state (paircode).
    4. Token Association: The server exchanges the code for an authentication ID and associates it with the paircode in its internal store (pc_store).
    5. Polling & Retrieval: The CLI client polls the server's /token endpoint using the paircode. Once the server detects the completed authentication, it returns the session cookies.
    6. Persistence: The client saves these cookies (e.g., to auth_token.txt) and attaches them to subsequent httpx.Client requests to access secured endpoints.
    # Client generates unique identifier
    paircode = secrets.token_urlsafe(16)
    
    # Client polls for the token
    def poll_token(paircode, host, interval=1, timeout=180):
        # ... implementation polls /token?paircode={paircode} ...
        return dict(client.cookies)
  6. Architecture of the XTermJS Terminal Example

    main

    The application implements a browser-based terminal that connects to a real server-side shell using the following stack:

    • FastHTML: The web application framework.
    • XTermJS: Provides the terminal UI in the browser.
    • WebSockets: Enables bidirectional communication between the client and the server.
    • PTY (Pseudo-terminal): Manages the actual shell interaction on the server.

    The logic is split between main.py (server-side PTY and WebSocket handling) and static/terminal.js (client-side XTermJS initialization and WebSocket management).

  7. Retrieve pagination state from query parameters

    main

    When implementing paginated endpoints (like infinite scroll), use request.query_params.get(key, default) to retrieve the current offset or starting index from the URL. This allows the server to calculate the next range of items to fetch.

    start = int(request.query_params.get("start", 21))
  8. Implement real-time synchronization with FastHTML WebSockets

    main

    This project uses FastHTML's @app.ws decorator to manage WebSocket connections for multi-client synchronization.

    Key patterns used:

    • Connection Management: Use on_connect and on_disconnect callbacks to manage a queue of active client connections (player_queue).
    • Out-of-Band (OOB) Swaps: Instead of standard HTMX requests returning HTML to the immediate caller, the server pushes FastHTML components to all connected clients via WebSockets. This ensures that an action taken by one user (e.g., clicking a cell) is reflected on all other users' screens.
    • Background Updates: Use an asyncio background task to continuously evolve the game state and broadcast updates to all players in the player_queue using a function like update_players().
    @app.ws('/gol', conn=on_connect, disconn=on_disconnect)
    def ws(msg:str, send): pass
    
    player_queue = []
    async def on_connect(send): player_queue.append(send)
    async def on_disconnect(send): await update_players()
  9. Implement Infinite Scroll with HTMX and FastHTML

    main

    To implement infinite scrolling, place a sentinel Div at the end of your content list. This Div uses the hx_trigger="intersect once" attribute to detect when it enters the viewport, triggering a request to fetch the next batch of data.

    To optimize for slow-loading content, you can place this sentinel element a few items before the actual end of the list so the next batch begins loading before the user reaches the bottom.

    Key HTMX attributes used:

    • hx_get: The endpoint to fetch the next set of items.
    • hx_trigger="intersect once": Triggers the request when the element becomes visible in the viewport, but only once per element.
    • hx_swap="afterend": Appends the new content immediately after the sentinel element.
    • hx_target="this": Targets the sentinel element itself for the swap operation.
    @app.get("/more-cards")
    def more_cards(request):
        # Get the current count from the query parameters
        start = int(request.query_params.get("start", 21))
        end = start + 20
        
        new_cards = [create_card(i) for i in range(start, end)]
        
        return *new_cards, Div(
                hx_get=f"/more-cards?start={end}",
                hx_trigger="intersect once",
                hx_swap="afterend",
                hx_target="this"
            )
  10. Implement Chunked Transfer for streaming responses

    main

    To stream large responses using Transfer-Encoding: chunked, use the htmx-ext-transfer-encoding-chunked extension.

    1. Setup: Pass exts='chunked-transfer' to the FastHTML constructor.
    2. Form: Add hx_ext="chunked-transfer" to the form.
    3. Server: Return a StreamingResponse from your handler. Use yield to send chunks of HTML (wrapped in to_xml) to the client.

    Example Handler:

    @app.post
    async def send(msg:str, messages:list[str]=None):
        if not messages: messages = []
        messages.append(msg.rstrip())
        return StreamingResponse(stream_response(msg, messages), media_type="text/plain", headers={"X-Transfer-Encoding": "chunked"})
    
    async def stream_response(msg, messages):
        # Initial setup and streaming loop
        yield to_xml(ChatMessage(msg, True, id=len(messages)-1))
        yield to_xml(ChatMessage('', False, id=len(messages)))
        r = (cli(messages, sp=sp, stream=True))
        response_txt = ''
        for chunk in r:
            response_txt += chunk
            yield to_xml(Div(
                response_txt,
                cls=f"chat-bubble chat-bubble-secondary",
                id=f"msg-{len(messages)}-content",
                hx_swap_oob="outerHTML",
            ))
            await asyncio.sleep(0.2)
        # ... (final cleanup yields)
    @app.post
    async def send(msg:str, messages:list[str]=None):
        if not messages: messages = []
        messages.append(msg.rstrip())
        return StreamingResponse(stream_response(msg, messages), media_type="text/plain", headers={"X-Transfer-Encoding": "chunked"})