Lazy images with IntersectionObserver, a reserved ratio and blur-up
Each frame reserves its 16:9 space from the start and shows a colour preview. The real image is requested only when its frame comes within 100px of the scroll box's visible area, then fades in. Scroll the gallery and watch the images appear.
A fast-feeling page loads what the user sees first and delays everything else: images below the fold load when they come near, placeholders and reserved space stop the page from jumping, skeletons show the shape of what's coming, rarely used components load on demand, likely next steps are prefetched, and non-critical work waits until the browser is idle.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 6 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write a useInView(ref, rootMargin) hook that returns true once the element has come near the viewport, and use it to lazy-render a heavy map component.
Add width, height and lazy loading to a list of product images so the page doesn't shift.
Explain it without notes
What are LCP, CLS and INP, and which loading techniques improve each?
How would you implement lazy-loaded images, and when would you not use loading="lazy"?
Skeletons or spinners: when do you use each?
How do you make a code-split component feel instant?
Preview
Console
Nothing logged yet.Expected console output
Images requested at the top: 3 of 12
After scrolling to 600px: 7 of 12