What is Repaint
Repaint (also called redraw) happens when visual changes occur that don't affect layout. The browser redraws affected pixels.
Layout vs Repaint
// Layout (Reflow): Geometry changes
// - Triggers reflow + repaint
element.style.width = '100px';
element.style.position = 'absolute';
element.style.margin = '10px';
// Repaint only: Visual changes
// - Triggers repaint only (no layout)
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.boxShadow = '0 0 5px black';
element.style.opacity = '0.5';
element.style.visibility = 'hidden';
What Triggers Repaint
// Color changes
element.style.color = 'red';
element.style.backgroundColor = '#fff';
element.style.borderColor = 'blue';
// Visibility
element.style.visibility = 'hidden';
element.style.opacity = '0.5';
element.style.display = 'none'; // Also triggers reflow!
// Shadows
element.style.boxShadow = '0 0 5px black';
element.style.textShadow = '1px 1px 2px black';
// Backgrounds
element.style.backgroundImage = 'url(...)';
element.style.backgroundGradient = 'linear-gradient(...)';
element.style.backgroundSize = 'cover';
// Borders
element.style.borderWidth = '2px';
element.style.borderStyle = 'solid';
element.style.borderRadius = '5px';
// Text
element.style.fontSize = '16px'; // May trigger reflow
element.style.fontWeight = 'bold';
element.textContent = 'New text'; // May trigger reflow
What Doesn't Trigger Repaint
// transform: composite only
element.style.transform = 'translateX(100px)';
element.style.transform = 'rotate(45deg)';
element.style.transform = 'scale(1.5)';
// opacity: may composite
element.style.opacity = '0.5'; // Can be composited
// will-change: promotes to layer
.element { will-change: transform; }
Repaint Triggers
Different types of changes trigger different levels of work.
Change Categories
┌─────────────────────────────────────────────────┐
│ Change Type │
├─────────────────────────────────────────────────┤
│ Layout + Paint + Composite │
│ - width, height, position, margin, padding │
│ - display, font-size, float │
│ - border, box-sizing │
├─────────────────────────────────────────────────┤
│ Paint + Composite (no layout) │
│ - color, background-color │
│ - visibility, opacity │
│ - box-shadow, text-shadow │
│ - border-color, border-style │
│ - background-image │
├─────────────────────────────────────────────────┤
│ Composite only (no layout, no paint) │
│ - transform │
│ - opacity (GPU-composited) │
│ - will-change │
└─────────────────────────────────────────────────┘
Performance Impact
// Layout: Most expensive
// - Recalculates geometry
// - Invalidates paint
// - May trigger repaint
element.style.width = '100px'; // Layout + Paint
// Paint: Medium expensive
// - Fills in pixels
// - Can be expensive for large areas
element.style.color = 'red'; // Paint only
// Composite: Cheapest
// - Combines layers
// - GPU-accelerated
element.style.transform = 'translateX(100px)'; // Composite only
Identifying Repaints
// Chrome DevTools: Rendering tab
// - Enable 'Paint flashing' (green overlay)
// - Shows areas being repainted
// Chrome DevTools: Performance tab
// - Record performance
// - Look for 'Paint' events
// - Check duration
// Performance.now() for timing
const start = performance.now();
// ... changes ...
const end = performance.now();
console.log(`Took ${end - start}ms`);
Example: Hover Effect
// BAD: Triggers layout + paint
element.addEventListener('mouseenter', () => {
element.style.width = '120px'; // Layout
element.style.padding = '20px'; // Layout
element.style.color = 'red'; // Paint
});
// BETTER: Paint only
element.addEventListener('mouseenter', () => {
element.style.color = 'red'; // Paint
element.style.boxShadow = '0 0 10px red'; // Paint
});
// BEST: Composite only
element.addEventListener('mouseenter', () => {
element.style.transform = 'scale(1.1)'; // Composite
element.style.opacity = '0.9'; // Composite
});
Performance Tips
Tips to minimize repaints and improve rendering performance.
1. Use transform Instead of top/left
// BAD: Triggers layout + paint
element.style.top = '100px';
element.style.left = '100px';
// BEST: Composite only
element.style.transform = 'translate(100px, 100px)';
2. Use opacity Instead of visibility
// BAD: Triggers repaint
element.style.visibility = 'hidden';
// BETTER: Can be composited
element.style.opacity = '0';
3. Promote to Compositor Layer
/* Use will-change for animated elements */
.animated {
will-change: transform, opacity;
}
/* But don't overuse! */
.animated:hover {
will-change: transform; /* Bad! */
}
4. Batch Style Changes
// BAD: Multiple repaints
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.boxShadow = '0 0 5px black';
// BETTER: Single repaint
element.style.cssText = 'color: red; background: blue; box-shadow: 0 0 5px black;';
// OR: Use CSS class
.element.modified {
color: red;
background: blue;
box-shadow: 0 0 5px black;
}
element.classList.add('modified');
5. Use CSS Containment
/* Tell browser to isolate changes */
.container {
contain: layout style paint;
}
/* Types of containment:
* layout: internal layout changes don't affect outside
* style: counters and quotes are scoped
* paint: element clips descendants
* size: element size is independent of children
* content: shorthand for layout style paint
*/
6. Reduce Paint Area
/* Bad: Large painted area */
.hero {
background: linear-gradient(...);
}
/* Better: Smaller area */
.hero::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(...);
}
7. Use Content-Visibility
/* Skip rendering off-screen content */
.offscreen {
content-visibility: auto;
contain-intrinsic-size: 0 500px;
}
8. Debounce Frequent Updates
// BAD: Many repaints
element.addEventListener('mousemove', (e) => {
element.style.left = `${e.clientX}px`;
});
// GOOD: Throttled
element.addEventListener('mousemove', throttle((e) => {
element.style.transform = `translateX(${e.clientX}px)`;
}, 16)); // ~60fps
Practice Problems
Create a reusable React component implementing Repaint. 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 Repaint using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Repaint 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 triggers repaint but NOT layout?
2. What is the cheapest way to animate?
3. What does will-change do?
4. How do you identify repaints in Chrome DevTools?
Flashcards
Question
What is repaint?
Click to reveal answer
Answer
Browser redraws affected pixels when visual changes occur (color, visibility, shadows) without affecting layout.
Question
What triggers repaint but not layout?
Click to reveal answer
Answer
color, background-color, visibility, opacity, box-shadow, text-shadow, border-color.
Question
What is the cheapest way to animate?
Click to reveal answer
Answer
Using transform (composite only). Also opacity. Both are GPU-accelerated.
Question
What does will-change do?
Click to reveal answer
Answer
Promotes element to its own compositor layer, improving animation performance.
Question
What is Repaint?
Click to reveal answer
Answer
Repaint is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Repaint redraws pixels without affecting layout
- 2.Color, visibility, shadows trigger repaint only
- 3.transform and opacity are cheapest (composite only)
- 4.Use will-change to promote to compositor layer
- 5.Batch style changes to minimize repaints
Interview Tips
- •Explain the difference between layout and repaint
- •Show how to identify repaints in DevTools
- •Discuss when to use transform vs top/left
- •Explain CSS containment and its benefits
Cheat Sheet
Repaint Cheat Sheet
What is it?
Redrawing pixels when visual changes occur (no layout change).
Triggers
- color, background-color
- visibility, opacity
- box-shadow, text-shadow
- border-color, border-style
- background-image
No Layout, No Paint
- transform (composite only)
- opacity (can be composited)
- will-change
Performance Tips
- Use transform instead of top/left
- Use opacity instead of visibility
- Use CSS classes for style changes
- Promote animated elements with will-change
- Use contain for isolation
- Debounce frequent updates
Tools
- Chrome: Paint flashing
- Chrome: Performance timeline
- performance.now()