Skip to content

Optimistic pending: apply loading state instantly on click #155

Description

@adnaan

Problem

When a user clicks a button that triggers Async work, the loading indicator ({{.lvt.Pending}}) only appears after the server round-trip completes and sends back the first WS frame (the "pending render"). On slow connections this creates a perceptible lag where nothing visually changes after the click.

The current flow:

  1. User clicks → client sends WS action message
  2. Server receives, runs the action handler, sets Pending=true, re-renders
  3. Server sends pending-render WS frame (frame Add comprehensive file upload support with S3 integration #1)
  4. Only now does the client show the loading state
  5. Async work completes → server sends completion-render WS frame (frame Add Claude Code GitHub Workflow #2)

Proposal

The client should optimistically apply a CSS class or data attribute (e.g., data-lvt-pending or a class on the triggering element/form) immediately on click, before the WS round-trip. The server's pending-render frame then confirms the state; the completion-render frame clears it.

This makes the loading indicator appear instantly regardless of network latency.

Design questions

  • Scope: Should the pending indicator be per-form (the <form> ancestor of the clicked button), per-element (the clicked <button> itself), or per-page (a global pending state)?
  • Attribute vs class: data-lvt-pending attribute lets CSS target [data-lvt-pending]; a class like lvt-pending is also viable.
  • Clearing: The attribute/class should be cleared when the server's completion render arrives (frame Add Claude Code GitHub Workflow #2). If the server never responds (connection drop), the client's reconnect/error handling should also clear it.

Context

  • livetemplate.Async[S, R] and {{.lvt.Pending}} shipped in livetemplate v0.22.0
  • Server-side Pending is per-render (true on the render that registered Async work, false on all others)
  • See livetemplate/livetemplate#481 for the original boilerplate-reduction investigation
  • Example app: docs/examples/greet-async/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions