Flexbox in the Real World

The Layout Patterns You'll Actually Use I've done frontend for a few years now. And here's the thing about Flexbox. The docs explain every property just fine. But nobody tells you the real secret. Which patterns do you reach for every single day? Just a handful. This post skips the theory. It goes straight to the good stuff. Read it, then paste it into your project. Done.

Lulu Zhang 4 min read
Hand-drawn rough design

First, the Mental Model

Flexbox is really one idea. A container. It decides how a row of kids line up. That's it.

Keep two things straight:

  • The container (the parent). It's where you write display: flex.
  • The children. The direct kids. They become "flex items" automatically.

Now here's the trap. Everybody hits it. Flex only bosses around direct children. Grandkids? Not its problem.

Don't sweat the terms "main axis" and "cross axis." Just remember three friends. flex-direction sets the main axis. justify-content lines things up along it. align-items lines them up the other way. We'll use these three again and again. They'll stick. Promise.



Trick 1: Center Something, Both Ways (Your Bread and Butter)

Centering used to be painful. margin: auto. Hacks on hacks. Now? Three lines. Boom.

.center-box {
  display: flex;
  /* justify-content runs the main axis, and that axis is horizontal by default, so this centers things left-to-right */
  justify-content: center;
  /* align-items runs the cross axis, so this centers things top-to-bottom */
  align-items: center;
  /* centering needs some height to show off, otherwise the box just hugs the content and there's nothing to center inside */
  min-height: 100vh;
}

This is the classic. Login box? Centered. Empty-state message? Centered. Loading spinner? You guessed it.

Trick 2: A Navbar (Logo Left, Menu Right)

Every project needs this. Logo pinned left. Links pinned right. Space fills the middle on its own.

.navbar {
  display: flex;
  /* space-between shoves the first and last child to the two ends and shares the leftover gap evenly in the middle */
  /* so you write zero margins, and adding or removing a menu item won't wreck the layout */
  justify-content: space-between;
  /* line the logo up with the text links, otherwise the icon and the words sit at different heights and it looks off */
  align-items: center;
  padding: 0 24px;
}

Want even spacing between the menu items? Don't margin each one. Just hand the container a gap.

.nav-links {
  display: flex;
  /* gap got solid browser support later than the rest of Flexbox, but every major browser is fine with it now */
  /* and compared to margin-right on each item then clearing the last one, gap is just way cleaner */
  gap: 16px;
}

Trick 3: The grow / shrink / basis Trio

This is the confusing part. So let me crack it open. Plain words. flex is shorthand. It packs three values.

.item {
  /* the order is flex: grow shrink basis */
  flex: 1 1 0;
}
  • flex-grow. Got extra room? Should this guy get bigger? And how much? 0 means stay put. 1 means jump in and grab a share.
  • flex-shrink. Running tight? Should this guy get squeezed? 0 means no way. 1 means sure, squeeze me.
  • flex-basis. The starting width. Before any extra space gets handed out.

In real projects you only need a couple of go-to recipes.

/* Recipe 1: let one element soak up all the leftover room, like the input next to a search button */
/* flex: 1 is just flex: 1 1 0 in disguise, and it means "whatever's left is mine" */
.fill-rest {
  flex: 1;
}

/* Recipe 2: this element holds its size and flat-out refuses to shrink, like a fixed-width sidebar */
/* I reach for flex: none instead of plain width because it locks both grow and shrink in one go */
/* if you only set width, Flexbox can still squeeze the element when room runs short, which is sneaky */
.fixed-size {
  flex: none;
  width: 240px;
}

Quick note on that fixed sidebar. There are two ways to do it. Let me lay both out first. Then I'll pick.

  • Option A: width: 240px. Simple. But the element gets squeezed when the container shrinks. Bug incoming.
  • Option B: flex: none; width: 240px. One extra line. But the width is locked solid. It never deforms. Ever.

In production I go Option B. Always. Because "the sidebar got squished on a small screen" is a sneaky little bug. Testing might miss it. Then a user opens a narrow window. And boom, there it is.

Trick 4: A Responsive Card Grid (The Big One!)

