On this page

Flexbox and Grid with Tailwind

14 min read TextCh. 2 — Layout and responsive

Flexbox and Grid with Tailwind

Layout is where Tailwind's utility-first approach shines most clearly. Instead of writing display: flex; align-items: center; justify-content: space-between in a stylesheet, you write flex items-center justify-between directly on the element. The mapping is immediate and readable.

Flexbox utilities

Every CSS flexbox property has a direct Tailwind equivalent.

Enabling flexbox

<div class="flex">...</div>          <!-- display: flex -->
<div class="inline-flex">...</div>   <!-- display: inline-flex -->

Direction

<div class="flex flex-row">...</div>         <!-- default: left to right -->
<div class="flex flex-col">...</div>         <!-- top to bottom (most common) -->
<div class="flex flex-row-reverse">...</div> <!-- right to left -->
<div class="flex flex-col-reverse">...</div> <!-- bottom to top -->

Alignment

justify-content controls the main axis (horizontal in a row, vertical in a column). align-items controls the cross axis.

<!-- justify-content -->
<div class="flex justify-start">...</div>    /* flex-start */
<div class="flex justify-center">...</div>   /* center */
<div class="flex justify-end">...</div>      /* flex-end */
<div class="flex justify-between">...</div>  /* space-between */
<div class="flex justify-around">...</div>   /* space-around */
<div class="flex justify-evenly">...</div>   /* space-evenly */

<!-- align-items -->
<div class="flex items-start">...</div>      /* flex-start */
<div class="flex items-center">...</div>     /* center */
<div class="flex items-end">...</div>        /* flex-end */
<div class="flex items-stretch">...</div>    /* stretch (default) */
<div class="flex items-baseline">...</div>   /* baseline */

Wrapping and gap

<!-- Wrap: children move to next line when they overflow -->
<div class="flex flex-wrap gap-4">
  <div class="w-32">Card 1</div>
  <div class="w-32">Card 2</div>
  <div class="w-32">Card 3</div>
  <!-- Cards wrap to next line when container is too narrow -->
</div>

<!-- Gap: space between flex/grid items -->
<div class="flex gap-4">...</div>      /* 1rem gap on both axes */
<div class="flex gap-x-4 gap-y-2">...</div>  /* different axes */

Flex grow, shrink, and basis

<!-- flex-1: item grows and shrinks equally, ignoring initial size -->
<div class="flex gap-4">
  <div class="flex-1 bg-indigo-100 p-4">Grows</div>
  <div class="flex-1 bg-purple-100 p-4">Grows equally</div>
  <div class="w-48 shrink-0 bg-gray-100 p-4">Fixed width, never shrinks</div>
</div>

<!-- flex-none: don't grow or shrink (shrink-0 equivalent but also no grow) -->
<img class="flex-none w-16 h-16 rounded-full" src="/avatar.jpg" alt="Avatar" />

<!-- Order: visually reorder flex children -->
<div class="flex">
  <div class="order-3">Shows third</div>
  <div class="order-1">Shows first</div>
  <div class="order-2">Shows second</div>
</div>

Practical flex pattern: media object

The media object pattern (icon/image left, text right) is one of the most common UI patterns:

<div class="flex items-start gap-4 p-4 bg-white rounded-xl border border-gray-100">
  <!-- Icon on the left, never shrinks -->
  <div class="shrink-0 w-12 h-12 rounded-full bg-indigo-100 flex items-center justify-center">
    <svg class="w-6 h-6 text-indigo-600" fill="currentColor" viewBox="0 0 24 24">
      <path d="M12 12c2.7 0 4.8-2.1 4.8-4.8S14.7 2.4 12 2.4 7.2 4.5 7.2 7.2 9.3 12 12 12zm0 2.4c-3.2 0-9.6 1.6-9.6 4.8v2.4h19.2v-2.4c0-3.2-6.4-4.8-9.6-4.8z"/>
    </svg>
  </div>
  <!-- Text grows to fill available space -->
  <div class="flex-1 min-w-0">
    <p class="font-semibold text-gray-900 truncate">Alice Johnson</p>
    <p class="text-sm text-gray-500">Replied to your comment</p>
    <p class="mt-1 text-sm text-gray-700">"Great article! The examples are very clear."</p>
  </div>
  <!-- Timestamp, pinned right -->
  <time class="shrink-0 text-xs text-gray-400">2h ago</time>
</div>

CSS Grid utilities

CSS Grid is for two-dimensional layouts — rows AND columns simultaneously.

Enabling grid and defining columns

<div class="grid grid-cols-3">...</div>         /* 3 equal columns */
<div class="grid grid-cols-4 gap-6">...</div>   /* 4 columns with gap */

<!-- Responsive column count (most common pattern) -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
  ...
</div>

Column spanning

<!-- col-span-N: item occupies N columns -->
<div class="grid grid-cols-6 gap-4">
  <div class="col-span-4 bg-blue-100 p-4">Main content (4 columns)</div>
  <div class="col-span-2 bg-gray-100 p-4">Sidebar (2 columns)</div>
