Back to Blog
CSScontent-visibilityPerformanceRendering

CSS content-visibility Property Guide

Master the CSS content-visibility property for render optimization and improving page performance.

B
Bootspring Team
Engineering
December 17, 2019
6 min read

The content-visibility property controls whether an element renders its contents, enabling significant performance improvements. Here's how to use it.

Basic Usage#

1/* Skip rendering off-screen content */ 2.section { 3 content-visibility: auto; 4 contain-intrinsic-size: auto 500px; 5} 6 7/* Possible values */ 8.visible { 9 content-visibility: visible; /* Default - always render */ 10} 11 12.hidden { 13 content-visibility: hidden; /* Never render (like display:none but preserves state) */ 14} 15 16.auto { 17 content-visibility: auto; /* Browser decides based on visibility */ 18}

Long Page Optimization#

1/* Optimize long scrolling pages */ 2article { 3 content-visibility: auto; 4 contain-intrinsic-size: 0 800px; /* Estimated height */ 5} 6 7/* Blog post sections */ 8.post-section { 9 content-visibility: auto; 10 contain-intrinsic-size: auto 400px; 11} 12 13/* Product cards in grid */ 14.product-card { 15 content-visibility: auto; 16 contain-intrinsic-size: 300px 400px; /* width height */ 17} 18 19/* Comment threads */ 20.comment { 21 content-visibility: auto; 22 contain-intrinsic-size: 0 150px; 23}

contain-intrinsic-size#

1/* Fixed intrinsic size */ 2.item { 3 content-visibility: auto; 4 contain-intrinsic-size: 200px 300px; /* width height */ 5} 6 7/* Height only (width from layout) */ 8.item { 9 content-visibility: auto; 10 contain-intrinsic-size: 0 300px; 11} 12 13/* Auto with fallback */ 14.item { 15 content-visibility: auto; 16 contain-intrinsic-size: auto 300px; 17 /* Uses last known size, falls back to 300px */ 18} 19 20/* Individual properties */ 21.item { 22 content-visibility: auto; 23 contain-intrinsic-width: 200px; 24 contain-intrinsic-height: 300px; 25} 26 27/* Block and inline */ 28.item { 29 content-visibility: auto; 30 contain-intrinsic-block-size: 300px; 31 contain-intrinsic-inline-size: 200px; 32}

Hidden vs display:none#

1/* display:none - removes from layout, resets state */ 2.hidden-display { 3 display: none; 4} 5 6/* content-visibility:hidden - keeps in layout, preserves state */ 7.hidden-content { 8 content-visibility: hidden; 9} 10 11/* Use case: Tab panels */ 12.tab-panel { 13 content-visibility: hidden; 14} 15 16.tab-panel.active { 17 content-visibility: visible; 18} 19 20/* Form state is preserved when hidden */ 21.form-section { 22 content-visibility: hidden; 23} 24 25.form-section.active { 26 content-visibility: visible; 27} 28/* Input values, scroll position, etc. are maintained */

Lazy Rendering Pattern#

1/* Lazy sections that render when visible */ 2.lazy-section { 3 content-visibility: auto; 4 contain-intrinsic-size: auto 600px; 5} 6 7/* Cards in infinite scroll */ 8.card-container { 9 display: grid; 10 grid-template-columns: repeat(3, 1fr); 11 gap: 1rem; 12} 13 14.card { 15 content-visibility: auto; 16 contain-intrinsic-size: auto 350px; 17} 18 19/* Image gallery */ 20.gallery-item { 21 content-visibility: auto; 22 contain-intrinsic-size: 300px 300px; 23} 24 25/* Timeline items */ 26.timeline-item { 27 content-visibility: auto; 28 contain-intrinsic-size: 0 200px; 29 padding: 1rem; 30 margin-bottom: 1rem; 31}

Dashboard Widgets#

1/* Dashboard with many widgets */ 2.dashboard { 3 display: grid; 4 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); 5 gap: 1rem; 6} 7 8.widget { 9 content-visibility: auto; 10 contain-intrinsic-size: auto 250px; 11 padding: 1rem; 12 background: white; 13 border-radius: 8px; 14} 15 16/* Chart widgets */ 17.chart-widget { 18 content-visibility: auto; 19 contain-intrinsic-size: auto 400px; 20} 21 22/* Table widgets */ 23.table-widget { 24 content-visibility: auto; 25 contain-intrinsic-size: auto 500px; 26}

Combining with Containment#

1/* Full containment for maximum optimization */ 2.optimized-section { 3 content-visibility: auto; 4 contain: layout style paint; 5 contain-intrinsic-size: auto 400px; 6} 7 8/* Strict containment */ 9.strict-section { 10 content-visibility: auto; 11 contain: strict; 12 contain-intrinsic-size: 100% 300px; 13} 14 15/* Size containment only */ 16.size-contained { 17 content-visibility: auto; 18 contain: size; 19 contain-intrinsic-size: 300px 200px; 20}