This is the MVP of responsive UI. A row of cards. Wide screen? Fit more. Narrow screen? Wrap automatically. And every card has a minimum width. So they never get squished into sad little slivers.

.card-grid {
  display: flex;
  /* flex-wrap is nowrap by default, which means it stubbornly stays on one line and squishes everything flat */
  /* flip it to wrap, and any card that doesn't fit drops to the next line, and that right there is the whole responsive trick */
  flex-wrap: wrap;
  gap: 16px;
}

.card {
  /* this one line is the soul of the whole thing, so let me pull it apart: */
  /* flex-grow: 1 means when a row isn't full, the cards share the leftover space and stretch to fill it */
  /* flex-shrink: 1 means they're allowed to shrink when things get tight */
  /* flex-basis: 280px means each card wants to be 280px, and once it can't, it wraps */
  /* all together: four or five per row on a big screen, two or one on a small one, and never cramped */
  flex: 1 1 280px;
}

Now people get stuck here. Flexbox or CSS Grid? My rule of thumb is simple.

  • Want "line them up when they fit, wrap when they don't"? That's a flowing layout. Use Flexbox flex-wrap. Like above.
  • Want a strict grid? Tidy rows and columns? Think admin tables or a neat photo wall? Use CSS Grid. Less hassle.

For card lists I usually grab Flexbox. The wrapping just feels more natural. And you skip the math on column counts.

Trick 5: Flip the Axis with a Media Query

Another super common move. Horizontal on desktop. Stacked on mobile. Flexbox does it by flipping one property. One.

.layout {
  display: flex;
  /* stack vertically by default, which is the mobile-first way to go */
  /* I start mobile-first because phone screens are the fussy ones, and it's easier to nail the tight case first then loosen up than to do it backwards */
  flex-direction: column;
  gap: 16px;
}

/* only switch to horizontal once the screen is actually wide enough */
@media (min-width: 768px) {
  .layout {
    /* swap flex-direction from column to row */
    /* the why: wide screens have room to go sideways, and horizontal packs in more info and just looks nicer */
    flex-direction: row;
  }
}

Pair this with the card grid above. Together they cover about 80% of responsive layouts. Easy.

Trick 6: A Footer That Sticks to the Bottom

Not much content? The footer still hugs the bottom of the screen. Tons of content? It gets pushed down nicely. Flexbox nails this one.

.page {
  display: flex;
  flex-direction: column;
  /* make the page at least one screen tall, otherwise the footer floats in midair when there's barely any content */
  min-height: 100vh;
}

.main-content {
  /* let the main area gobble up all the leftover vertical space, which shoves the footer to the very bottom */
  /* I pick flex: 1 over giving the footer margin-top: auto, and honestly both work fine */
  /* but flex: 1 reads clearer, since it basically says "the main content does the stretching," which matches what's really happening */
  flex: 1;
}

A Few Traps. Dodge Them Early.

First trap. Kids get squished and bent out of shape. Images and text especially. Flex items shrink by default. So your nice round avatar? It can turn into an oval.

.avatar {
  /* slap this on any fixed-size element to tell flex "hands off" */
  /* skip it, and the avatar gets squashed the second the content next to it grows, which is a super common production bug */
  flex-shrink: 0;
}

Second trap. Text won't wrap and it blows up the layout. Long text in a flex item won't shrink past its content width. Not by default.

.text-block {
  flex: 1;
  /* here's a hidden detail that rides along with flex: */
  /* a flex item's minimum width is its content width by default, so long text just bursts it wide open */
  /* set it to 0, and finally the item can shrink and the text ellipsis kicks in */
  min-width: 0;
}

This min-width: 0 thing? I'd bet money you've been bitten by it. Write it down. It'll save you half a day of head-scratching.

Wrap-Up

In production you'll live in just these combos. 90% of the time.

  • Centering: justify-content: center + align-items: center
  • Two-end navbar: justify-content: space-between
  • Soak up leftover space: flex: 1
  • Responsive cards: flex-wrap: wrap + flex: 1 1 <base width>
  • Flip direction for responsiveness: change flex-direction in a media query

Paste these in. Pair them with the lifesavers. gap. flex-shrink: 0. min-width: 0. And everyday layouts will pretty much stop fighting you. Go build something.