Measuring Containers in React

August 15, 2025

A while back, I built a component to render PDFs in the Tipbox CMS. All was working well, though the component was relying on window measurement for its width. Later we added another part of the app that needed a similar PDF rendering solution, but my component was only designed to work its original context. The PDFs were rendering at a zoomed in state when trying to use them in our more compressed tasking UI (since they were basing their width on the window).

Essentially, my component was relying on an assumption about its context instead of being properly composable.

PDF Viewer Component

Originally, out PDF viewer was only used in the CMS content area, so it used a simple window-based calculation:

This worked fine for the full-width layout it was designed for. But when the same component was later needed in a narrow sidebar drawer, the sizing was off.

Why Window-Based Sizing Fails

  • Assumes full-width usage: window.innerWidth gives you the entire browser width
  • Ignores actual container: Your parent component might be in a 300px sidebar, not a 1200px main area
  • Breaks re-usability: The component becomes tightly coupled to specific layout assumptions, making it difficult to use elsewhere.

A Solution: Container-Aware Sizing

Step 1: Create a Way to Measure

  • useRef() gives us direct access to DOM elements for measurement
  • the measured width is stored in state so React can respond to changes
  • the default value prevents errors during initial render

Step 2: Measure the Actual Container

getBoundingClientRect() gives us the real, rendered size of an element - not what CSS says it should be, but the actual size on screen.

Step 3: Handle Dynamic Changes

Why ResizeObserver? Unlike window.resize events, ResizeObserver watches specific elements. It fires when your container changes size, not when the whole window changes.

Step 4: Connect to Your JSX

The ref connects your measurement logic to the actual DOM element we want to measure.

Step 5: Use the Measured Width Intelligently

Why the math?:

  • containerWidth - 100: Use available space minus a buffer for padding or other UI elements
  • Math.max(300, ...): Never smaller than a readable size for a PDF -Math.min(..., 1200): Never larger than practical size for a PDF (ultrawide monitors are a thing here)

When to Use This Pattern

Good candidates:

  • Components with fixed-size content (images, videos, canvases)
  • Charts or visualizations that need specific dimensions
  • Components originally built for one layout, now used in multiple places
  • Anything that breaks when container size changes unexpectedly

Alternatives to consider:

  • CSS-only solutions: Sometimes max-width: 100% // (of the parent) is enough, though it wasn't working for me here using react-pdf
  • Percentage-based sizing: When we want proportional scaling
  • CSS Container Queries: The emerging, browser-native solution that will eventually make much of this manual measurement unnecessary. I plan to explore this more!

Common Gotchas

  1. Measuring too early: Component might not be rendered yet when we measure
  2. Forgetting cleanup: ResizeObserver keeps running unless we disconnect it
  3. Infinite re-renders: Don't trigger state changes in render without dependencies
  4. Over-measuring: Not every component needs dynamic sizing

The Bigger Picture

This pattern represents a shift from "designing for one context" to "designing for composability". Modern React applications often need components that compose regardless of their context.

By measuring the actual container instead of making assumptions about the environment, this PDF viewer can now compose across different parts of our application.