Most of us have written media queries that felt... a little wrong. You're styling a card component, but you're checking the viewport width to decide how it should look. The card lives inside a sidebar, though. So when the sidebar is narrow ā even on a wide screen ā your card still renders like it has all the space in the world. š
Sound familiar?
CSS Container Queries fix exactly this. And if you haven't used them yet, this post will change how you think about responsive design.
What Are CSS Container Queries?
Here's the simple version: a container query lets an element respond to the size of its parent container, not the viewport.
Think of it like this. Imagine you're a chameleon. With media queries, you change color based on the weather outside ā the whole environment. With container queries, you change color based on the room you're standing in. Much more accurate. Much more useful.
In code terms, you define a container, and then you write styles that activate based on how wide (or tall) that container is.
.card-wrapper {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
}
That's it. The .card now adapts based on .card-wrapper's width, not the browser window.
Why This Matters
Here's the problem with viewport-based media queries for components.
You build a ProductCard component. It looks great in the main content area. Then a designer drops it into a sidebar. Suddenly it breaks ā because the sidebar is only 280px wide, but your media query triggers at 768px viewport width. The viewport is still wide. The component has no idea it's in a tight space.
This is such a common frustration that developers have patched around it for years using JavaScript resize observers, custom hooks, or just making separate components. That's a lot of workarounds for something CSS should handle natively.
Container queries are the native solution. They've been in all major browsers since late 2023, and by 2025 browser support crossed 94%+. There is no good reason not to use them.
Benefits with Real-Life Examples
Here's what container queries actually give you in day-to-day work:
Truly reusable components. A card component can live in a sidebar, a grid, a modal, or a full-width section ā and style itself correctly in each case. You write it once. It works everywhere. ā
Cleaner component code. Instead of passing a
variant="compact"prop to a card just to handle narrow layouts, let CSS handle it. Your component logic stays focused on behavior, not presentation math.Better design systems. Design system teams love this. A
Button,Card, orListItemcomponent can now be truly layout-agnostic. It adapts to wherever the design places it without needing every consuming team to override styles.Less JavaScript. Before container queries, libraries like
react-use-measureor customResizeObserverhooks were common. Container queries replace most of those use cases with two lines of CSS.Easier maintenance. When styles live with the container instead of scattered across breakpoints at the page level, debugging is much faster. You open the component's CSS, not 400 lines of
@mediarules. š§
Media Queries vs Container Queries
This comparison does make sense here, because knowing when to use each one is the whole point.
| Situation | Use |
|---|---|
| Layout changes at the page level (header collapses, sidebar hides) | Media Query |
| A component adapts based on where it's placed | Container Query |
| Typography scaling for the whole page | Media Query |
| A card switches from column to row layout in different contexts | Container Query |
| Dark mode, print styles | Media Query |
| A widget component used across multiple layout zones | Container Query |
Short version: page-level layout ā media queries. Component-level layout ā container queries.
You will still use both. They solve different problems. Container queries don't replace media queries ā they complete them.
How to Use Container Queries (Step by Step)
Step 1: Define the container
Add container-type to the parent element you want to query against.
.sidebar {
container-type: inline-size;
}
inline-size means you're querying the width of the container (which is the horizontal size in most writing modes). There's also size if you want to query both width and height.
Step 2: Optionally name the container
Naming is useful when you have nested containers and need to query a specific ancestor.
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
Or shorthand:
.sidebar {
container: sidebar / inline-size;
}
Step 3: Write your container query
@container (min-width: 500px) {
.card {
display: flex;
gap: 1rem;
}
}
/* Using a named container */
@container sidebar (min-width: 300px) {
.card-title {
font-size: 1.25rem;
}
}
That's the full API for the most common use cases. There's also max-width, aspect-ratio, and orientation queries available.
Best Tips
ā
Do: Apply container-type to the wrapper, not the component itself.
The container and the element being queried should be different elements. Querying your own size creates a circular reference. Wrap the component in a div and apply container-type there.
/* Correct */
.card-wrapper {
container-type: inline-size;
}
/* Avoid: the card querying itself */
.card {
container-type: inline-size; /* then @container styles for .card itself won't work as expected */
}
ā Do: Use container queries for layout decisions, not content decisions.
Swapping from a stacked layout to a side-by-side layout based on container width ā great use case. Changing text content based on container size ā that's better handled in JavaScript or with conditional rendering in your framework.
ā
Do: Combine with CSS custom properties for cleaner scaling.
.card-wrapper {
container-type: inline-size;
--card-padding: 0.75rem;
}
@container (min-width: 400px) {
.card-wrapper {
--card-padding: 1.5rem;
}
}
.card {
padding: var(--card-padding);
}
ā Do: Name your containers when nesting.
Unnamed queries match the nearest ancestor container. When you have nested containers, name them to be explicit and avoid surprising style inheritance.
Common Mistakes People Make
1. Forgetting to set container-type.
Writing a @container query without first declaring container-type on the parent does nothing. No error, no warning ā just silence. This trips up almost everyone the first time. Always double-check the parent has the declaration.
2. Querying the element that is also the container.
If .card has both container-type: inline-size and @container rules targeting .card, you'll get unexpected behavior. The element cannot query its own size cleanly. Use a wrapper.
3. Replacing media queries entirely.
Some developers get excited and try to remove all media queries. Don't. Page-level layout (navigation collapsing, footer restructuring, font scaling for the whole page) still belongs to media queries. Container queries handle component layout. Know the difference.
4. Not testing in all the layout positions the component lives in.
The whole point of container queries is that your component adapts. But you need to actually test it in every context it'll be used ā sidebar, main column, modal, full-width section. A component that works in isolation might still break somewhere in the app if you haven't checked.
5. Worrying about browser support when you don't need to.
Container queries have strong support across Chrome, Edge, Firefox, and Safari. If you're not supporting very old browsers, you can use them today without polyfills. Check caniuse.com if you need to verify for your specific audience, but for most modern projects the answer is: go ahead. ā”
Conclusion
CSS Container Queries solve one of the longest-standing frustrations in responsive web development. The idea is simple: components should respond to where they live, not just how big the screen is.
If you've been shipping variant props, ResizeObserver hooks, or duplicated CSS just to handle components in different layout contexts ā container queries are the cleaner answer you've been waiting for.
The API is small. The browser support is solid. And once you start using it, you'll wonder how you built component libraries without it.
Start with one component. Pick a card, a widget, or a sidebar item that behaves differently in different layouts. Replace that logic with a container query. See how it feels.
Once you do, you probably won't go back. š
If this helped you, I'd love for you to share it with a dev friend who's still wrestling with layout props and media queries. More practical guides like this are on hamidrazadev.com ā drop by anytime.
Got questions or a use case you want to talk through? Leave a comment below. Happy to help.
Top comments (0)