Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
PerformanceJune 26, 20264 min read

Cumulative Layout Shift Optimization: Mastering CSS Containment

Cumulative Layout Shift optimization is easier when you use CSS containment and ResizeObserver. Learn how to stop UI jitter and stabilize your dynamic layouts.

Web PerformanceCSSFrontend EngineeringCore Web VitalsJavaScriptPerformanceWeb VitalsFrontend

Last month, I spent about three days chasing a phantom layout shift on a client's dashboard. Every time a user triggered a filter, a sidebar would expand, causing the main grid to jump roughly 40px, ruining the user's focus. We were fighting the browser's layout engine because we hadn't accounted for the flow of dynamic content.

If you’ve ever watched a paragraph of text jump while an image loads or a widget injects itself into the DOM, you know exactly how frustrating Cumulative Layout Shift (CLS) is for users. It’s not just an annoyance; it’s a direct hit to your Core Web Vitals and your site's perceived reliability.

The Problem with Dynamic Injection

When you inject content into the DOM dynamically—like a "Load More" button or a fetched notification—the browser has to recalculate the positions of every element that follows. If you don't reserve space for that content beforehand, the browser shifts the entire document flow to accommodate the new node.

In my recent project, we tried to fix this with standard CSS transitions, but that only hid the problem. The browser still reflowed the page, just more slowly. To truly master Cumulative Layout Shift, we had to stop treating layout as an afterthought and start architecting it as a fixed constraint.

Using CSS Containment for Stability

The most powerful tool in your belt for CLS optimization is the contain property. It tells the browser that a subtree is independent of the rest of the page. By isolating the layout and paint of a specific container, you prevent changes inside that container from triggering a global reflow.

I usually start by applying contain: layout or contain: strict to dynamic containers. Here is how I set up a sidebar widget to prevent it from affecting the main content:

CSS
#9CDCFE">color:#4EC9B0">.dynamic-sidebar-widget {
  #9CDCFE">contain: layout style;
  #9CDCFE">min-height: 300px; #9CDCFE">color:#6A9955">/* Reserve space */
  width: 100%;
}

By setting a min-height, I ensure the browser knows exactly how much space to reserve. Even if the content takes 50ms to fetch, the layout remains stable. If you're still seeing issues, Forced Synchronous Layout: How to Fix Reflow Bottlenecks is a great resource to help you identify if your JavaScript is causing unnecessary recalculations.

Leveraging ResizeObserver for Fluidity

Sometimes, you can't predict the height of your dynamic content. Fixed min-height values lead to either massive gaps or overflow issues. This is where ResizeObserver shines. It allows your JavaScript to react to size changes before the browser completes the layout paint cycle, giving you a chance to animate or adjust surrounding elements gracefully.

Here’s how I implement a guardian for dynamic content:

JAVASCRIPT
const observer = new ResizeObserver(entries => {
  for (let entry of entries) {
    const { height } = entry.contentRect;
    // Perform logic to prevent jarring jumps
    // e.g., adjusting a parent wrapper's height
    console.log(CE9178">'Component height changed to:', height);
  }
});

observer.observe(document.querySelector(CE9178">'.dynamic-content-container'));

This approach is much cleaner than polling the DOM or using brittle setTimeout hacks. It’s precise, performant, and keeps your layout logic decoupled from your data-fetching logic.

Trade-offs and Lessons Learned

My first attempt at this involved using content-visibility: auto, which is excellent for performance, but it actually caused more layout shifts because the browser wouldn't calculate the dimensions of off-screen elements. I had to revert to explicit aspect-ratio or min-height declarations to maintain stability.

Always remember:

  1. Reserve space: Use aspect-ratio or min-height for any dynamic content.
  2. Isolate: Use contain to keep reflows local.
  3. Observe: Use ResizeObserver for components that genuinely need to be fluid.

If you are dealing with complex navigation or page transitions, I’d suggest checking out View Transitions API and Content-Visibility: Faster Page Navigation to see how modern APIs can handle these shifts natively.

FAQ

Does contain: strict break my layout? Yes, it can. It acts like a block formatting context, so your child elements won't be able to escape the container's bounds. Use it carefully on containers that don't need to overflow their parents.

Why not just use a skeleton loader? Skeleton loaders are great, but they don't replace the need for CSS containment. If your skeleton loader itself jumps when the real data arrives, you haven't solved the underlying issue.

Does this affect INP (Interaction to Next Paint)? Indirectly, yes. By preventing heavy layout work, you leave more main-thread capacity for your JavaScript to respond to user inputs.

I'm still experimenting with how container-queries interact with these containment strategies. It feels like there's a sweet spot where the browser can handle the layout logic entirely without manual ResizeObserver intervention, but for now, the manual approach is the most reliable way to ship stable interfaces.

Back to Blog

Similar Posts

PerformanceJune 27, 20265 min read

Font Loading Strategy for Core Web Vitals and Better UX

Master your Font Loading Strategy to improve Core Web Vitals. Learn how to prevent FOIT and Cumulative Layout Shift using variable fonts and CSS best practices.

Read more
PerformanceJune 21, 20264 min read

Speculation Rules API: Achieving Instant Navigation in Modern Apps

Master the Speculation Rules API for predictive prefetching. Learn how to drive instant navigation and improve Core Web Vitals without breaking your server.

Read more
PerformanceJune 28, 20264 min read

Core Web Vitals Optimization: Eliminating Critical Request Chains

Learn to eliminate critical request chains and boost Core Web Vitals. Discover how precise resource prioritization and preload scanning improve load times.

Read more