</div>

<!-- col-span-full: item spans all columns -->
<div class="grid grid-cols-3 gap-4">
  <div class="col-span-full bg-indigo-100 p-4">Full-width header</div>
  <div class="bg-white p-4">Card 1</div>
  <div class="bg-white p-4">Card 2</div>
  <div class="bg-white p-4">Card 3</div>
</div>

Row spanning and row definition

<!-- row-span-N: item occupies N rows -->
<div class="grid grid-cols-2 grid-rows-2 gap-4 h-64">
  <div class="row-span-2 bg-indigo-500 p-4 text-white">Tall left panel</div>
  <div class="bg-purple-300 p-4">Top right</div>
  <div class="bg-purple-200 p-4">Bottom right</div>
</div>

Auto-fit responsive grids

Tailwind v4 supports arbitrary values for grid templates. For a self-wrapping grid where columns size automatically:

<!-- Repeating auto-fit grid: items fill the container and wrap -->
<div class="grid gap-6 [grid-template-columns:repeat(auto-fit,minmax(280px,1fr))]">
  <div class="bg-white rounded-xl p-6 shadow">Card 1</div>
  <div class="bg-white rounded-xl p-6 shadow">Card 2</div>
  <div class="bg-white rounded-xl p-6 shadow">Card 3</div>
  <div class="bg-white rounded-xl p-6 shadow">Card 4</div>
</div>

Container queries (Tailwind v4)

Container queries let a component respond to the size of its parent container rather than the viewport. This is perfect for components that appear in different contexts (sidebar, main column, modal).

<!-- 1. Mark the parent as a container -->
<div class="@container">

  <!-- 2. Use @sm:, @md:, @lg: on the child -->
  <div class="grid grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-3 gap-4">
    <div class="bg-white p-4 rounded-lg shadow">Card</div>
    <div class="bg-white p-4 rounded-lg shadow">Card</div>
    <div class="bg-white p-4 rounded-lg shadow">Card</div>
  </div>

</div>

When the .@container div is 400px wide, the grid shows 1 column. When it's 640px wide, it shows 2 columns — regardless of the viewport width.

Holy Grail layout with Grid

The "holy grail" layout (header, two sidebars, main content, footer) is trivial with CSS Grid:

<div class="grid grid-cols-[220px_1fr_180px] grid-rows-[auto_1fr_auto] min-h-screen">
  <header class="col-span-3 bg-white border-b border-gray-200 px-6 py-4 flex items-center">
    <span class="font-bold text-lg">Logo</span>
  </header>

  <aside class="bg-gray-50 border-r border-gray-200 p-4">
    <nav>Left Sidebar</nav>
  </aside>

  <main class="p-6 overflow-auto">
    Main Content
  </main>

  <aside class="bg-gray-50 border-l border-gray-200 p-4">
    Right Sidebar
  </aside>

  <footer class="col-span-3 bg-white border-t border-gray-200 px-6 py-4 text-sm text-gray-500">
    Footer
  </footer>
</div>

Practice

  1. Build a dashboard sidebar layout: fixed sidebar (w-64) on the left and a scrollable main content area on the right using flex.
  2. Create a photo gallery using grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 with square image cells using aspect-square.
  3. Wrap the gallery in a @container and replace the viewport breakpoints with @sm:grid-cols-2 @lg:grid-cols-4.

Use gap instead of margin for flex/grid spacing
gap-4 on a flex or grid container adds space between children without adding outer margin to the first or last item. This is cleaner and more predictable than adding margin to individual children.
Container queries are built into Tailwind v4
Tailwind v4 adds built-in support for CSS Container Queries. Mark a container with @container, then use @sm:, @md:, @lg: prefixes on children to respond to the container's size rather than the viewport. This is perfect for reusable card components.
<!-- Responsive navigation: logo left, links center, actions right -->
<nav class="flex items-center justify-between px-6 py-4 bg-white border-b border-gray-100 shadow-sm">

  <!-- Logo -->
  <a href="/" class="text-xl font-bold text-indigo-600 tracking-tight">
    MyApp
  </a>

  <!-- Center links (hidden on mobile) -->
  <ul class="hidden md:flex items-center gap-6 list-none m-0 p-0">
    <li><a href="/features" class="text-sm text-gray-600 hover:text-indigo-600 transition-colors">Features</a></li>
    <li><a href="/pricing" class="text-sm text-gray-600 hover:text-indigo-600 transition-colors">Pricing</a></li>
    <li><a href="/docs" class="text-sm text-gray-600 hover:text-indigo-600 transition-colors">Docs</a></li>
  </ul>

  <!-- Right side actions -->
  <div class="flex items-center gap-3">
    <a href="/login" class="text-sm font-medium text-gray-700 hover:text-indigo-600 transition-colors">
      Sign in
    </a>
    <a href="/signup" class="bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-semibold px-4 py-2 rounded-lg transition-colors">
      Get started
    </a>
  </div>
</nav>