EmpireUI
Get Pro
← Blog8 min read#tailwind#container queries#@container

Tailwind Container Queries: @container in Tailwind v4

Tailwind v4 ships first-class @container support — no plugin needed. Here's how container queries actually work and when to reach for them over media queries.

Developer writing Tailwind CSS code on a laptop screen

Why Container Queries Exist (And Why Media Queries Aren't Enough)

Media queries answer one question: how wide is the viewport? That's been the backbone of responsive design since 2012, and it works fine when a component always occupies a predictable slice of the page. But what happens when you're building a card component that sometimes lives in a 320px sidebar and sometimes in a full-width three-column grid? The viewport might be 1440px wide, but your card has 280px to work with. A media query won't tell you that.

Container queries solve this by letting elements respond to the size of their *parent container* instead. The card doesn't care about the browser window — it asks "how much space does my parent give me?" and adjusts accordingly. That's a fundamentally different model, and once you've felt it work, going back to viewport-only queries feels like reasoning about the wrong thing entirely.

In practice, this is the feature that makes truly reusable components possible. A <ProductCard> should look great whether it's in a 240px widget or a 600px featured slot — without you writing separate media query breakpoints for every page layout it might appear in. Write the component once, slot it anywhere.

Worth noting: container queries have been in Chrome since 2022 (v105), Firefox since v110, and Safari since 16. By mid-2026 you're dealing with essentially universal support. There's no real polyfill story needed anymore.

How @container Works in Tailwind v4

Tailwind v4 ships container query support baked in — no @tailwindcss/container-queries plugin required. That plugin was the v3 workaround; v4 treats @container as a first-class citizen alongside the rest of the responsive system.

The API is clean. You mark a parent as a container with the @container utility, then use @sm:, @md:, @lg: etc. on child elements to apply styles at container breakpoints. The @ prefix is the visual cue that you're responding to a container rather than the viewport.

<div class="@container">
  <div class="grid grid-cols-1 @md:grid-cols-2 @lg:grid-cols-3 gap-4">
    <article class="p-4 @sm:p-6 @md:p-8">
      <h2 class="text-base @sm:text-lg @lg:text-xl font-semibold">Card title</h2>
      <p class="text-sm @md:text-base text-neutral-600">Card body text that reflows naturally.</p>
    </article>
  </div>
</div>

The default container breakpoints in v4 map to these container widths: @xs = 320px, @sm = 384px, @md = 448px, @lg = 512px, @xl = 576px, @2xl = 672px. Notice these are smaller than the viewport breakpoints — they're designed for component-level thinking, not page-level layout.

Honestly, the naming is one of the smartest decisions in v4. The @ prefix means you can mix viewport (md:) and container (@md:) utilities on the same element without ambiguity. No class name collisions, no mental overhead trying to remember which system you're in.

Named Containers and Nested Contexts

When you have nested containers — say, a sidebar that itself contains a card grid — you need a way to tell a child element *which* container to respond to. By default @container queries bubble up to the nearest @container ancestor. Named containers let you target a specific parent explicitly.

<!-- Named container -->
<aside class="@container/sidebar w-64">
  <div class="@container/card">
    <!-- This responds to /card container -->
    <div class="p-2 @sm/card:p-6">
      <!-- This responds to /sidebar container -->
      <nav class="hidden @lg/sidebar:block">...</nav>
    </div>
  </div>
</aside>

The /name suffix syntax is straightforward once you see it. @container/sidebar declares a named container, and @lg/sidebar: on a descendant queries that specific container's width. You can nest as deep as you want without things colliding.

Quick aside: named containers are also useful for accessibility tooling — you can query a container by name in JavaScript using ContainerQueryList if you ever need to sync JS behavior with the CSS breakpoints. Handy for things like lazy-loading different image sizes based on actual rendered slot width.

Arbitrary Container Breakpoints and Custom Values

The built-in breakpoints cover most cases, but sometimes you need something specific — a 420px breakpoint because your design system says so, or a 900px threshold because the layout goes weird below that. Tailwind v4 handles this with arbitrary values, same pattern as other utilities.

<!-- Arbitrary container breakpoint -->
<div class="@container">
  <div class="text-sm @[420px]:text-base @[900px]:text-lg">
    Scales at exactly 420px and 900px container width
  </div>
</div>

You can also define custom container breakpoints in your tailwind.config.ts under the containers key if you're reaching for the same value repeatedly. That keeps your markup clean and your design tokens in one place.

// tailwind.config.ts
export default {
  theme: {
    extend: {
      containers: {
        '2xs': '240px',
        '3xl': '960px',
      },
    },
  },
}

Look, arbitrary values are an escape hatch — don't reach for @[347px]: in production markup. If you find yourself using the same arbitrary breakpoint more than twice, just define it in config. Your teammates will thank you.

Real-World Patterns: Cards, Sidebars, and Design System Components

The place container queries shine brightest is design systems. If you're building components for a component library, you want your Button, Card, Alert, and Badge components to adapt to whatever context they're dropped into — without coupling their internals to page-level layouts.

A product card is the canonical example. At 280px it shows a stacked image-above-text layout. At 400px it goes side-by-side. At 600px it adds a description excerpt. All of that with zero knowledge of the viewport size: ``html <div class="@container"> <div class="flex flex-col @[380px]:flex-row gap-3 p-4"> <img class="w-full @[380px]:w-32 @[380px]:h-32 object-cover rounded-lg" src="/product.jpg" alt="Product" /> <div class="flex flex-col gap-1"> <h3 class="font-semibold text-sm @[380px]:text-base">Product Name</h3> <p class="hidden @[500px]:block text-sm text-neutral-500 line-clamp-2"> Full description only visible in wider slots. </p> <span class="text-indigo-600 font-bold">$49</span> </div> </div> </div> ``

Sidebars are another killer use case. A collapsible sidebar that goes from 64px (icon-only) to 280px (full nav) can drive its own child components purely through container queries. No JS class toggling for layout — the CSS handles it. That said, you'll still want JS for the actual expand/collapse interaction; don't try to do everything in CSS.

If you're building glassmorphism or neumorphism components — check out the glassmorphism generator for quick card prototypes — container queries let you scale frosted-glass cards cleanly across dashboard sidebar widgets and hero-section features without maintaining two separate component variants.

One more thing — container queries work perfectly with CSS Grid's auto-fit and minmax patterns. The two features are complementary, not competing. grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)) handles the slot allocation; container queries handle the per-slot component adaptation. That combination is genuinely powerful.

Container Queries vs. Media Queries: When to Use Which

Don't throw away media queries. They're still the right tool for page-level layout decisions: when to switch from a single-column to a three-column layout, when to show a mobile nav vs. a desktop nav, when to change the overall typographic scale of the page. Those decisions depend on the viewport, so media queries answer them correctly.

Container queries are for *component internals*. The <Sidebar> component doesn't know if it's 280px because the viewport is 1024px or because it's been manually resized in some dashboard drag-to-resize panel. It shouldn't need to know. Container queries let the component reason about its own available space.

In practice, you'll use both. The md: breakpoint decides that the sidebar exists at all and is 280px wide. The @sm: container query inside the sidebar card decides to show a two-line description instead of one. Different layers of the layout hierarchy, different tools.

What you want to avoid is using container queries to re-implement page layout. If you catch yourself adding @container on <body> or your top-level <main>, stop — that's what media queries are for. Keep the boundary clean: page structure = viewport media queries, component internals = container queries.

Migrating from the v3 Plugin to Native v4

If you're on Tailwind v3 with @tailwindcss/container-queries, the migration to v4 native is mostly mechanical. The class names are identical — that was intentional. The @container class still marks a container, and @sm:, @md:, etc. still work the same way.

What changes: remove @tailwindcss/container-queries from your dependencies and from the plugins array in your config. That's it. Run npm uninstall @tailwindcss/container-queries, delete the plugin entry, and you're done. The v4 engine handles everything natively now.

// tailwind.config.ts
- import containerQueries from '@tailwindcss/container-queries'

export default {
  // ...
  plugins: [
-   containerQueries,
  ],
}

The one gotcha: if you're using v3's container utility (the one that sets max-width on layout containers — completely different feature), make sure you haven't aliased it to anything confusing. In v4 the naming is unambiguous because @container is the query marker and container utilities got renamed, but double-check your existing classes if you relied on v3's container for layout.

Worth noting: v4 also brings CSS-first configuration via @theme in your CSS file instead of tailwind.config.ts. You can define container breakpoints there too. The ecosystem is still catching up on which pattern to standardize, but both work fine today.

FAQ

Do I need the @tailwindcss/container-queries plugin in Tailwind v4?

No. Tailwind v4 ships container query support natively. Uninstall the plugin and remove it from your config — the class names are the same, so nothing else breaks.

What's the default @sm container breakpoint width in Tailwind v4?

@sm maps to 384px container width, not 640px. Container breakpoints are smaller than viewport breakpoints because they're designed for component-level sizing, not page layout.

Can I use container queries and media queries on the same element?

Yes, and you often should. The @md: prefix targets the container; md: targets the viewport. They don't conflict — Tailwind v4 disambiguates them cleanly with the @ prefix.

What browsers support @container queries in 2026?

All of them that matter. Chrome 105+, Firefox 110+, and Safari 16+ all support @container natively. You don't need a polyfill for any production-grade project in 2026.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Tailwind CSS OKLCH Colors: Perceptually Uniform Palettes in v4Tailwind :has() Selector: Parent Styling Without JavaScriptContainer Queries for Components: Component-Driven Responsive DesignResponsive Component Design: Container Queries Over Media Queries