Skip to main content

Container Queries

In this chapter, you learn about container queries, which gained native support in Tailwind CSS v4.

What are container queries

Container queries are a feature that applies styles based on the size of the parent container rather than the viewport.

<!-- Traditional media query: depends on the viewport width -->
<div class="md:flex">...</div>

<!-- Container query: depends on the container width -->
<div class="@container">
<div class="@md:flex">...</div>
</div>

Basic usage

Defining a container

Define a container with the @container class.

<div class="@container">
<div class="@md:flex @lg:grid @lg:grid-cols-2">
A layout that responds to the container size
</div>
</div>

Container breakpoints

PrefixMin width
@3xs16rem (256px)
@2xs18rem (288px)
@xs20rem (320px)
@sm24rem (384px)
@md28rem (448px)
@lg32rem (512px)
@xl36rem (576px)
@2xl42rem (672px)
@3xl48rem (768px)
@4xl56rem (896px)
@5xl64rem (1024px)
@6xl72rem (1152px)
@7xl80rem (1280px)
<div class="@container">
<div class="block @sm:flex @lg:grid @lg:grid-cols-3">
A responsive layout
</div>
</div>

Named containers

You can give containers names to distinguish between multiple ones.

<div class="@container/main">
<div class="@container/sidebar">
<div class="@lg/main:flex @md/sidebar:block">
Reference named containers
</div>
</div>
</div>

@max-* variants

With the @max-* variants, you can apply styles when the container width is below a specified value.

<div class="@container">
<div class="flex flex-row @max-md:flex-col">
Stacked vertically when the container width is below 448px (28rem)
</div>
</div>

By stacking it with a regular variant, as in @sm:@max-md:flex, you can also specify a range.

Arbitrary values

<div class="@container">
<div class="@[500px]:flex">
flex at 500px and up
</div>
</div>

Container queries vs media queries

The problem with media queries

<!-- Media query: with a sidebar, cards go side by side even when narrow -->
<div class="grid grid-cols-1 md:grid-cols-2">
<Card />
<Card />
</div>

The container query solution

<!-- Container query: change the layout based on the card's own width -->
<div class="@container">
<div class="block @md:flex gap-4">
<Card />
<Card />
</div>
</div>

Practical examples

Responsive card

<article class="@container bg-white rounded-xl shadow-lg overflow-hidden">
<div class="@md:flex">
<!-- Image -->
<div class="@md:w-1/3 @lg:w-1/4">
<img
src="..."
class="w-full h-48 @md:h-full object-cover"
/>
</div>
<!-- Content -->
<div class="p-6 @md:w-2/3 @lg:w-3/4">
<span class="text-sm text-blue-600 font-semibold">Category</span>
<h2 class="mt-2 text-xl @lg:text-2xl font-bold">Title</h2>
<p class="mt-2 text-gray-600 @lg:text-lg">
The description goes here...
</p>
<div class="mt-4 flex flex-col @sm:flex-row gap-2">
<button class="px-4 py-2 bg-blue-500 text-white rounded-sm">
Details
</button>
<button class="px-4 py-2 border border-gray-300 rounded-sm">
Save
</button>
</div>
</div>
</div>
</article>

Dashboard widget

<div class="@container bg-white rounded-lg shadow-sm p-4">
<div class="@xs:flex @xs:justify-between @xs:items-center mb-4">
<h3 class="font-bold text-lg">Sales statistics</h3>
<span class="text-sm text-gray-500">Last 30 days</span>
</div>

<div class="grid grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-4 gap-4">
<div class="p-4 bg-blue-50 rounded-lg">
<p class="text-sm text-blue-600">Total sales</p>
<p class="text-2xl font-bold">¥1,234,567</p>
</div>
<div class="p-4 bg-green-50 rounded-lg">
<p class="text-sm text-green-600">Orders</p>
<p class="text-2xl font-bold">1,234</p>
</div>
<div class="p-4 bg-yellow-50 rounded-lg">
<p class="text-sm text-yellow-600">Average price</p>
<p class="text-2xl font-bold">¥12,345</p>
</div>
<div class="p-4 bg-purple-50 rounded-lg">
<p class="text-sm text-purple-600">Repeat rate</p>
<p class="text-2xl font-bold">45.6%</p>
</div>
</div>
</div>

Sidebar + main content

