Representative interview topic

Frontend Interview: How Do You Design a Dashboard Card That Responds to Container Size?

FrontendHard
Offer.cc Editorial TeamPublished Updated

Question

A dashboard card can be resized by drag interactions and can change width when a sidebar collapses, fonts load, or the grid reflows. It must switch to a compact layout below 320px while remaining responsive during continuous resizing. Design the implementation, explain why window.resize is insufficient, show how to avoid ResizeObserver feedback loops, and describe performance and compatibility verification.

Prompt and scope

You maintain a multi-column dashboard. A card's size is determined by the grid, dragging, the sidebar, and font loading, so it cannot be treated as a function of viewport size alone. Below 320px it shows a compact title and one-column metrics; above the threshold it shows the full layout. Collapsing the sidebar without changing the window must take effect immediately.

This question tests element-level measurement, browser layout timing, React lifecycle, and performance boundaries. Public front-end interview material commonly covers DOM, CSS, browser events, and performance; the platform documentation gives precise ResizeObserver notification and loop behavior for deeper follow-ups.

What the interviewer is testing

  • Explain that window.resize describes viewport changes, not grid reflows, font swaps, or parent-size changes.
  • Choose content-box or border-box and define what the threshold means.
  • Write measurements to state or CSS variables without synchronously resizing the observed element.
  • Handle target replacement, unmounting, hidden nodes, older browsers, and many cards.
  • Prove smoothness with user metrics, long tasks, and layout traces rather than callback counts alone.

Questions to clarify first

  • Is the threshold about content width, border-box width, or a style-only decision that CSS container queries can express?
  • Should size changes trigger data work, or only change presentation?
  • Must the UI follow every frame, or is one coalesced update per frame sufficient?
  • What is the browser support floor, and can server rendering touch window?
  • Are there dozens or thousands of cards, and can only visible cards be observed?

A 30-second answer

“I would first establish that this is an element-size problem, not a viewport-size problem. For style-only breakpoints I would prefer CSS container queries. If JavaScript needs the measurement, I would attach a ResizeObserver on the client, read a defined box, deduplicate the threshold state, and write a CSS variable or discrete state. The callback should not synchronously change the observed size; nonessential writes can be scheduled for the next frame, and cleanup must disconnect the observer. For many cards I would bound the observed set and measure INP, long tasks, and layout cost. Older browsers get an explicit fixed-layout, container-query, or throttled viewport fallback.”

Deep-dive answer

Step 1: Check whether CSS is enough

If the requirement is only “show compact styles below 320px,” a container query is usually simpler and keeps measurement out of JavaScript. ResizeObserver is justified when size drives chart sampling, virtualization, a third-party renderer, or observable business logic.

Step 2: Define the size contract

ResizeObserverEntry exposes content and border box measurements. Define which box owns the threshold, then standardize units and rounding. A floating-point sample is not automatically a business event; deduplicate a 319.9-to-320.1 transition as a breakpoint state when that is the product contract.

Step 3: Establish the observer lifecycle

Create the observer after the client component mounts, observe the card node, and call unobserve or disconnect when the node changes or the component unmounts. In React, let a ref own the target and an Effect own observer creation and cleanup so ordinary renders do not recreate it.

tsx
const ref = useRef<HTMLDivElement>(null)
const [compact, setCompact] = useState(false)

useEffect(() => {
  const node = ref.current
  if (!node || !('ResizeObserver' in window)) return

  const observer = new ResizeObserver(([entry]) => {
    const width = entry.contentRect.width
    const next = width < 320
    setCompact((current) => (current === next ? current : next))
  })

  observer.observe(node)
  return () => observer.disconnect()
}, [])

Step 4: Prevent a callback feedback loop

If the callback changes the observed element's width or height, that change can schedule another notification and eventually produce ResizeObserver loop completed with undelivered notifications. Keep the callback focused on the size contract, or schedule visual writes with requestAnimationFrame and make them idempotent.

Step 5: Separate measurement from rendering

Write measurements to a CSS custom property when CSS can own the layout, or keep only a discrete state such as compact. Do not put every pixel change into React state during a drag. If a continuous value is required, coalesce notifications per animation frame and record dropped frames and long tasks.

Step 6: Handle many cards and hidden nodes

For thousands of cards, observe only visible nodes or let a layout layer distribute measurements. display: none, collapsed panels, and virtualization change observable sizes; after a node becomes visible, verify that the first notification restores the correct state. An observer is not a polling loop.

Step 7: Define fallback and server boundaries

Server rendering cannot access window. Detect support inside a client Effect. Without ResizeObserver, use a fixed layout, CSS media/container rules, or a throttled viewport listener, and document the element-level behavior that the fallback cannot provide.

Step 8: Verify with evidence

Test sidebar collapse, dragging, font loading, grid reflow, rotation, and browser zoom. Record callbacks, layout, paint, and long tasks in Chrome Performance; use INP or input delay to check drag responsiveness. Add a test for the console loop warning and verify that an unmounted card no longer receives updates.

Trade-offs and boundaries

Container queries versus ResizeObserver

Container queries fit style-only breakpoints and remain declarative. ResizeObserver fits JavaScript calculations and third-party rendering but requires lifecycle and performance controls. The boundary is whether the measurement must leave the style layer.

content-box versus border-box

Use content-box for content-layout thresholds and border-box for an outer card contract that includes padding and borders. A wrong choice shifts the breakpoint; put the choice in the contract and tests.

Continuous versus discrete values

Continuous width can drive a chart but updates frequently. A discrete breakpoint is stable and easier to test. Start with the discrete contract and add continuous updates only with a measured budget and clear visual benefit.

Failure drills and evolution

Failure: listen only to window.resize

Sidebar collapse and grid reflow do not change the viewport, so the card stays in the wrong layout. Observe the card or its container and test non-viewport changes.

Failure: mutate the observed size in the callback

The mutation triggers another callback and can create a loop and extra layout. Write an independent CSS variable, use a discrete state, or schedule an idempotent next-frame update.

Failure: setState for every pixel

Dragging creates high-frequency React renders and worsens input response. Deduplicate thresholds, coalesce with rAF, observe visible cards only, and confirm the cost in Performance.

Common mistakes and follow-ups

Mistake: treating ResizeObserver as a stronger resize event

It observes an element's box and delivers notifications according to layout timing; it is not a simple replacement for a viewport event.

Follow-up: how do you verify React Strict Mode?

Ensure development setup and cleanup remain paired and observer counts do not accumulate. Do not mistake the extra development check for production duplicate subscriptions.

Follow-up: can the callback call getBoundingClientRect?

It can, but the extra measurement may add layout cost. Prefer the entry's box values and prove any additional read with a performance trace.

Follow-up: how do you test a size loop?

Make the callback change a style that affects its own size, observe warnings, notification count, and final size, then assert the fixed version stabilizes without continuous notifications.

Follow-up: when is JavaScript unnecessary?

When the requirement is only a container breakpoint style change, prefer CSS container queries. Keep JavaScript for data, measurement, or third-party rendering that truly needs it.

Follow-up: how do you prove the optimization worked?

Compare INP, long-task count, layout time, React commits, and memory under the same drag script. Callback count alone is not a user-experience metric.

Public sources

Related questions