Loading states are present for async content (skeletons, spinners)
Why it matters
Async components that render blank space while fetching data feel broken to users — they perceive the app as frozen, tap repeatedly, or abandon the page before content arrives. On 3G and flaky mobile networks the gap between navigation and first paint can stretch to several seconds, directly degrading Interaction to Next Paint and Cumulative Layout Shift (Core Web Vitals that feed Google ranking signals). Missing loading states also mask real failures: a stalled fetch looks identical to a successful empty state, so users cannot tell whether to wait or retry.
Severity rationale
Low because the UX feels sluggish but functionality still works once data eventually arrives.
Remediation
Wrap every async component with a loading state — either an isLoading guard returning a skeleton, or a <Suspense> boundary with a fallback. Skeleton screens that mirror the final layout reduce perceived latency more than generic spinners. Example:
import { Suspense } from 'react'
export default function Page() {
return (
<Suspense fallback={<CommentSkeleton count={3} />}>
<CommentList />
</Suspense>
)
}
Detection
-
ID:
loading-states -
Severity:
low -
What to look for: Count all components that fetch data asynchronously (using
useSWR,useQuery,fetchinuseEffect,Suspenseboundaries, or equivalent). For each async component, check for loading states: skeleton screens, spinners,Suspensefallbacks, orisLoadingguards. Enumerate: "X async-loading components found, Y have loading states." -
Pass criteria: At least 80% of components with async data fetching show loading states (skeletons, spinners, Suspense fallbacks). No more than 1 async component lacks a loading state. The UX is smooth even on 3G networks — no page goes blank while waiting for data. Report: "X of Y async components have loading states."
-
Fail criteria: 2 or more components with async data fetching lack loading states. Content goes from blank to fully rendered with no visual transition, or the page shows a blank screen while fetching.
-
Skip (N/A) when: No async content is loaded from the client side (0 client-side data fetching patterns detected — no
useSWR,useQuery,fetchinuseEffect, orSuspenseboundaries). -
Detail on fail:
"2 of 5 async components lack loading states — dashboard grid and comments section show blank content until data arrives"or"0 of 3 Suspense boundaries have fallback components — page flashes blank during data fetch" -
Remediation: Add loading states:
// Before — no loading state export default function Comments() { const { data } = useSWR('/api/comments') return <>{data?.map(c => <Comment {...c} />)}</> } // After — with loading state export default function Comments() { const { data, isLoading } = useSWR('/api/comments') if (isLoading) return <CommentSkeleton count={3} /> return <>{data?.map(c => <Comment {...c} />)}</> } // Next.js Suspense export default async function Comments() { return ( <Suspense fallback={<CommentSkeleton count={3} />}> <CommentList /> </Suspense> ) }
Taxons
History
- 2026-04-18·v1.0.0·Initial import from performance-load·automated