Unlock Lifetime Access to 12,400+ Premium Components, an Advanced UI Builder, and an AI Assistant! 👇
🚀 Get it Now!

Tailwind Container Queries

Style components based on their parent’s dimensions.

by Yucel Faruk Sahan
21 min read
Updated on

Tailwind Container Queries

Intro

Tailwind CSS has always been a go-to tool for developers who want to create responsive, utility-first designs quickly and efficiently.

With the introduction of container queries, Tailwind has taken things a step further by allowing you to style components based on the size of their parent container rather than the entire viewport.

Tailwind’s implementation uses a special @ syntax to differentiate container query breakpoints from traditional viewport breakpoints. For example, you can mark an element as a container with an @container class, and then use utility variants like @sm:, @md:, @lg: etc. on child elements to apply styles when the container reaches those sizes. This approach provides a convenient, utility-first way to write container query styles directly in your HTML, avoiding the need to write custom @container at-rules manually.

Why do container queries matter?

They allow truly component-driven responsive design. Rather than relying only on global breakpoints, each component can adjust itself based on the space available to it. This leads to more flexible and robust layouts. For example, a sidebar widget could reflow its content if the sidebar is narrow, or a card component could change its layout depending on whether it’s in a wide or narrow column, without affecting the rest of the page.

Tailwind Container Queries make it easy to achieve this, and they offer benefits over traditional media queries in certain scenarios. You can write more modular CSS — styles that are scoped to the component’s container — which reduces the risk of global CSS conflicts and makes components easier to reuse.wind Container Queries to build more resilient responsive designs.

Getting Started with Tailwind Container Queries

Before using container queries in Tailwind, you need to ensure you have the right setup:

For example:

// tailwind.config.js
module.exports = {
  // ... other config ...
  plugins: [
    require('@tailwindcss/container-queries'),
    // ... other plugins ...
  ],
}

Once on the latest Tailwind (or with the plugin enabled), you’re ready to use container queries.

  • Marking a Container: To use a container query, first designate an element as a container. This is done by adding the @container utility class to that element. For example:

    <div class="@container ...">
      ... content ...
    </div>

    Behind the scenes, this applies the necessary CSS (like container-type: inline-size;) to make the element a query container. Only direct child elements (and their descendants) of this container can respond to its size.

  • Using Container Query Variants: Once an element is marked with @container, you can apply responsive variants to its children using the @ prefix syntax. This works similarly to Tailwind’s regular breakpoints but is relative to the container’s width instead of the viewport. For example, @md:text-center would center text only when the container is at least the md size. In raw HTML, it looks like:

    <div class="@container">
      <p class="text-base @md:text-xl">
        This text is base size normally, but larger when the container is ≥ md.
      </p>
    </div>
    

    In this snippet, the <p> tag will use text-base by default, and switch to text-xl when its parent container’s width reaches the md breakpoint (which by Tailwind’s defaults is 28rem, or 448px). Tailwind’s JIT engine generates the necessary CSS @container rules under the hood. By default, Tailwind provides container breakpoints named @xs through @7xl, with sizes from 20rem (320px) up to 80rem (1280px). These defaults are inspired by common content widths and correspond to typical component sizes.

  • Customizing Breakpoints (Optional): If the default container sizes don’t fit your needs, you can customize them in your Tailwind config. In your tailwind.config.js, under the theme extend, you can add a containers section. For example:

    // tailwind.config.js
    module.exports = {
      theme: {
        extend: {
          containers: {
            '2xs': '16rem',   // add a smaller breakpoint at 16rem
            'lg': '40rem',    // override lg to 40rem if desired
            // ...other overrides...
          }
        }
      }
    }

    This will adjust or add to the breakpoints that @container utilities recognize. You can also use arbitrary values on the fly. Tailwind allows syntax like @[500px]:bg-red-100 to apply a style when the container is at least 500px wide. This gives you fine-grained control without editing the config for one-off cases.

  • @container Variants and Behavior: By default, Tailwind’s container query variants target minimum width (min-width) conditions – i.e., @md: means “when container width is at least md”. As of Tailwind v4, there is also support for max-width container queries using the @max-... prefix. For example, @max-md:bg-blue-50 would apply a background color when the container is at most md width. You can even combine min and max to target a specific range (similar to using between breakpoints), e.g. @min-sm:@max-lg:text-red-500 would apply red text only when the container is between the sm and lg sizes. These give you flexibility to handle both “at least X” and “at most X” scenarios, akin to typical min-width and max-width media queries.

  • Named Containers (Optional): In more complex layouts, you might have multiple containers and want to target one specifically. Tailwind allows naming a container by suffixing the class. For example:

    <div class="@container/main"> ... </div>

    This names the container “main”. Now inside this container’s descendants, you can target only this specific container by including the name in the variant, for instance: <div class="@lg/main:flex">...</div> will apply flex layout when the "main" container is ≥ lg. Named containers are useful if a child element is nested inside multiple container contexts or if you have sibling containers on the page. It avoids ambiguity by explicitly tying styles to the container you intend.

  • Removing a Container: If you want to disable container behavior on an element (for example, to override an inherited container in nested structures), Tailwind provides @container-normal to turn off container querying on that element. This is less commonly needed, but good to know it exists for edge cases.

