Skip to content
Codeloom
Tailwind

Responsive Design with Tailwind CSS

Build responsive layouts with Tailwind CSS: mobile-first breakpoints, responsive utilities, container queries, and practical patterns for real-world responsive components.

·7 min read · By Codeloom
Intermediate 12 min read

What you'll learn

  • How Tailwind breakpoints and mobile-first design work
  • Responsive typography, spacing, and grid patterns
  • Container queries for component-level responsiveness
  • Building a responsive navbar, card grid, and dashboard
  • Common responsive pitfalls and how to avoid them

Prerequisites

  • Basic Tailwind CSS knowledge (utility classes)
  • Understanding of CSS flexbox and grid
  • HTML fundamentals

Tailwind CSS takes a mobile-first approach to responsive design. You write styles for mobile by default, then layer on changes at larger breakpoints. This means every utility class without a breakpoint prefix applies to all screen sizes, and prefixed utilities only kick in at that breakpoint and above.

Default breakpoints

Tailwind ships with five breakpoints:

PrefixMin widthTypical target
sm640pxLarge phones, small tablets
md768pxTablets
lg1024pxLaptops
xl1280pxDesktops
2xl1536pxLarge screens
<!-- Mobile: single column -->
<!-- md and up: two columns -->
<!-- lg and up: three columns -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
  <div class="bg-white p-4 rounded shadow">Card 1</div>
  <div class="bg-white p-4 rounded shadow">Card 2</div>
  <div class="bg-white p-4 rounded shadow">Card 3</div>
</div>

Mobile-first thinking

Write the mobile layout first, then add breakpoint prefixes for larger screens. This is the opposite of the “desktop first, then fix mobile” approach.

<!-- Start with mobile styles (no prefix) -->
<!-- Add tablet and desktop overrides -->
<section class="px-4 py-8 md:px-8 md:py-12 lg:px-16 lg:py-16">
  <h1 class="text-2xl font-bold md:text-3xl lg:text-5xl">
    Welcome to our platform
  </h1>
  <p class="mt-4 text-base text-gray-600 md:text-lg lg:text-xl lg:max-w-2xl">
    Build something amazing with our tools and APIs.
  </p>
  <div class="mt-6 flex flex-col gap-3 sm:flex-row sm:gap-4">
    <a href="/signup" class="rounded bg-blue-600 px-6 py-3 text-center text-white hover:bg-blue-700">
      Get started
    </a>
    <a href="/docs" class="rounded border border-gray-300 px-6 py-3 text-center hover:bg-gray-50">
      Read docs
    </a>
  </div>
</section>

On mobile: stacked layout, smaller text, full-width buttons stacked vertically. On tablet and up: more padding, larger text, buttons side by side.

Responsive navigation

A common pattern: horizontal nav on desktop, hamburger menu on mobile.

<nav class="bg-white shadow">
  <div class="mx-auto max-w-7xl px-4">
    <div class="flex h-16 items-center justify-between">
      <!-- Logo -->
      <a href="/" class="text-xl font-bold">Logo</a>
      
      <!-- Desktop nav (hidden on mobile) -->
      <div class="hidden md:flex md:items-center md:gap-6">
        <a href="/products" class="text-gray-700 hover:text-blue-600">Products</a>
        <a href="/pricing" class="text-gray-700 hover:text-blue-600">Pricing</a>
        <a href="/docs" class="text-gray-700 hover:text-blue-600">Docs</a>
        <a href="/login" class="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700">
          Sign in
        </a>
      </div>
      
      <!-- Mobile menu button (hidden on desktop) -->
      <button class="md:hidden rounded p-2 hover:bg-gray-100" id="menu-btn">
        <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
        </svg>
      </button>
    </div>
    
    <!-- Mobile menu (shown when toggled) -->
    <div class="md:hidden border-t py-3 hidden" id="mobile-menu">
      <a href="/products" class="block py-2 text-gray-700">Products</a>
      <a href="/pricing" class="block py-2 text-gray-700">Pricing</a>
      <a href="/docs" class="block py-2 text-gray-700">Docs</a>
      <a href="/login" class="mt-2 block rounded bg-blue-600 px-4 py-2 text-center text-white">
        Sign in
      </a>
    </div>
  </div>
</nav>

The hidden md:flex pattern hides the desktop nav on mobile and shows it from md up. The md:hidden pattern does the inverse for the hamburger button.

Responsive grid patterns

Dashboard layout

