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 elementsMath.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 usingreact-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
- Measuring too early: Component might not be rendered yet when we measure
- Forgetting cleanup: ResizeObserver keeps running unless we disconnect it
- Infinite re-renders: Don't trigger state changes in render without dependencies
- 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.