<div class="flex">
<!-- Sidebar -->
<aside class="w-64 shrink-0">
<nav class="p-4">Sidebar</nav>
</aside>

<!-- Main content (uses container queries) -->
<main class="flex-1 @container p-4">
<div class="grid grid-cols-1 @md:grid-cols-2 @xl:grid-cols-3 gap-6">
<!-- Card -->
<div class="@container bg-white rounded-lg shadow-sm">
<div class="@xs:flex p-4">
<img src="..." class="w-full @xs:w-20 h-20 object-cover rounded-sm" />
<div class="mt-2 @xs:mt-0 @xs:ml-4">
<h3 class="font-bold">Title</h3>
<p class="text-sm text-gray-600">Description...</p>
</div>
</div>
</div>
<!-- Repeat -->
</div>
</main>
</div>

Product card

<div class="@container group bg-white rounded-xl shadow-lg overflow-hidden">
<!-- Image section -->
<div class="relative @lg:flex">
<div class="@lg:w-1/2">
<img
src="..."
class="w-full aspect-square @lg:aspect-auto @lg:h-full object-cover"
/>
</div>

<!-- Details section -->
<div class="p-6 @lg:w-1/2 @lg:flex @lg:flex-col @lg:justify-center">
<div class="flex items-center gap-2 mb-2">
<span class="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full">
New
</span>
<span class="text-sm text-gray-500">SKU: 12345</span>
</div>

<h2 class="text-xl @md:text-2xl font-bold">
Product name
</h2>

<p class="mt-2 text-gray-600 @md:text-lg line-clamp-3 @lg:line-clamp-none">
The detailed product description goes here. Long text is
clamped in small containers.
</p>

<div class="mt-4 @sm:flex @sm:items-center @sm:justify-between">
<div>
<span class="text-2xl font-bold text-blue-600">¥12,800</span>
<span class="ml-2 text-sm text-gray-400 line-through">¥15,800</span>
</div>
<button class="mt-4 @sm:mt-0 w-full @sm:w-auto px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg">
Add to cart
</button>
</div>
</div>
</div>
</div>

Search result item

<div class="@container">
<article class="@md:flex gap-4 p-4 bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow">
<!-- Thumbnail -->
<div class="@md:w-48 @md:shrink-0">
<img
src="..."
class="w-full h-48 @md:h-32 object-cover rounded-sm"
/>
</div>

<!-- Content -->
<div class="mt-4 @md:mt-0 flex-1">
<div class="@lg:flex @lg:justify-between @lg:items-start">
<div>
<h3 class="font-bold text-lg hover:text-blue-600">
Search result title
</h3>
<p class="mt-1 text-sm text-gray-500">
Posted by: Username • 3 hours ago
</p>
</div>
<div class="mt-2 @lg:mt-0 flex gap-2">
<span class="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded-sm">
Tag 1
</span>
<span class="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded-sm">
Tag 2
</span>
</div>
</div>

<p class="mt-2 text-gray-600 line-clamp-2 @lg:line-clamp-3">
The search result description goes here...
</p>

<div class="mt-4 flex items-center gap-4 text-sm text-gray-500">
<span>👍 123</span>
<span>💬 45</span>
<span>👁 1.2k</span>
</div>
</div>
</article>
</div>

Container query best practices

1. Make components self-contained

<!-- Good: define the container inside the component -->
<div class="@container">
<div class="@md:flex">...</div>
</div>

<!-- Avoid: depending on the parent -->
<div class="md:flex">...</div>

2. Choose appropriate breakpoints

<!-- Breakpoints matched to the component's size -->
<div class="@container max-w-md">
<div class="@xs:flex">For a small container</div>
</div>

<div class="@container max-w-4xl">
<div class="@lg:grid @lg:grid-cols-3">For a large container</div>
</div>

3. Be careful with nesting

<!-- Be explicit with named containers -->
<div class="@container/outer">
<div class="@container/inner">
<div class="@md/outer:flex @sm/inner:block">
An explicit reference
</div>
</div>
</div>

Summary

  • Define a container with @container
  • Use @sm: / @md: / @lg:, etc. for styles that respond to the container size
  • Specify upper bounds with @max-* variants such as @max-md:
  • Distinguish between multiple containers with named containers
  • Ideal for component-based development
  • Can be combined with media queries