Accordion Pattern#

1/* Accordion with content-visibility */ 2.accordion-panel { 3 content-visibility: hidden; 4 contain-intrinsic-size: 0 0; 5 height: 0; 6 overflow: hidden; 7 transition: height 0.3s ease; 8} 9 10.accordion-panel.open { 11 content-visibility: visible; 12 contain-intrinsic-size: auto; 13 height: auto; 14} 15 16/* Alternative: keep size but hide */ 17.accordion-content { 18 content-visibility: hidden; 19} 20 21.accordion-item.active .accordion-content { 22 content-visibility: visible; 23}
1/* Modal optimization */ 2.modal-backdrop { 3 content-visibility: hidden; 4 position: fixed; 5 inset: 0; 6 background: rgba(0, 0, 0, 0.5); 7} 8 9.modal-backdrop.open { 10 content-visibility: visible; 11} 12 13.modal-content { 14 /* Render-heavy content */ 15 content-visibility: inherit; 16} 17 18/* Off-canvas menu */ 19.off-canvas { 20 content-visibility: hidden; 21 position: fixed; 22 left: 0; 23 top: 0; 24 width: 300px; 25 height: 100vh; 26 transform: translateX(-100%); 27 transition: transform 0.3s; 28} 29 30.off-canvas.open { 31 content-visibility: visible; 32 transform: translateX(0); 33}

Table Optimization#

1/* Large table optimization */ 2.data-table { 3 width: 100%; 4 border-collapse: collapse; 5} 6 7.data-table tbody tr { 8 content-visibility: auto; 9 contain-intrinsic-size: auto 50px; 10} 11 12/* Grouped rows */ 13.table-group { 14 content-visibility: auto; 15 contain-intrinsic-size: auto 200px; 16} 17 18/* Virtual scrolling enhancement */ 19.virtual-row { 20 content-visibility: auto; 21 contain-intrinsic-size: 100% 40px; 22}

Animation Considerations#

1/* Avoid content-visibility on animated elements */ 2.animated-card { 3 /* Don't use content-visibility: auto here */ 4 /* It can interfere with animations */ 5} 6 7/* Use for static sections around animations */ 8.static-section { 9 content-visibility: auto; 10 contain-intrinsic-size: auto 300px; 11} 12 13/* Animated section remains visible */ 14.animated-section { 15 content-visibility: visible; 16}

JavaScript Integration#

1// Detect when content becomes visible 2const section = document.querySelector('.lazy-section'); 3 4// contentvisibilityautostatechange event 5section.addEventListener('contentvisibilityautostatechange', (e) => { 6 if (e.skipped) { 7 console.log('Section is hidden'); 8 } else { 9 console.log('Section is visible'); 10 // Load data, start animations, etc. 11 } 12}); 13 14// Check current state 15if (section.matches(':visible')) { 16 // Content is being rendered 17} 18 19// Force visibility 20function ensureVisible(element) { 21 element.style.contentVisibility = 'visible'; 22} 23 24// Intersection Observer fallback 25const observer = new IntersectionObserver((entries) => { 26 entries.forEach((entry) => { 27 if (entry.isIntersecting) { 28 entry.target.style.contentVisibility = 'visible'; 29 } 30 }); 31});

Performance Measurement#

1// Measure rendering performance 2const start = performance.now(); 3 4// Apply content-visibility 5document.querySelectorAll('.section').forEach((section) => { 6 section.style.contentVisibility = 'auto'; 7 section.style.containIntrinsicSize = 'auto 400px'; 8}); 9 10// Force style recalculation 11document.body.offsetHeight; 12 13const end = performance.now(); 14console.log(`Optimization applied in ${end - start}ms`); 15 16// Monitor layout shifts 17const observer = new PerformanceObserver((list) => { 18 for (const entry of list.getEntries()) { 19 console.log('Layout shift:', entry.value); 20 } 21}); 22 23observer.observe({ type: 'layout-shift', buffered: true });

Best Practices#

Usage: ✓ Use for off-screen content ✓ Set contain-intrinsic-size ✓ Use 'auto' for most cases ✓ Apply to repeated elements Performance: ✓ Reduces initial render time ✓ Skips off-screen layout ✓ Improves scroll performance ✓ Preserves element state Intrinsic Size: ✓ Estimate accurately ✓ Use 'auto' prefix when possible ✓ Match typical content size ✓ Update if content varies Avoid: ✗ On visible above-fold content ✗ On animated elements ✗ Without intrinsic size ✗ Causing layout shifts

Conclusion#

The content-visibility property significantly improves rendering performance by skipping off-screen content. Use it for long pages, lists, and hidden UI elements. Always set contain-intrinsic-size to prevent layout shifts. The 'auto' value lets the browser optimize automatically based on visibility.

Share this article

Help spread the word about Bootspring