April 15, 2025

Reflow, Repaint, Layout Shift là gì? Tối ưu để tránh CLS cao trong Core Web Vitals
Tìm hiểu về Reflow, Repaint và Layout Shift – ba khái niệm quan trọng ảnh hưởng đến trải nghiệm người dùng và điểm Core Web Vitals của trang web bạn.
Vì sao bạn cần hiểu Reflow, Repaint và Layout Shift?
Khi xây dựng giao diện web, đặc biệt là các ứng dụng SPA hay các trang thương mại điện tử, bạn thường tối ưu hình ảnh, bundle nhỏ gọn, code chia nhỏ... Nhưng có một thứ tinh tế mà đôi khi chúng ta bỏ qua: trải nghiệm người dùng khi trang đang render.
Các hiện tượng như:
- Giao diện bị “nhảy” khi ảnh vừa tải xong
- Văn bản tự động bị đẩy xuống vì banner xuất hiện chậm
- Người dùng định click thì nút trượt sang chỗ khác
Tất cả đều liên quan đến Reflow, Repaint, và Layout Shift. Và những điều này ảnh hưởng trực tiếp đến CLS – Cumulative Layout Shift, một chỉ số quan trọng trong Core Web Vitals của Google.
Reflow là gì?
Reflow (hay còn gọi là Layout) là quá trình trình duyệt tính toán lại kích thước và vị trí của các phần tử trên trang.
Reflow xảy ra khi:
- Thêm, xóa hoặc thay đổi phần tử DOM
- Thay đổi các thuộc tính CSS như
width
,font-size
,position
,display
... - Thay đổi nội dung text
- Kích thước cửa sổ trình duyệt thay đổi (resize)
Ví dụ:
element.style.width = "200px";
Thao tác này yêu cầu trình duyệt tính toán lại bố cục các phần tử liên quan – đó chính là Reflow.
Reflow rất tốn kém tài nguyên. Một thay đổi nhỏ có thể ảnh hưởng đến nhiều phần tử, đặc biệt trong layout phức tạp.
Repaint là gì?
Repaint là quá trình vẽ lại phần tử trên màn hình mà không cần tính toán lại layout.
Repaint xảy ra khi:
- Thay đổi màu sắc (
background-color
,color
) - Thay đổi
visibility
- Đổ bóng, viền, opacity…
Ví dụ:
element.style.backgroundColor = "blue";
Không cần tính lại bố cục, chỉ cần tô lại màu => Repaint.
So với Reflow, Repaint nhẹ hơn. Tuy nhiên nếu diễn ra liên tục (ví dụ animation không tối ưu), vẫn gây drop FPS và lag.
Layout Shift là gì?
Layout Shift xảy ra khi một phần tử bất ngờ thay đổi vị trí trên màn hình mà người dùng không tương tác gì cả. Khi điều này xảy ra quá nhiều hoặc quá trễ, trải nghiệm người dùng sẽ bị ảnh hưởng mạnh mẽ.
Đây chính là thứ mà CLS – Cumulative Layout Shift đo lường.
Một số nguyên nhân phổ biến:
- Ảnh chưa có kích thước trước khi tải => ảnh “đẩy” phần tử khác xuống
- Font hiển thị muộn => văn bản bị dịch chuyển (FOIT/FOUT)
- Quảng cáo, popup render trễ => đẩy nội dung đi
- Sử dụng JavaScript thêm nội dung bất ngờ vào DOM
Cách đo CLS (Cumulative Layout Shift)
CLS đo bằng công thức:
CLS = Tổng Layout Shift Score = Impact Fraction * Distance Fraction
Nói đơn giản:
- Impact Fraction: bao nhiêu phần trăm vùng màn hình bị thay đổi
- Distance Fraction: khoảng cách phần tử đã bị dịch chuyển bao nhiêu
Google khuyến nghị: CLS < 0.1 để đạt tiêu chuẩn tốt cho Core Web Vitals.
Cách giảm Reflow và Repaint không cần thiết
- Tránh thao tác DOM liên tục
// KHÔNG nên
for (let i = 0; i < 100; i++) {
document.body.style.margin = `${i}px`;
}
// NÊN
document.body.style.margin = "100px";
- Dùng class thay vì thay đổi style trực tiếp
element.classList.add("expanded"); // Gọn hơn, dễ maintain
- Đọc và ghi DOM riêng biệt
const height = element.offsetHeight; // Đọc layout
element.style.height = height + 10 + "px"; // Ghi layout
// Gộp nhiều thay đổi vào cùng một frame
requestAnimationFrame(() => {
// Thay đổi nhiều thứ tại đây
});
Cách tối ưu để tránh CLS cao
1. Luôn đặt chiều cao cố định cho ảnh
<img src="/logo.png" width="200" height="100" alt="Logo" />
Hoặc dùng CSS:
img { aspect-ratio: 2 / 1; }
Next.js dùng
next/image
sẽ tự động xử lý việc này.
2. Tránh font nhảy – preload font sớm
<link rel="preload" href="/fonts/custom.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
Hoặc dùng font-display: swap
để tránh chặn render quá lâu.
3. Đặt chỗ sẵn cho component động (banner, quảng cáo...)
Nếu biết chiều cao của một thành phần sẽ xuất hiện muộn, hãy dành sẵn khoảng trống trong layout:
.banner-placeholder { min-height: 120px; }
4. Tránh chèn DOM bất ngờ từ JS
Hạn chế setTimeout
để render component động sau vài giây. Nếu cần animation, dùng opacity
, transform
để transition mượt mà mà không gây layout shift.
Tổng kết
Reflow, Repaint và Layout Shift là ba yếu tố quan trọng ảnh hưởng trực tiếp đến trải nghiệm người dùng – và đặc biệt là chỉ số CLS trong Core Web Vitals.
Việc hiểu cách trình duyệt hoạt động giúp bạn:
- Tối ưu hiệu suất mà không cần chỉ nhìn vào Lighthouse
- Tránh “trượt điểm” SEO vì lỗi UX
- Làm chủ frontend một cách chuyên nghiệp hơn
Hãy bắt đầu tối ưu từ những điều nhỏ: đặt kích thước ảnh, preload font, tránh layout shift không cần thiết. Dần dần, bạn sẽ thấy trang web mượt mà hơn, nhanh hơn – và người dùng hài lòng hơn.
What are Reflow, Repaint, and Layout Shift? Optimizing to Reduce CLS in Core Web Vitals
Understand the critical rendering concepts of Reflow, Repaint, and Layout Shift that impact web performance and how to minimize CLS.
Why should you understand Reflow, Repaint, and Layout Shift?
When building a website UI—especially in modern SPAs or e-commerce pages—you might already optimize images, split your code, lazy load, or minify scripts. But there's one subtle yet crucial aspect many developers overlook: how stable and smooth the page appears while rendering.
You may have seen things like:
- The layout “jumps” after an image finishes loading
- A block of text suddenly moves down because of a late banner
- A button shifts right when the user is just about to click it
All of these are related to Reflow, Repaint, and Layout Shift. These behaviors directly impact CLS – Cumulative Layout Shift, one of the key metrics in Google’s Core Web Vitals.
What is Reflow?
Reflow (also called Layout) is when the browser recalculates the size and position of elements on the page.
Reflow is triggered when:
- A DOM element is added, removed, or modified
- CSS properties like
width
,font-size
,position
,display
, etc. are changed - Text content is updated
- The browser window is resized
For example:
element.style.width = "200px";
This forces the browser to recalculate the layout for that element and potentially for its siblings, parent, or children—hence, a reflow.
Reflow is resource-intensive. A small change can cascade and affect many parts of the layout, especially in complex structures.
What is Repaint?
Repaint is when the browser redraws elements visually without recalculating their layout.
Repaint happens when:
- You change styles like
background-color
,color
,border
, orvisibility
- You apply shadow effects, opacity changes, etc.
Example:
element.style.backgroundColor = "blue";
No layout change needed, just visual re-rendering.
Repaint is less expensive than Reflow, but if triggered repeatedly (e.g., through poorly optimized animations), it can still hurt performance.
What is Layout Shift?
Layout Shift refers to elements unexpectedly moving on the screen without user interaction. If this happens too often or too late in the load process, it can lead to a very poor user experience.
This is exactly what CLS – Cumulative Layout Shift measures.
Common causes:
- Images that load without predefined dimensions
- Custom fonts replacing fallback fonts (FOIT/FOUT)
- Late-loaded ads or banners pushing content around
- JavaScript injecting DOM elements dynamically without space reservations
How is CLS calculated?
CLS is determined by this formula:
CLS = Total Layout Shift Score = Impact Fraction × Distance Fraction
Simply put:
- Impact Fraction: how much of the screen is affected by the shift
- Distance Fraction: how far the shifted elements move
Google recommends keeping CLS below 0.1 for good Core Web Vitals.
How to reduce unnecessary Reflows and Repaints
- Avoid continuous DOM manipulations
// NOT recommended
for (let i = 0; i < 100; i++) {
document.body.style.margin = `${i}px`;
}
// Recommended
document.body.style.margin = "100px";
- Use CSS classes instead of inline styles
element.classList.add("expanded");
- Read and write DOM separately
const height = element.offsetHeight; // Read layout
element.style.height = height + 10 + "px"; // Write layout
// Better: batch DOM updates in requestAnimationFrame
requestAnimationFrame(() => {
// Make multiple changes here
});
How to optimize and reduce CLS
1. Always set fixed dimensions for images
<img src="/logo.png" width="200" height="100" alt="Logo" />
Or use CSS:
img { aspect-ratio: 2 / 1; }
If you’re using Next.js, the
next/image
component does this automatically.
2. Prevent font swap shifts – preload fonts early
<link rel="preload" href="/fonts/custom.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
Or use:
font-display: swap;
This avoids layout-blocking and reduces font-related shifts.
3. Reserve space for dynamic components (banners, ads...)
If a component is going to appear late (e.g., a newsletter banner), reserve its space upfront:
.banner-placeholder { min-height: 120px; }
4. Avoid unexpected DOM insertions via JavaScript
Avoid using setTimeout
to inject large UI blocks without prior layout preparation.
If animations are necessary, prefer opacity
or transform
, which don’t cause layout recalculations.
Conclusion
Reflow, Repaint, and Layout Shift are fundamental to understanding how your page behaves during render—and directly affect CLS, a major Core Web Vital.
By learning how the browser processes your layout, you can:
- Optimize performance beyond Lighthouse scores
- Prevent frustrating user experiences
- Build more stable and professional frontend interfaces
Start with the small things: define image sizes, preload fonts, and reserve layout space. Over time, your pages will load smoother, feel faster, and earn better trust from your users.