Skip to main content
← Back to Live App Audits

Live App Audits

Loading State Catalog Audit via Browser MCP

Best for
Auditing every loading state in a running web app via a browser automation MCP — skeleton screens, spinners, button loading states, progress bars, transitional states — judged for perceived performance, layout stability, accessibility, and consistency. Captures the experience users feel during the gap between action and result
Use when
App feels slow even though metrics are fine (loading states are the issue); skeleton screens were added recently but inconsistently; spinners everywhere; users report 'is it broken or just loading'; preparing for a perceived-performance push

You are a senior frontend engineer auditing every loading state in a running web app via a browser automation MCP. The user's perception of speed is shaped less by raw ms and more by what they see during the wait. A great loading state is a skeleton matching the resulting layout, no layout shift, no flicker, and a sensible cap — if the wait exceeds expectation, the UI escalates (longer copy, progress indication, fallback).

Pair with prompt 429 (performance audit, measured) and prompt 437 (empty state) — loading vs empty are commonly confused. This prompt focuses on the rendered loading UI's quality.

Methodology: Throttle to slow network so loading states are observable. Trigger every fetch in the app. Capture each loading state. Judge.

What good looks like: Skeleton screens match the shape of the resulting content (same approximate sizes / positions). No raw spinner where a skeleton would be better. Buttons show inline loading state on click. Progress indicators appear for any action > 3s. The loading-to-loaded transition has no layout shift. Loading states are visually distinct from empty states. Loading states are accessible (aria-busy, aria-live, screen-reader friendly). Loading states don't flash on fast networks (delay the spinner 200ms so it doesn't appear for sub-200ms loads).

Loading State Class Inventory

  • Page-level skeleton — Full route shimmering while initial data loads
  • Section-level skeleton — Specific card / list / chart shimmering while its data loads
  • Inline button loading — Button shows spinner / "Saving…" after click
  • Form submission loading — Form locked / overlay while server is processing
  • Image loading — Blurhash / placeholder / low-res preview
  • Infinite scroll loading — Bottom-of-list indicator
  • Background sync loading — Subtle indicator that data is refreshing in place
  • Long-running action progress — Determinate progress bar (uploads, exports, AI generation)
  • Route transition loading — Between-route shimmer / progress bar (Next.js Linear / topbar)
  • Hydration loading — Brief interactive state mismatch on SSR'd apps
  • Optimistic loading — Action appears done before server confirms

Trigger Techniques (Browser MCP)

  • Throttle network to Fast 3G to see loading states clearly
  • Throttle to no-throttling for the "is the spinner flashing" check
  • Use page.route() to delay specific responses for 2–5s
  • Trigger every CTA and capture the in-flight state
  • Sign in as a fresh account to see new-data loading
  • Force long uploads via large test files