With the basics enabled and understood, you can now start using container queries in your Tailwind HTML just as you would use other responsive utilities. The next section will walk through concrete examples to illustrate how this works in practice.

Examples and Use Cases

Let’s get into some practical examples of using Tailwind Container Queries.

Example 1: Responsive Card Component (Adjusting Layout by Container Width)

One classic use case is a card component that should display differently depending on how much horizontal space it has. Suppose we have a card with an image and some text content. In a narrow layout, we want the image to stack on top of the text (vertical layout). In a wider layout, we want the image and text to sit side-by-side (horizontal layout). Traditionally, you might handle this with viewport breakpoints, but if this card appears in various contexts (maybe in a sidebar vs. in a full-width grid), a container query can make the card adapt to its parent’s width automatically.

Step 1: Mark up the base card structure. We’ll start with a simple HTML structure for the card, using basic Tailwind classes for styling (padding, background, etc.) without any responsive behavior yet:

<!-- Base Card Component Structure -->
<div class="bg-white shadow rounded p-4">
  <img src="https://via.placeholder.com/300" alt="Card image" class="w-full rounded-md">
  <div class="mt-4">
    <h2 class="text-lg font-semibold">Card Title</h2>
    <p class="mt-2 text-gray-700">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
    <button class="mt-4 bg-blue-600 text-white py-2 px-4 rounded">Read More</button>
  </div>
</div>

In this base version, the card has a white background, drop shadow, and padding. The image is full width (w-full) and appears above the text. The text section has a top margin (mt-4) to give space under the image. Right now, this card will always stack the image above the text, which is ideal for narrow containers, but in wider contexts we might want a different layout.

Step 2: Make the card a container for responsive queries. To enable container queries on this card, we add the @container class to the outermost <div> of the component. This turns the card into a query container that we can target for width-based conditions:

<div class="@container bg-white shadow rounded p-4">
  <!-- ... card content ... -->
</div>

With this change, the card component itself now monitors its own width and can trigger child style changes when it hits certain breakpoints.

Step 3: Apply container query variants to adjust layout. We want the layout to switch to a side-by-side format when the card is sufficiently wide. A common approach is to use a flexbox: in a wide state, make the container flex-row so image and text sit side by side; in a narrow state, keep it in column (block) layout. We’ll use a container query variant on a flex utility. Specifically, we can wrap the image and text in a container (like the card itself, or we can make the card flex directly). For simplicity, we can make the card’s inner wrapper (currently the outermost <div> itself) switch display modes. By default it’s block (or we can explicitly set flex flex-col for clarity), and at a certain container width, we switch to flex-row. Here, let’s use the md breakpoint as the threshold:

<div class="@container bg-white shadow rounded p-4 **flex flex-col @md:flex-row**">
  <img src="..." alt="Card image" class="**w-full @md:w-1/3 @md:mr-4** rounded-md">
  <div class="**mt-4 @md:mt-0** flex-1">
    <!-- ... title, text, button ... -->
  </div>
</div>

Let’s break down the additions in the code above:

  • We added flex flex-col to the card container, so by default the content is laid out in a column (which keeps the image on top, text below).

  • We added the variant @md:flex-row to the same container element. This means when the card’s width reaches the md breakpoint (28rem), it will switch to a horizontal flex layout (flex-row), placing children side by side.

  • On the <img> element, we keep w-full for the base (so it spans full width in narrow mode), but add @md:w-1/3. That means when the container is ≥ md, the image will take one-third of the card’s width. We also add @md:mr-4 to give it a right margin in horizontal layout, spacing it from the text.

  • On the text container <div> that follows the image, we change the top margin: it has mt-4 by default (for vertical spacing when stacked), but @md:mt-0 removes that margin in the horizontal layout (since we don’t need top margin when items are side by side). We also give this div flex-1 so that in flex layout it will expand to fill the remaining space beside the image.

