Lab
Performance Audit & Fix
25 min
Intermediate3 Free Attempts
Instructions
Objective
You are given a React application with severe performance problems. Your task is to identify and fix issues affecting all three Core Web Vitals: LCP, CLS, and INP.
The Problem Application
The application is a product listing page with the following deliberate performance issues:
LCP Issues
- Hero image is a 2MB unoptimized PNG with no width/height attributes
- Three render-blocking Google Fonts loaded via
<link>in the head - All CSS is in a single large bundle (no critical CSS extraction)
- No preloading of the hero image
CLS Issues
- Product images have no width/height or aspect-ratio set
- A promotional banner is dynamically injected after 2 seconds
- Web font swap causes visible layout shift (no
size-adjust) - An ad placeholder has no reserved height
INP Issues
- Product filtering runs a synchronous loop over 10,000 items on every keystroke
- Sort function blocks the main thread for 300ms+
- Click handlers trigger synchronous layout recalculations (forced reflow)
Your Tasks
Task 1: Fix LCP (35 points)
- Convert the hero image to use
<picture>with AVIF/WebP sources and a JPG fallback - Add
width,height, andfetchpriority="high"to the hero image - Add
<link rel="preload">for the hero image - Use
font-display: swapfor custom fonts and preload the primary font file - Extract critical above-the-fold CSS into an inline
<style>block
Task 2: Fix CLS (30 points)
- Add
widthandheightattributes (or CSSaspect-ratio) to all product images - Reserve space for the promotional banner using
min-heightbefore it loads - Declare a second
@font-facefor the fallback (src: local(...)) carryingsize-adjust,ascent-override, anddescent-overridetuned to BrandFont's metrics, and put it in thefont-familystack ahead of the generic. Do not put those descriptors on BrandFont's own rule — that changes how BrandFont renders instead of matching the fallback to it - Set a fixed
min-heighton the ad placeholder container
Task 3: Fix INP (35 points)
- Debounce the search/filter input with a 300ms delay
- Break the product filtering into chunks using
setTimeout(fn, 0)orscheduler.yield()to yield to the main thread - Move the sort comparison into a Web Worker or break it into yielding chunks
- Reorder the click handler so the
offsetHeightread happens before any style write, then apply the writes (arequestAnimationFramecallback is a good place for them). Reordering is what removes the forced layout; rAF alone does not
Starter Code
The starter code below represents the broken application. Fix the issues in place.
Evaluation Criteria
Your submission will be evaluated on whether the fixes correctly address each Core Web Vital issue. You do not need to run Lighthouse — the grading rubric checks your code for the specific optimization patterns.
What to Submit
Your submission should contain 1 file section in the editor below: the fixed React application code with all performance issues resolved.
Grading Rubric
LCP Fix — Hero image uses <picture> with AVIF/WebP sources, has width/height and fetchpriority='high', a <link rel='preload'> is added, fonts use font-display: swap and primary font is preloaded, critical CSS is inlined35 points
CLS Fix — Product images have width/height or aspect-ratio, promo banner container has min-height to reserve space even before content loads, a SEPARATE fallback @font-face (src: local(...)) carries size-adjust/ascent-override/descent-override and appears in the font-family stack (these descriptors on BrandFont's own rule is incorrect and scores zero for this item), ad placeholder has a fixed min-height30 points
INP Fix — Search input is debounced (300ms), product filtering uses chunked processing with setTimeout or scheduler.yield to avoid blocking, forced reflow is eliminated by moving the offsetHeight read BEFORE the style writes (wrapping the same read-after-write order in requestAnimationFrame does not fix it and scores zero for this item)35 points
Checklist
0/12Your Solution
3 free attempts remaining