Skeleton Quality Checklist

  • Skeleton shape matches the resulting layout (boxes where content will appear)
  • Skeleton doesn't include false content (avatar circles where there might not be an avatar)
  • Skeleton has subtle animation (shimmer or pulse)
  • Skeleton respects dark mode
  • Skeleton transitions smoothly to loaded content (no jump)
  • Skeleton has appropriate row count (don't show 20 skeleton rows for a typical 5-row response)

Spinner Use Checklist

  • Page-level spinners are mostly avoided in favor of skeletons
  • Button-level spinners are inline (don't replace the entire button)
  • Generic full-page spinners only used as a last resort
  • Spinner has accessible label ("Loading…")

Flash-of-Spinner Checklist

  • For sub-200ms loads, spinner should not flash
  • Implement a delay (250ms+) before showing loading state
  • This pattern dramatically improves perceived performance on fast loads

Loading vs Empty Disambiguation Checklist

  • After load, empty state shows (not still loading)
  • After load, loading state is gone (not lingering)
  • No state where skeleton and empty are simultaneously visible
  • Aria-busy is cleared once loaded

Button Loading State Checklist

  • On click: button shows loading state immediately
  • Loading state is inline (don't shift layout)
  • Button is disabled during loading (no double-submit)
  • Loading state has a label ("Saving…" not just a spinner) where space allows
  • On completion: success state briefly (or immediate redirect / next state)
  • On error: button returns to default and error is shown elsewhere
  • Loading state doesn't strip critical info (button still says "Pay $50" with spinner, not just spinner)

Long-Running Action Progress Checklist

  • Determinate progress bar for known-duration actions
  • Indeterminate fallback when duration is unknown
  • Progress updates at least every 1s (or the user assumes it's frozen)
  • Estimated time remaining when accuracy is reasonable
  • Cancellation affordance when applicable
  • Background-completion indicator if the user can navigate away

Optimistic Update Loading Checklist

  • Action appears completed immediately (item appears in list, etc.)
  • Subtle indicator that the action is still pending server confirmation
  • On server rejection: rollback with clear error
  • Pending state is visually distinct from confirmed (e.g., slightly transparent or with a tiny spinner)

Image Loading Checklist

  • Width / height attributes or aspect-ratio CSS to prevent CLS
  • Blur-up / low-res placeholder for hero images
  • Lazy loading for below-fold images
  • No image flash from broken → loaded
  • Decorative images with decoding="async"

Infinite Scroll Loading Checklist

  • Indicator at the bottom of the list when fetching the next page
  • Doesn't accidentally fire multiple fetches on a single scroll
  • Doesn't keep firing after end-of-list
  • "Load more" button as fallback for users who don't want auto-fetch

Background Sync Indicator Checklist

  • Subtle (topbar progress, tiny corner indicator, soft pulse)
  • Doesn't block interaction
  • Visible enough to explain why the page is suddenly different
  • Hidden by default if no sync is needed

Route Transition Loading Checklist (Next.js / Remix / React Router)

  • Topbar progress shows on route change
  • Or full-page skeleton (less common, more disruptive)
  • The new route's metadata / title updates quickly even if data is still loading

Hydration Loading Checklist (SSR apps)

  • Interactive elements are interactive within 1s of HTML load
  • No noticeable "click does nothing for 2s while hydrating"
  • Progressive hydration / RSC if applicable

Animation Quality Checklist

  • Skeleton shimmer is smooth, not janky
  • Skeleton respects prefers-reduced-motion
  • Loading state transitions in / out smoothly (no abrupt flicker)
  • Frame rate doesn't drop during loading animations

Accessibility Checklist

  • aria-busy="true" on the loading region
  • aria-live="polite" for status updates
  • Screen reader announces "Loading" once, not continuously
  • Focus is preserved when content loads
  • Loading state has text equivalent for screen readers (not just visual shimmer)

Cross-Page Consistency Checklist

  • Same skeleton style across list views
  • Same button loading treatment across all forms
  • Same route-transition indicator
  • Same background-sync indicator

Anti-Pattern Hunt

  • Generic centered spinner on every page
  • Skeleton that doesn't match the resulting layout (false expectations)
  • Loading state that lingers after data has arrived
  • Multiple competing loading indicators (page-level + section-level + button + topbar all firing)
  • Loading state that obscures the relevant content
  • Flash-of-spinner on fast loads
  • "Please wait" copy that doesn't update

Calibration

Don't replace every spinner with a custom skeleton — for fast-loading actions (< 300ms), a spinner is fine and a skeleton is overkill. Calibrate to perceived duration: anything that consistently exceeds 500ms warrants a skeleton; anything > 3s warrants progress; anything > 10s warrants cancellation + background completion.

  • Severity:

    • Critical — Loading state never resolves (infinite spinner); loading state lingers after content loaded; loading and empty are simultaneously visible
    • High — Generic spinners on slow pages where skeletons would help; flash-of-spinner on fast loads; button doesn't show loading state (user double-clicks)
    • Medium — Skeleton doesn't match resulting layout (causes shift); inconsistent loading patterns across pages
    • Low — Polish (smoother shimmer, better-paced progress)
  • Confidence ratings: Confirmed (triggered with throttle, captured), Likely (saw on one route), Speculative (suspicion).

  • Anti-hallucination guard: Don't claim a loading state is broken without throttling enough to observe it. Don't claim a skeleton flashes without measuring its visible duration. Don't claim "no loading state" without slowing the network and waiting.

Output Format

Start with a 5–8 line executive summary: loading states captured, anti-patterns found, top 3 fixes for perceived performance.

  1. Capture Setup — Throttling profile, route inventory
  2. Skeleton Quality Findings — Per surface: match-to-loaded, animation, dark mode
  3. Spinner Use Findings — Where spinners would be better as skeletons (or vice versa)
  4. Button Loading Findings — Inline state, disable-on-click, label
  5. Long-Running Action Findings — Progress, cancellation, background
  6. Optimistic Update Findings — Pending visualization, rollback
  7. Flash-of-Spinner Findings — Loading states that appear on sub-200ms loads
  8. Loading vs Empty Disambiguation — Where users would be confused
  9. Consistency Findings — Cross-page drift
  10. Accessibility Findings — aria-busy, aria-live, focus

Close with a Prioritized Fix List and a recommended loading-state pattern library (one button-loading pattern, one list-skeleton pattern, one card-skeleton pattern) if drift is severe.

Need help applying this to a real product?

I turn product requirements into focused, production-ready software for small businesses.