Common Flexbox Patterns

โ–ถ Try It Yourself

Knowing the individual Flexbox properties is valuable; knowing which combination solves a specific layout problem is what makes you productive. In this lesson you will work through the most common real-world Flexbox patterns โ€” the holy grail layout, responsive navigation, media objects, and card decks with pinned footers โ€” understanding exactly why each property is chosen.

Most Common Flexbox Patterns

Pattern Key Properties Typical Use Case
Perfect center align-items: center; justify-content: center; Hero sections, modals, empty states
Sidebar + main flex: 0 0 Npx sidebar, flex: 1 main Dashboard, docs, admin panels
Navbar split justify-content: space-between or margin-left: auto Site header navigation
Media object display: flex; gap: Npx; align-items: flex-start; Comments, notifications, list items
Pinned footer card Column flex on card + margin-top: auto on footer Pricing cards, product tiles
Sticky header / footer in scroll flex-shrink: 0 on header/footer, flex: 1; overflow-y: auto on body Chat windows, app shells

Holy Grail Layout Properties

Region flex Value Reason
Header flex-shrink: 0 Never shrinks โ€” always full height
Left sidebar flex: 0 0 200px Fixed width โ€” never grows or shrinks
Main content flex: 1 Takes all remaining horizontal space
Right sidebar flex: 0 0 180px Fixed width โ€” never grows or shrinks
Footer flex-shrink: 0 Never shrinks โ€” always visible

Media Object Variants

Variant Key Difference Property Used
Standard Image left, text right flex-direction: row (default)
Reversed Text left, image right flex-direction: row-reverse
Stacked (mobile) Image above text flex-direction: column at narrow viewport
Top-aligned Image doesn’t stretch to text height align-items: flex-start
Note: The media object pattern (image + text side by side) is one of the oldest and most common web layout patterns. With Flexbox it requires just four lines of CSS โ€” display: flex, gap, align-items: flex-start, and flex-shrink: 0 on the image โ€” replacing table-based hacks that dominated for years.
Tip: For a scrollable chat or log window where the header and input bar must stay visible: make the outer shell a column flex container, give header and footer flex-shrink: 0, and give the message list flex: 1; overflow-y: auto. The browser handles the rest.
Warning: Avoid deeply nesting flex containers unnecessarily. Every display: flex creates a new formatting context. If a pattern can be achieved with a single flex container, don’t split it into two โ€” simpler CSS is easier to maintain, debug, and understand.

