What is CLS
What is CLS
Cumulative Layout Shift measures the sum of all unexpected layout shifts during page lifecycle.
How CLS is Calculated
CLS = Impact Fraction × Distance Fraction
- Impact Fraction: How much of the viewport is affected
- Distance Fraction: How far the element shifts
Measuring CLS
import { onCLS } from 'web-vitals';
onCLS((metric) => {
console.log('CLS:', metric.value);
console.log('Rating:', metric.rating);
// Get layout shift entries
metric.entries.forEach((entry) => {
console.log('Shift:', entry);
});
});
Thresholds
| Rating | Threshold |
|---|---|
| Good | ≤ 0.1 |
| Needs Improvement | ≤ 0.25 |
| Poor | > 0.25 |
Common Causes
- Images without dimensions
- Ads/embeds with dynamic size
- Dynamically injected content
- Web fonts causing FOIT/FOUT
- Late-loading JavaScript that changes layout
Debugging CLS
- Open Chrome DevTools → Performance panel
- Enable "Layout Shift Regions" in rendering tab
- Record page load
- Yellow highlighted areas show layout shifts
- Use Layout Shift debugger in DevTools
Preventing Layout Shift
Preventing Layout Shift
Set Image Dimensions
// Before: No dimensions
<img src="/photo.jpg" alt="Photo" />
// After: With dimensions
<img
src="/photo.jpg"
alt="Photo"
width={800}
height={600}
style={{ aspectRatio: '800/600' }}
/>
// Responsive images
<img
src="/photo.webp"
srcset="/photo-400.webp 400w, /photo-800.webp 800w"
sizes="(max-width: 768px) 100vw, 50vw"
width={800}
height={600}
alt="Photo"
/>
Reserve Space for Ads
function AdBanner({ ad }) {
return (
<div className="ad-container" style={{ minHeight: 250 }}>
{ad ? (
<img
src={ad.imageUrl}
alt={ad.alt}
width={ad.width}
height={ad.height}
/>
) : (
<div className="ad-placeholder">Advertisement</div>
)}
</div>
);
}
CSS Aspect Ratio
/* Modern approach */
.video-container {
aspect-ratio: 16 / 9;
width: 100%;
}
/* Fallback */
.video-container {
width: 100%;
padding-bottom: 56.25%; /* 9/16 * 100 */
position: relative;
}
.video-container > * {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
Skeleton Screens
function ContentCard({ isLoading, content }) {
if (isLoading) {
return (
<div className="card skeleton" style={{ height: 300 }}>
<div className="skeleton-image" style={{ height: 200 }} />
<div className="skeleton-text" style={{ height: 24, marginTop: 16 }} />
<div className="skeleton-text" style={{ height: 24, width: '60%' }} />
</div>
);
}
return (
<div className="card" style={{ height: 300 }}>
<img
src={content.image}
alt={content.title}
width={400}
height={200}
/>
<h3>{content.title}</h3>
<p>{content.excerpt}</p>
</div>
);
}
content-visibility
/* Skip rendering of off-screen content */
.below-fold {
content-visibility: auto;
contain-intrinsic-size: 0 500px;
}
Font Loading
Font Loading
FOIT vs FOUT
FOIT (Flash of Invisible Text):
- Text is invisible until font loads
- Bad for CLS if layout shifts when font appears
**FOUT (Flash of Unstyled Text):
- Fallback font shown immediately
- Text may shift when custom font loads
font-display
/* Optional: Show fallback, swap when ready */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap;
}
/* Other values */
font-display: auto; /* Browser default */
font-display: block; /* Hide text for 3s */
font-display: swap; /* Show fallback immediately */
font-display: fallback; /* Show fallback, swap if ready quickly */
font-display: never; /* Use fallback only */
Font Preloading
<link rel="preload" href="/fonts/custom.woff2" as="font" type="font/woff2" crossorigin />
Size-Adjust
/* Match fallback font metrics */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap;
size-adjust: 105%;
ascent-override: 90%;
descent-override: 20%;
line-gap-override: 0%;
}
Font Loading Strategy
// Preload critical fonts
document.fonts.preload('/fonts/regular.woff2');
// Wait for fonts before measuring
await document.fonts.ready;
// Check if font is loaded
const isLoaded = document.fonts.check('16px CustomFont');
CLS Prevention Checklist
- Set width/height on all images
- Use aspect-ratio for containers
- Reserve space for ads/embeds
- Use font-display: swap
- Preload critical fonts
- Use skeleton screens
- Avoid dynamically injecting content above the fold
Practice Problems
Create a reusable React component implementing Cumulative Layout Shift. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Cumulative Layout Shift using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Cumulative Layout Shift for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What is a good CLS score?
2. What does CLS measure?
3. What is FOIT?
4. How does font-display: swap help CLS?
5. Why should images always have width and height?
Flashcards
Question
What is CLS?
Click to reveal answer
Answer
Cumulative Layout Shift - measures the sum of unexpected layout shifts during page lifecycle.
Question
What is FOIT?
Click to reveal answer
Answer
Flash of Invisible Text - text hidden until custom font loads.
Question
What is font-display: swap?
Click to reveal answer
Answer
A CSS property that shows fallback text immediately and swaps to custom font when ready.
Question
Why do images need width and height?
Click to reveal answer
Answer
To allow the browser to reserve space and prevent layout shifts when images load.
Question
What is Cumulative Layout Shift?
Click to reveal answer
Answer
Cumulative Layout Shift is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.CLS measures visual stability with a good threshold of ≤ 0.1
- 2.Always set dimensions on images to reserve space
- 3.Use font-display: swap to prevent FOIT
- 4.Reserve space for ads and dynamically loaded content
- 5.Skeleton screens help maintain layout stability
Interview Tips
- •Explain what CLS measures and why it matters
- •Discuss techniques for preventing layout shift
- •Know the difference between FOIT and FOUT
Cheat Sheet
CLS Cheat Sheet
Threshold
- Good: ≤ 0.1
- Needs Improvement: ≤ 0.25
- Poor: > 0.25
Prevention
- Set width/height on images
- Use aspect-ratio for containers
- Reserve space for ads/embeds
- Use font-display: swap
- Preload critical fonts
- Use skeleton screens
Common Causes
- Images without dimensions
- Ads with dynamic size
- Late-loading fonts
- Dynamic content injection