With these changes, the card component will automatically adapt. In a narrow parent (less than 448px width), it will render as image on top of text (with appropriate spacing). In a wider parent (≥448px), the card will reflow to have the image on the left and the text on the right, utilizing the space more effectively.

  • On a wide container, the card component displays its image and content side by side (horizontal layout with the image taking up one third of the width).

  • On a narrow container, the same card stacks the image above the text (vertical layout), making use of the container query to automatically switch layout without a global media query.

  • This example demonstrates a key benefit of container queries: the card is self-contained and responsive to wherever it’s placed. If you drop this card into a two-column layout, it will likely render in the side-by-side mode (if the column is wide enough). If you place it in a small sidebar, it will automatically stack vertically. We didn’t have to write special CSS for each context — the component itself handles it.

Example 2: Flexible Navigation Menu (Adapting Orientation in Different Containers)

Navigations often need to adapt depending on available space. Consider a simple navigation menu (a list of links). In a wide header area, you might want the links laid out horizontally in a row. But if that same nav component is used in a narrow side panel or on a small layout, you might want the links stacked vertically (or a different style entirely). Container queries let the nav adjust based on its container width, making the menu reusable in different parts of a site.

Step 1: Create the base nav menu structure. We’ll use a <nav> element containing an unordered list of links. We’ll style it with Tailwind to look like a simple menu. For the base state, let’s assume a vertical menu (suitable for narrow containers like a sidebar or mobile view):

<nav class="bg-gray-100 p-4">
  <ul class="flex flex-col space-y-2">
    <li><a href="#" class="block text-gray-800 hover:text-blue-600">Home</a></li>
    <li><a href="#" class="block text-gray-800 hover:text-blue-600">About</a></li>
    <li><a href="#" class="block text-gray-800 hover:text-blue-600">Services</a></li>
    <li><a href="#" class="block text-gray-800 hover:text-blue-600">Contact</a></li>
  </ul>
</nav>

Here, the <ul> is given flex flex-col to stack the <li> items vertically, with some vertical spacing (space-y-2). Each link is a block-level element with basic styling.

Step 2: Designate the nav as a container. We add the @container class to the <nav> element, because we want the nav component to respond to its own width:

<nav class="@container bg-gray-100 p-4"> ... </nav>

Now the navigation bar will act as a container for the list inside it.

Step 3: Apply container query variants for the responsive behavior. Suppose we want that when the nav is used in a wide container (for example, a full-width top navbar on desktop), the menu should display horizontally. We can achieve this by using a container breakpoint variant on the <ul> element. Let’s choose the md breakpoint again for switching layout:

<ul class="flex flex-col @md:flex-row @md:space-x-4 @md:space-y-0">
  ...
</ul>

We’ve added @md:flex-row which means if the <nav> (the container) is ≥ md (448px) wide, the <ul> will switch to a horizontal row layout. We also adjust spacing: in the base vertical mode we had space-y-2 (vertical spacing between items). In horizontal mode, we want horizontal spacing instead, so we add @md:space-x-4 and also nullify vertical space with @md:space-y-0 (to remove any vertical gap in row mode).

Additionally, we might want to style the links slightly differently in a horizontal context – for example, maybe center them or add a border for separation. For simplicity, let’s just ensure the links have some padding so they’re easy to click. We can add a utility like px-2 to each <a> for horizontal padding, which is fine in both modes. (No container variant needed for that since it’s static.)

The modified nav code becomes:

<nav class="@container bg-gray-100 p-4">
  <ul class="flex flex-col space-y-2 @md:flex-row @md:space-y-0 @md:space-x-4">
    <li><a href="#" class="block px-2 text-gray-800 hover:text-blue-600">Home</a></li>
    <li><a href="#" class="block px-2 text-gray-800 hover:text-blue-600">About</a></li>
    <li><a href="#" class="block px-2 text-gray-800 hover:text-blue-600">Services</a></li>
    <li><a href="#" class="block px-2 text-gray-800 hover:text-blue-600">Contact</a></li>
  </ul>
</nav>

Now the navigation menu will render as a vertical list in narrow containers (for instance, if you put this <nav> in a small sidebar or on a mobile view). But if you place the same nav component in a wide area (like a full-width header on desktop), once its width crosses 448px, it will automatically turn into a horizontal menu with items in a row.

You can further extend this idea. For example, using container queries, you could hide the menu items and show a “Hamburger” icon in extremely small containers, or adjust typography (e.g., maybe smaller text on narrow, larger on wide). The approach would be similar: mark the nav as @container and then use @sm:, @lg:, etc., on the elements to show/hide or style differently. The key point is the nav component is self-contained in handling its responsiveness based on where it’s used.