Basic Example

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Common Flexbox Patterns</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; padding: 32px; background: #f8fafc; margin: 0; }
    section { margin-bottom: 40px; }
    h2 { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.06em;
         color: #64748b; margin: 0 0 12px; }

    /* โ”€โ”€ 1. Perfect center โ”€โ”€ */
    .center-demo {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 140px;
      background: linear-gradient(135deg, #4f46e5, #7c3aed);
      border-radius: 12px;
      color: white;
      font-weight: 700;
      font-size: 1.1rem;
    }

    /* โ”€โ”€ 2. Media object โ”€โ”€ */
    .media {
      display: flex;
      gap: 16px;
      align-items: flex-start;
      background: white;
      border: 1px solid #e2e8f0;
      border-radius: 12px;
      padding: 20px;
      margin-bottom: 12px;
    }
    .media-img {
      width: 56px;
      height: 56px;
      border-radius: 50%;
      background: #ede9fe;
      color: #4f46e5;
      font-weight: 700;
      font-size: 1.1rem;
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;         /* never shrinks */
    }
    .media-body h3 { margin: 0 0 4px; font-size: 0.95rem; color: #0f172a; }
    .media-body p  { margin: 0; font-size: 0.875rem; color: #64748b; line-height: 1.5; }

    /* โ”€โ”€ 3. Card with pinned footer โ”€โ”€ */
    .card-deck { display: flex; gap: 16px; }
    .pricing-card {
      flex: 1;
      display: flex;
      flex-direction: column;
      background: white;
      border: 1px solid #e2e8f0;
      border-radius: 16px;
      padding: 24px;
    }
    .pricing-card .card-name  { font-weight: 700; font-size: 0.9rem; color: #4f46e5; margin: 0 0 8px; }
    .pricing-card .card-price { font-size: 2rem; font-weight: 800; color: #0f172a; margin: 0 0 16px; }
    .pricing-card .card-price span { font-size: 1rem; font-weight: 400; color: #64748b; }
    .pricing-card ul { list-style: none; padding: 0; margin: 0; font-size: 0.875rem; color: #475569; flex: 1; }
    .pricing-card ul li { padding: 6px 0; border-bottom: 1px solid #f1f5f9; }
    .pricing-card ul li::before { content: "โœ“  "; color: #10b981; font-weight: 700; }
    .pricing-card .card-cta {
      display: block;
      margin-top: auto;         /* pinned to bottom */
      padding-top: 20px;
      text-align: center;
    }
    .pricing-card .card-cta a {
      display: block;
      padding: 12px;
      background: #4f46e5;
      color: white;
      text-decoration: none;
      border-radius: 8px;
      font-weight: 600;
      font-size: 0.9rem;
    }

    /* โ”€โ”€ 4. Chat window โ”€โ”€ */
    .chat-window {
      display: flex;
      flex-direction: column;
      height: 280px;
      background: white;
      border: 1px solid #e2e8f0;
      border-radius: 12px;
      overflow: hidden;
    }
    .chat-header {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 12px 16px;
      background: #1e293b;
      color: white;
      font-size: 0.9rem;
      font-weight: 600;
      flex-shrink: 0;           /* never shrinks */
    }
    .chat-messages {
      flex: 1;                  /* fills remaining space */
      overflow-y: auto;
      padding: 16px;
      display: flex;
      flex-direction: column;
      gap: 10px;
    }
    .msg { font-size: 0.85rem; padding: 8px 12px; border-radius: 8px; max-width: 80%; }
    .msg-in  { background: #f1f5f9; align-self: flex-start; }
    .msg-out { background: #4f46e5; color: white; align-self: flex-end; }
    .chat-input {
      display: flex;
      gap: 8px;
      padding: 12px 16px;
      border-top: 1px solid #e2e8f0;
      flex-shrink: 0;           /* never shrinks */
    }
    .chat-input input {
      flex: 1;
      border: 1px solid #e2e8f0;
      border-radius: 6px;
      padding: 8px 12px;
      font-size: 0.875rem;
      font-family: system-ui, sans-serif;
    }
    .chat-input button {
      background: #4f46e5; color: white; border: none;
      border-radius: 6px; padding: 8px 16px;
      font-size: 0.8rem; cursor: pointer; font-weight: 600;
    }
  </style>
</head>
<body>

  <section>
    <h2>1 โ€” Perfect Center</h2>
    <div class="center-demo">Perfectly Centered Content</div>
  </section>

  <section>
    <h2>2 โ€” Media Object</h2>
    <div class="media">
      <div class="media-img">AR</div>
      <div class="media-body">
        <h3>Alex Rivera <small style="color:#94a3b8;font-weight:400">2 hours ago</small></h3>
        <p>Great article! The section on align-self was especially helpful for understanding how to build toolbars with mixed alignment requirements.</p>
      </div>
    </div>
  </section>

  <section>
    <h2>3 โ€” Pricing Cards with Pinned CTA</h2>
    <div class="card-deck">
      <div class="pricing-card">
        <p class="card-name">Starter</p>
        <p class="card-price">$0 <span>/ month</span></p>
        <ul><li>5 projects</li><li>1GB storage</li></ul>
        <div class="card-cta"><a href="#">Get started</a></div>
      </div>
      <div class="pricing-card">
        <p class="card-name">Pro</p>
        <p class="card-price">$19 <span>/ month</span></p>
        <ul><li>Unlimited projects</li><li>20GB storage</li><li>Priority support</li><li>Analytics</li></ul>
        <div class="card-cta"><a href="#">Upgrade now</a></div>
      </div>
      <div class="pricing-card">
        <p class="card-name">Enterprise</p>
        <p class="card-price">$49 <span>/ month</span></p>
        <ul><li>Unlimited everything</li><li>100GB storage</li><li>24/7 support</li></ul>
        <div class="card-cta"><a href="#">Contact sales</a></div>
      </div>
    </div>
  </section>

  <section>
    <h2>4 โ€” Chat Window (scrollable middle, fixed header and input)</h2>
    <div class="chat-window">
      <div class="chat-header">
        <div style="width:28px;height:28px;border-radius:50%;background:#4f46e5;display:flex;align-items:center;justify-content:center;font-size:0.75rem;">JD</div>
        Jane Doe โ€” Online
      </div>
      <div class="chat-messages">
        <div class="msg msg-in">Hey! Did you see the new Flexbox lesson?</div>
        <div class="msg msg-out">Yes! The alignment section was super clear.</div>
        <div class="msg msg-in">The margin-left: auto trick blew my mind.</div>
        <div class="msg msg-out">Same! I had been using spacer divs for that ๐Ÿ˜…</div>
        <div class="msg msg-in">Now I need to go rewrite half my nav bars.</div>
      </div>
      <div class="chat-input">
        <input type="text" placeholder="Type a message...">
        <button>Send</button>
      </div>
    </div>
  </section>

</body>
</html>

How It Works

Step 1 โ€” Media Object Uses align-items: flex-start

Setting align-items: flex-start prevents the avatar from stretching to the full height of the text block. The avatar stays at its natural 56px height regardless of how long the text is โ€” the fundamental media object pattern.

Step 2 โ€” flex-shrink: 0 Protects the Avatar Size

In a narrow container, flex items shrink by default. The avatar with flex-shrink: 0 always stays 56ร—56px. Without it the avatar would squish โ€” a common Flexbox gotcha with fixed-size images and icons.

Step 3 โ€” Column Flex + margin-top: auto Pins the CTA

Each pricing card is a column flex container. The feature list gets flex: 1 to absorb height differences. The CTA div gets margin-top: auto โ€” this pushes it to the card’s absolute bottom regardless of feature count, aligning all three CTAs perfectly.

Step 4 โ€” Chat Window Uses Three-Layer Column Flex

The chat shell is a column flex container with fixed height. The header and input bar both have flex-shrink: 0 so they never collapse. The message list has flex: 1 so it claims all remaining height, plus overflow-y: auto to enable scrolling when messages overflow.

Step 5 โ€” Column Flex on Messages Creates Bubble Alignment

The message list is itself a column flex container with gap: 10px. Sent messages have align-self: flex-end and received messages have align-self: flex-start โ€” the two-line solution for the classic chat bubble layout.

Real-World Example: Holy Grail Layout

/* holy-grail.css */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }

.page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  font-family: system-ui, sans-serif;
}

.page-header {
  background: #1e293b;
  color: white;
  padding: 0 24px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}

.page-body {
  display: flex;
  flex: 1;
  overflow: hidden;
}

.page-left-sidebar {
  width: 220px;
  flex-shrink: 0;
  background: #f8fafc;
  border-right: 1px solid #e2e8f0;
  overflow-y: auto;
  padding: 24px 0;
}

.page-main {
  flex: 1;
  overflow-y: auto;
  padding: 32px;
  background: white;
}

.page-right-sidebar {
  width: 200px;
  flex-shrink: 0;
  background: #f8fafc;
  border-left: 1px solid #e2e8f0;
  overflow-y: auto;
  padding: 24px 16px;
}

.page-footer {
  background: #f1f5f9;
  border-top: 1px solid #e2e8f0;
  padding: 12px 24px;
  font-size: 0.8rem;
  color: #64748b;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-shrink: 0;
}

Common Mistakes

Mistake 1 โ€” Not setting flex-shrink: 0 on fixed UI chrome

โŒ Wrong โ€” header collapses when content is long:

.header { height: 60px; } /* shrinks in a flex container when space is tight */

โœ… Correct โ€” explicitly prevent shrinking:

.header { height: 60px; flex-shrink: 0; }

Mistake 2 โ€” Scrollable area without flex: 1

โŒ Wrong โ€” message list does not fill the available space:

.messages { overflow-y: auto; } /* no flex-grow โ€” area doesn't expand */

โœ… Correct โ€” flex: 1 makes the list fill remaining height:

.messages { flex: 1; overflow-y: auto; }

Mistake 3 โ€” Pinning CTA with padding instead of margin-top: auto

โŒ Wrong โ€” CTA appears at different heights across cards based on content:

.card { display: flex; flex-direction: column; }
.cta  { padding-top: 20px; } /* does not actually pin to bottom */

โœ… Correct โ€” margin-top: auto absorbs all free space above:

.cta { margin-top: auto; padding-top: 20px; }

▶ Try It Yourself

Quick Reference

Pattern CSS Technique Key Property
Perfect center Container: flex, align-items+justify-content: center Both axes centered
Media object flex row + gap + align-items: flex-start + flex-shrink: 0 on image flex-shrink: 0
Pinned CTA Column flex card + flex: 1 on body + margin-top: auto on CTA margin-top: auto
Scrollable middle Column flex shell + flex-shrink: 0 on header/footer + flex: 1 + overflow-y: auto on body flex: 1 + overflow
Navbar split justify-content: space-between or margin-left: auto margin-left: auto
Holy grail Column shell, row body, fixed sidebars (flex: 0 0 Npx), fluid main (flex: 1) flex: 0 0 Npx

🧠 Test Yourself

Three pricing cards have different amounts of feature text but you want all “Buy Now” buttons pinned to the same vertical position at the bottom. What is the correct approach?





โ–ถ Try It Yourself