Skip to the content.

Container queries: size a component by its parent, not the viewport

Container queries: size a component by its parent, not the viewport

You build a card component. In the sidebar it should stack its image above its copy; in the main column it should put them side by side. With media queries you write the breakpoint against the viewport — but the viewport did not change. The card moved into a narrower parent, and nothing about the screen told you that. Container queries fix the mismatch: instead of asking how wide the browser window is, they ask how wide the container is. That is the question a reusable component actually needs answered before it can lay itself out.

Container queries size a component by its parent The same card component adapts to the width of its container: media stacked above the copy in a narrow column, side by side in a wide one, while the viewport stays unchanged. narrow container media stacks above the copy wide container media sits beside the copy
The viewport never changes — only the container's width does, and the card reflows.

The container is the unit of reuse

A media query is the right tool when the page is the thing that changes — a phone, a tablet, a desktop. But the moment you build a component and drop it into several parents, the viewport is the wrong ruler. A card in a sidebar and the same card in a hero are the same component under different parents, and the layout decision belongs to the parent’s width, not the window’s. Container queries make the component self-describing: it declares the widths it needs and reflows itself wherever it lands. That is the difference between a component that is reusable and one that is merely copy-pasteable.

Declaring a query container

Any element can become a query container by giving it a container-type. The inline-size value queries the horizontal axis only, which is what most layouts care about and what avoids the expensive full-size variant. A container-name lets you target a specific ancestor when several are query containers:

.product-card {
  container-type: inline-size;   /* this element is now a query container */
  container-name: card;          /* name it so queries are unambiguous */
}

@container card (min-width: 480px) {
  .product-card__media {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
}

The @container rule reads almost like a media query, but the condition is the container’s width, not the viewport’s. The card now carries its own responsive behaviour with it, and the sidebar and the hero both get the right layout with zero page-level breakpoints.

The JavaScript you no longer need

Before container queries, the same effect meant a ResizeObserver and a class toggle — JavaScript doing what CSS can now do declaratively:

const card = document.querySelector('.product-card');
const observer = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const wide = entry.contentBoxSize[0].inlineSize >= 480;
    card.classList.toggle('is-wide', wide);
  }
});
observer.observe(card);

That works, but it is imperative, per-component, and easy to forget on the eleventh card you add. Container queries move the decision into the stylesheet where it belongs, and the browser handles the observation for you. ResizeObserver still earns its keep for things CSS cannot express — reacting to a height, or running arbitrary logic on resize — but for “reflow when my parent gets wider”, the @container rule is the simpler, more robust answer.

Where the trade-off bites

Container queries are not a free upgrade. A query container creates a new containment context, so position: fixed children and some sizing behaviour resolve against the container rather than the viewport — the same surprise contain and transform already teach you. And because the browser must track each container’s size, a page with hundreds of query containers pays a real cost; reach for them on the components that genuinely move between parents, not on every element. The rule of thumb: if the component’s layout depends on its own parent’s width, use a container query; if it depends on the device, keep the media query. Most reusable components are the former, which is why this is the tool that finally makes “responsive component” mean something.