<div class="min-h-screen bg-gray-100">
  <!-- Sidebar: hidden on mobile, fixed on desktop -->
  <aside class="fixed inset-y-0 left-0 z-30 hidden w-64 bg-white shadow lg:block">
    <div class="p-6">
      <h2 class="text-lg font-bold">Dashboard</h2>
      <nav class="mt-6 space-y-2">
        <a href="#" class="block rounded px-3 py-2 bg-blue-50 text-blue-700">Overview</a>
        <a href="#" class="block rounded px-3 py-2 text-gray-600 hover:bg-gray-50">Analytics</a>
        <a href="#" class="block rounded px-3 py-2 text-gray-600 hover:bg-gray-50">Settings</a>
      </nav>
    </div>
  </aside>
  
  <!-- Main content: full width on mobile, offset on desktop -->
  <main class="lg:ml-64">
    <div class="p-4 md:p-6 lg:p-8">
      <!-- Stats grid -->
      <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
        <div class="rounded-lg bg-white p-6 shadow">
          <p class="text-sm text-gray-500">Revenue</p>
          <p class="mt-1 text-2xl font-bold">$45,231</p>
        </div>
        <div class="rounded-lg bg-white p-6 shadow">
          <p class="text-sm text-gray-500">Users</p>
          <p class="mt-1 text-2xl font-bold">2,350</p>
        </div>
        <div class="rounded-lg bg-white p-6 shadow">
          <p class="text-sm text-gray-500">Orders</p>
          <p class="mt-1 text-2xl font-bold">1,247</p>
        </div>
        <div class="rounded-lg bg-white p-6 shadow">
          <p class="text-sm text-gray-500">Conversion</p>
          <p class="mt-1 text-2xl font-bold">3.2%</p>
        </div>
      </div>
      
      <!-- Chart and table: stack on mobile, side by side on desktop -->
      <div class="mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3">
        <div class="rounded-lg bg-white p-6 shadow lg:col-span-2">
          <h3 class="font-semibold">Revenue Chart</h3>
          <!-- chart goes here -->
        </div>
        <div class="rounded-lg bg-white p-6 shadow">
          <h3 class="font-semibold">Recent Activity</h3>
          <!-- activity list goes here -->
        </div>
      </div>
    </div>
  </main>
</div>

Product card grid

<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
  <article class="group overflow-hidden rounded-lg bg-white shadow transition hover:shadow-lg">
    <div class="aspect-square overflow-hidden">
      <img
        src="/product.jpg"
        alt="Product"
        class="h-full w-full object-cover transition group-hover:scale-105"
      />
    </div>
    <div class="p-4">
      <h3 class="font-semibold text-gray-900">Product Name</h3>
      <p class="mt-1 text-sm text-gray-500 line-clamp-2">
        A short description of the product that might be long.
      </p>
      <div class="mt-3 flex items-center justify-between">
        <span class="text-lg font-bold text-blue-600">$29.99</span>
        <button class="rounded bg-blue-600 px-3 py-1.5 text-sm text-white hover:bg-blue-700">
          Add to cart
        </button>
      </div>
    </div>
  </article>
  <!-- more cards -->
</div>

Responsive typography

<!-- Scale text with breakpoints -->
<h1 class="text-3xl font-bold sm:text-4xl md:text-5xl lg:text-6xl">
  Big headline
</h1>

<!-- Responsive line length for readability -->
<p class="max-w-prose text-base md:text-lg">
  Long paragraph text that should not stretch too wide on large screens.
  The max-w-prose utility caps the width at 65 characters, which is
  the optimal line length for reading.
</p>

Responsive visibility

Show and hide elements at different breakpoints.

<!-- Only visible on mobile -->
<div class="block sm:hidden">Mobile only content</div>

<!-- Hidden on mobile, visible from sm up -->
<div class="hidden sm:block">Tablet and desktop content</div>

<!-- Only visible on lg screens -->
<div class="hidden lg:block xl:hidden">Laptop only</div>

Container queries

Tailwind v3.4+ supports container queries, which let components respond to their container’s size rather than the viewport.

<!-- Mark the container -->
<div class="@container">
  <div class="flex flex-col @md:flex-row @md:items-center gap-4">
    <img src="/avatar.jpg" class="h-16 w-16 @md:h-20 @md:w-20 rounded-full" />
    <div>
      <h3 class="text-lg @md:text-xl font-semibold">User Name</h3>
      <p class="text-sm text-gray-500 @md:text-base">Software Engineer</p>
    </div>
  </div>
</div>

Container queries are useful for reusable components that might appear in a sidebar (narrow) or main content area (wide). The component adapts to its container, not the viewport.

Custom breakpoints

Add custom breakpoints in tailwind.config.js:

// tailwind.config.js
module.exports = {
  theme: {
    screens: {
      xs: "475px",
      sm: "640px",
      md: "768px",
      lg: "1024px",
      xl: "1280px",
      "2xl": "1536px",
    },
  },
};

Or extend the defaults:

module.exports = {
  theme: {
    extend: {
      screens: {
        xs: "475px",
        "3xl": "1920px",
      },
    },
  },
};

Common mistakes

Designing desktop first. With Tailwind, unprefixed styles apply to all sizes. If you write the desktop layout first and then try to override for mobile, you end up fighting your own styles.

Too many breakpoints. Most layouts need two or three breakpoints. Using all five on every element creates noise. Start with md (tablet) and lg (desktop); add others only when needed.

Forgetting touch targets on mobile. Buttons and links need at least 44x44px touch targets on mobile. Padding utilities like py-3 px-4 help.

Ignoring landscape orientation. On mobile, landscape mode has a short viewport height. Avoid fixed-height layouts that assume portrait orientation.

Responsive design in Tailwind is about layering breakpoint prefixes onto a solid mobile foundation. Start with the smallest screen, make it look good, then add prefixes to adapt for larger screens. The utility-first approach makes responsive changes explicit and co-located with the elements they affect.