Example 3: Adaptive Typography and Spacing within a Container

Container queries aren’t just for big layout changes; they can also finely tune smaller design details like typography, padding, or spacing based on container size. This ensures optimal readability and aesthetics in different contexts. In this example, let's adjust a text block (like a promotional banner or card) such that its font sizes and spacing respond to the container.

Scenario: Imagine a “promo banner” component with a heading and a short paragraph. In a narrow column, we want the text to be slightly smaller and tightly spaced. In a wider container (where the banner has room to breathe), we want to increase the font size and spacing for a more open design.

Step 1: Base structure and styles. We create a simple banner with a heading, a paragraph, and maybe a call-to-action button. We’ll give it some baseline styles:

<div class="bg-blue-50 p-4 rounded @container">
  <h3 class="text-xl font-bold">Limited Time Offer</h3>
  <p class="mt-2 text-gray-800">
    Sign up now and get 50% off on your first year. Don't miss out on this exclusive deal!
  </p>
  <button class="mt-4 bg-blue-600 text-white py-2 px-4 rounded">Learn More</button>
</div>

We included @container on the outer <div> from the start, since we know we want it to be responsive to its size. At this point, the heading is text-xl (around 1.25rem) and the paragraph is default (roughly text-base or 1rem since we didn't specify a size). The spacing is modest (mt-2 for paragraph gap, mt-4 before the button).

Step 2: Apply container query variants for larger container. Let’s say when this promo banner is used in a wider container (for example, in a full-width section or large column), we want to bump up the text size to grab attention, and increase spacing a bit for a more airy layout. We can choose a breakpoint, perhaps md or lg depending on how aggressive we want the change. We’ll use lg (32rem, ~512px) to mean “if the banner’s container is at least 512px wide, use larger styles”:

<div class="bg-blue-50 p-4 rounded @container">
  <h3 class="text-xl font-bold @lg:text-3xl">Limited Time Offer</h3>
  <p class="mt-2 text-gray-800 @lg:text-lg @lg:mt-3">
    Sign up now and get 50% off on your first year. Don't miss out on this exclusive deal!
  </p>
  <button class="mt-4 bg-blue-600 text-white py-2 px-4 rounded @lg:mt-6 @lg:px-6 @lg:py-3">
    Learn More
  </button>
</div>

Changes made for wide (@lg) containers:

  • The heading <h3> gets @lg:text-3xl, making it much larger (approx 1.875rem) on large containers.

  • The paragraph <p> gets @lg:text-lg (increase font size a notch) and also @lg:mt-3 to slightly increase the space above it.

  • The button gets @lg:mt-6 to put more space before the button on large layouts, and we also increased its padding with @lg:px-6 @lg:py-3 so it’s a bit bigger and more prominent when space allows.

In a narrow container, these larger styles won’t apply, so the banner will remain more compact and modest. In a wide container, it will have a big bold headline, comfortable line spacing, and a larger button — all making use of the available space to create a more impactful design.

This technique ensures text is always readable and well-proportioned for the container it’s in. If you use this promo banner in a small sidebar, it won’t overwhelm the layout with huge text; if you use it in a hero section, it will scale up nicely without you writing separate styles for that context.

Example 4: Using Named Containers for Complex Layouts

For our last example, let's explore named containers, which are useful in more complex or nested layouts. Imagine a page layout with multiple regions, for instance a header and a main content area, both of which are containers that might have different breakpoints of interest. If a component is inside the header, you want it to respond to the header’s width; if another component is inside main content, it should respond to the main content width. By naming containers, we can target styles specifically to one container or another.

Scenario: We have a page with a top banner section and a main article section below it. Both are marked as containers with different names ("header" and "main"). Inside each, we have elements that should resize based on their respective container.

Step 1: Set up multiple named containers. We create two containers, one for the header and one for main content:

<header class="@container/header bg-gray-800 text-white p-4">
  <h1 class="text-2xl">My Blog</h1>
</header>

<main class="@container/main p-4">
  <article class="prose">
    <h2>Article Title</h2>
    <p>...article content...</p>
  </article>
</main>

Here, the <header> has class @container/header, naming it "header". The <main> has @container/main, naming it "main". For now, we have not applied any container query variants yet.

Step 2: Apply styles targeting each named container’s size. Let’s say we want the blog title (<h1>) in the header to change size based on the header width, and maybe the article text to change layout based on the main width. For instance, if the header gets really wide, we might want a larger font for the title. If the main content area is wide, maybe we want to use a two-column layout for the text or increase font size for readability.

Targeting the header container: We’ll use @lg/header:text-4xl on the <h1> to bump it up when the header container is large (≥ lg):

<h1 class="text-2xl @lg/header:text-4xl">My Blog</h1>

This will make the site title go from 2xl to 4xl when the header region is wide enough (for example, on a desktop layout).

Targeting the main container: Suppose in the main content, when it’s wide, we want to introduce a two-column layout for the article (perhaps the text and a sidebar of quotes/images). For simplicity, let's target the <article> when the main container is large. We can add a class like @xl/main:columns-2 (using Tailwind’s CSS columns utility) to break the text into two columns at an extra-large main width:

<article class="prose @xl/main:columns-2">
  ...
</article>

We could also adjust text size: maybe @lg/main:prose-xl (if using Tailwind’s typography plugin classes) or manually set something like @lg/main:text-lg on paragraphs inside. For demonstration, we’ll do a simpler tweak: increase paragraph spacing on wide main content. If not using the typography plugin, we can target paragraphs:

<p class="mt-4 @lg/main:mt-6">...</p>

This would add extra spacing between paragraphs when the main content container is large.

Bringing it together, the structure with named queries might look like:

<header class="@container/header bg-gray-800 text-white p-4">
  <h1 class="text-2xl @lg/header:text-4xl">My Blog</h1>
</header>

<main class="@container/main p-4">
  <article class="prose @xl/main:columns-2">
    <h2>Article Title</h2>
    <p class="mt-4 @lg/main:mt-6">First paragraph of the article...</p>
    <p class="mt-4 @lg/main:mt-6">Second paragraph of the article...</p>
    <!-- ... more content ... -->
  </article>
</main>

Now we have two independent container contexts on the page. The header’s @lg/header:text-4xl will only respond to the header width, and the main’s @lg/main:mt-6 (on paragraphs) responds only to the main content width. Without naming, if you just used @lg:text-4xl, the <h1> might accidentally also try to respond to the main container if it was nested, or vice versa. Naming ensures the right container is targeted.

This example is a bit contrived but it shows how named container queries allow precise control in a complex layout with multiple containers. In practice, you’ll name a container when you need to differentiate it from another container at the same level or overlapping scope. If you find yourself with unpredictable container query behavior, introducing names can resolve the ambiguity.

Best Practices for Using Container Queries

  • Use for Component-Level Adjustments:
    Container queries excel at “micro-layout” changes within components. Use them to fine-tune elements based on available space, while relying on media queries for overall page layout.

  • Plan Your Container Structure:
    Only mark key components—like cards, navbars, or sidebars—as containers. Avoid overusing the @container class to keep your code simple and maintainable.

  • Stick to Media Queries When Appropriate:
    Use media queries for changes that depend on the viewport. Reserve container queries for adjustments driven by a parent element’s size.

  • Avoid Over-Nesting Containers:
    Keep the hierarchy simple; each component should generally respond to one container. If nesting is needed, use named containers to clarify which ancestor influences which child.

  • Provide Fallbacks for Older Browsers:
    Ensure your base styles work without container queries for older browsers. This guarantees graceful degradation when container queries aren’t supported.

  • Consider Performance:
    Use container queries judiciously. Modern browsers handle them well, but don’t declare every element as a container if it’s not necessary.

  • Focus on Maintainability:
    Document your container query breakpoints and naming conventions. Consistent practices make it easier for your team to manage responsive designs.

By applying these best practices, you’ll enhance your Tailwind CSS designs with flexible, efficient, and maintainable container queries.

FAQ

How do I enable container queries in Tailwind?

Use Tailwind CSS v4.0+ or install the container queries plugin for earlier versions. Then, add the @container class to parent elements and use @ variants on children.

Can container queries work with named containers?

A: Yes, you can assign names to containers and target them with specific query variants. This helps apply styles only within the intended container context.

Are container queries limited to width-based conditions?

By default, they target container width (inline-size), but you can configure them for height or other dimensions if needed. In most cases, width is the primary focus.

Can I mix container queries with traditional responsive classes?

Yes, use media queries for overall layout and container queries for component-level adjustments. This combination offers flexible, layered responsiveness.

Yucel Faruk Sahan

Yucel is a digital product maker and content writer specializing in full-stack development. He is passionate about crafting engaging content and creating innovative solutions that bridge technology and user needs. In his free time, he enjoys discovering new technologies and drawing inspiration from the great outdoors.