CSS Flexbox Explained with Examples
If you've ever tried to center a div vertically using nothing but old-school CSS, you already know the pain Flexbox was built to solve. Before Flexbox came along, developers were juggling floats, clearfixes, and display: inline-block hacks just to get a few boxes to sit next to each other nicely. It worked, technically, but it always felt like fighting the browser rather than working with it.
Flexbox changed that. It gave us a layout model designed specifically for arranging items in a single direction — in a row or in a column — and handling all the messy details of spacing, alignment, and sizing along the way. Once you get comfortable with it, you'll wonder how you ever lived without it.
This guide walks through Flexbox from the ground up, with plenty of code examples and a few diagrams to make the more abstract bits click. By the end, you should be able to look at a layout problem and immediately know which Flexbox property solves it.
What Flexbox Actually Is
Flexbox, short for the Flexible Box Layout Module, is a CSS layout mode built around one core idea: you have a container, and that container distributes space among its children along a single axis. That's really the whole concept in a nutshell. Everything else is just refining how that distribution happens.
It's worth being upfront about what Flexbox is not good at. It's a one-dimensional layout system — meaning it really shines when you're arranging things in a row or a column, but it starts to get awkward for full two-dimensional grid layouts (rows and columns at the same time). For that, CSS Grid is the better tool, and we'll touch on the difference toward the end. But for navigation bars, button groups, card layouts, centering content, and dozens of other everyday UI patterns, Flexbox is usually exactly what you want.
Turning an Element into a Flex Container
Everything starts with one line of CSS:
.container {
display: flex;
}
The moment you add display: flex to an element, two things happen. First, that element becomes a flex container. Second, every direct child of that element automatically becomes a flex item, whether you asked for it or not. Grandchildren aren't affected — only the immediate children.
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
.container {
display: flex;
}
With nothing else applied, those three items will already line up in a neat row, side by side, instead of stacking on top of each other the way block-level divs normally would. That single line of CSS does a surprising amount of heavy lifting.
The Main Axis and the Cross Axis
This is the one concept that trips up almost every beginner, so let's slow down here. Flexbox thinks in terms of two axes:
- The main axis is the primary direction items are laid out along. By default, that's horizontal, left to right.
- The cross axis runs perpendicular to the main axis. By default, that's vertical.
Here's the catch: which axis is "main" and which is "cross" isn't fixed. It depends entirely on the flex-direction property, which we'll get to in a second. If you switch the direction to a column, the main axis becomes vertical and the cross axis becomes horizontal. Properties like justify-content and align-items always refer back to these axes, not to literal "horizontal" and "vertical," so getting this mental model right early saves a lot of confusion down the line.
Picture it as a container with three boxes inside, the boxes sitting one after another along that main axis, with the cross axis running perpendicular to them. Keep this picture in your head — we'll be referring back to it constantly.
flex-direction: Choosing Your Main Axis
.container {
display: flex;
flex-direction: row; /* default */
}
There are four possible values:
row— items run left to right (this is the default, so you technically don't need to write it).row-reverse— items run right to left.column— items stack top to bottom.column-reverse— items stack bottom to top.
A small but important detail: row-reverse and column-reverse don't just flip the visual order — they actually reverse the main axis itself. This matters for things like tab order and screen readers, since the visual order and the underlying document order are no longer the same. It's not a dealbreaker, just something to be aware of if accessibility matters for your project (and it should).
flex-wrap: What Happens When Items Don't Fit
By default, Flexbox tries to cram all items onto a single line, shrinking them if necessary. Sometimes that's exactly what you want. Other times, you'd rather let items wrap onto multiple lines once they run out of room — think of a gallery of photo thumbnails.
.container {
display: flex;
flex-wrap: wrap;
}
The options here are:
nowrap— default behavior, everything squeezes onto one line.wrap— items wrap onto new lines as needed, top to bottom.wrap-reverse— same aswrap, but new lines stack in the opposite direction.
There's also a shorthand that combines flex-direction and flex-wrap into a single property:
.container {
flex-flow: row wrap;
}
It's a small convenience, but once you're used to writing both properties together, the shorthand saves a bit of typing.
justify-content: Spacing Things Out Along the Main Axis
This is probably the single most-used Flexbox property, and for good reason — it controls how extra space gets distributed along the main axis.
.container {
display: flex;
justify-content: center;
}
Here are the values you'll reach for most:
flex-start— items bunch up at the start of the container (default).center— items huddle together in the middle.flex-end— items bunch up at the end.space-between— equal space is placed between items, with no space at the outer edges.space-around— each item gets equal space on both sides, so the edges end up with half as much space as between items.space-evenly— every gap, including the outer edges, gets exactly equal space.
A lot of beginners mix up space-between and space-around. The quickest way to remember the difference: with space-between, the first and last items hug the container's edges tightly. With space-around, there's always a bit of breathing room on the outside too.
align-items: Spacing Things Out Along the Cross Axis
While justify-content handles the main axis, align-items handles the cross axis — so in a default row layout, that means vertical alignment.
.container {
display: flex;
align-items: center;
}
Common values:
stretch— items stretch to fill the container's cross-axis size (this is the default, and it's why flex items often appear the same height even without you setting one).flex-start— items align to the start of the cross axis.center— items center themselves along the cross axis.flex-end— items align to the end of the cross axis.baseline— items align based on their text baselines, handy when items contain text of different sizes.
That stretch default trips people up constantly. If you've ever wondered why your flex items all end up the same height even though you never told them to, this is why — align-items: stretch is doing that quietly in the background.
align-content: For When You Have Multiple Lines
This one gets confused with align-items a lot, but they solve different problems. align-items aligns items within a single line. align-content controls the spacing between multiple lines, and it only does anything when flex-wrap: wrap is active and there's extra space in the cross-axis direction.
.container {
display: flex;
flex-wrap: wrap;
align-content: space-between;
}
If your container only has one row of items, align-content won't visibly do anything — there's nothing to distribute it across. Worth remembering before you spend ten minutes debugging a property that simply doesn't apply to your situation.
gap: The Property That Should Have Existed from Day One
For years, putting consistent space between flex items meant adding margins to individual items and then writing extra CSS to cancel out the margin on the first or last item, so things didn't look lopsided. Thankfully, that's no longer necessary.
.container {
display: flex;
gap: 16px;
}
gap adds consistent spacing between items without affecting the outer edges of the container at all. You can also control row and column gaps separately:
.container {
display: flex;
flex-wrap: wrap;
row-gap: 20px;
column-gap: 10px;
}
Browser support for gap in Flexbox is excellent at this point, so there's really no reason to reach for the old margin-hack approach anymore.
Flex Item Properties: order, flex-grow, flex-shrink, and flex-basis
So far, everything we've covered applies to the container. Now let's look at properties you apply to individual items to control how they behave.
order
By default, items appear in the order they exist in the HTML. The order property lets you visually rearrange them without touching the markup itself.
.item-1 {
order: 2;
}
.item-2 {
order: 1;
}
Items are sorted by their order value, lowest to highest. Items with the same order value fall back to their source order relative to each other. This is great for things like reordering content on smaller screens without duplicating HTML, but use it sparingly — overusing order can make your code confusing to follow, since the visual layout no longer matches the document structure.
flex-grow
This controls whether an item is allowed to grow and absorb any leftover space in the container.
.item {
flex-grow: 1;
}
By default, flex-grow is 0, meaning items won't grow beyond their natural size even if there's extra room. Set it to 1, and the item will expand to soak up any available space. If multiple items all have flex-grow: 1, they'll share that extra space equally. Give one item flex-grow: 2 while the others stay at 1, and that item will claim twice as much of the leftover space as its siblings.
flex-shrink
The opposite of flex-grow — this controls whether an item is allowed to shrink when there isn't enough room for everyone at their natural size.
.item {
flex-shrink: 0;
}
The default value is 1, meaning items will shrink proportionally if needed. Setting it to 0 tells the browser "never shrink this item, no matter what," which is handy for things like icons or logos that should never get visually squished.
flex-basis
This sets the starting size of an item before any growing or shrinking happens, along the main axis.
.item {
flex-basis: 200px;
}
Think of flex-basis as similar to width (in a row layout) or height (in a column layout), but specifically for Flexbox's sizing calculations. It takes priority over width when both are present, though in practice many developers use them interchangeably for simple cases.
The flex Shorthand
In real-world code, you'll almost always see flex-grow, flex-shrink, and flex-basis combined into a single shorthand property:
.item {
flex: 1 1 200px;
}
That line means: grow if there's space, shrink if there isn't, and start from a base size of 200px. A few shorthand values come up constantly enough to be worth memorizing:
.item {
flex: 1; /* shorthand for 1 1 0% — grow and shrink freely, ignore natural size */
}
.item {
flex: auto; /* shorthand for 1 1 auto — grow and shrink based on content size */
}
.item {
flex: none; /* shorthand for 0 0 auto — rigid, won't grow or shrink at all */
}
flex: 1 in particular shows up everywhere, since it's the go-to way to make a set of items split available space equally.
align-self: Overriding Alignment for One Item
Sometimes you want most of your items to follow the container's align-items setting, but one particular item needs to break that pattern. That's exactly what align-self is for.
.item-special {
align-self: flex-end;
}
It accepts the same values as align-items (flex-start, center, flex-end, stretch, baseline), but applies only to the individual item you set it on, overriding whatever the container specified.
Putting It Together: Real Examples
Reading through properties one at a time is useful, but Flexbox really clicks once you see it solving actual layout problems. Here are a few patterns you'll use constantly.
Perfectly Centering Anything
This is the classic "how do I center a div" problem, and Flexbox makes it almost embarrassingly simple:
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
That's genuinely it. Three lines, and whatever's inside that container — text, an image, a button, a whole card — sits perfectly centered both horizontally and vertically.
A Simple Navigation Bar
<nav class="navbar">
<div class="logo">MySite</div>
<ul class="nav-links">
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
}
.nav-links {
display: flex;
gap: 24px;
list-style: none;
}
Notice that display: flex is being used twice here — once on the navbar itself to push the logo and links apart, and again on the list of links to lay them out horizontally with consistent spacing. Nesting flex containers like this is extremely common and nothing to be afraid of.
Equal-Height Cards
<div class="card-row">
<div class="card">Short content.</div>
<div class="card">A bit more content in this one, spanning a couple of lines.</div>
<div class="card">Medium amount of content here.</div>
</div>
.card-row {
display: flex;
gap: 20px;
}
.card {
flex: 1;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
Thanks to that default align-items: stretch behavior we talked about earlier, all three cards will automatically end up the same height, regardless of how much text is inside each one. No JavaScript, no manual height calculations — Flexbox handles it for you.
A Responsive Sidebar Layout
.layout {
display: flex;
flex-wrap: wrap;
}
.sidebar {
flex: 1 1 250px;
}
.main-content {
flex: 3 1 600px;
}
On wider screens, the sidebar and main content sit side by side, with the main content claiming roughly three times the space. Once the viewport gets too narrow for both to fit at their basis widths, flex-wrap: wrap kicks in and the sidebar drops below the main content automatically. It's a simple, CSS-only responsive layout without a single media query.
Common Mistakes to Watch For
A handful of issues come up again and again with people new to Flexbox.
Forgetting that display: flex only affects direct children is a big one. If your layout isn't behaving the way you expect, double check you're not accidentally trying to style grandchildren as if they were flex items.
Confusing justify-content and align-items is another classic. Just remember: justify is for the main axis, align is for the cross axis, and which one is "horizontal" depends on your flex-direction.
People also frequently forget that flex-shrink defaults to 1, meaning items will shrink by default even if you never explicitly told them to. If something is squishing smaller than you expect, check whether flex-shrink: 0 would fix it.
And lastly, relying too heavily on order to fix layout problems instead of fixing the actual HTML structure. It works, but it can make code harder to maintain over time, especially for anyone using assistive technology that follows document order rather than visual order.
Flexbox vs. Grid: A Quick Note
It's worth addressing this since the question comes up constantly. Flexbox and CSS Grid aren't competitors — they solve different problems and often work great together.
Flexbox is one-dimensional: it's built for arranging items along a single row or column, and it's fantastic at distributing space and handling content of varying sizes. Grid is two-dimensional: it's built for laying out rows and columns simultaneously, which makes it the better choice for full-page layouts or anything resembling a true grid structure.
In practice, a lot of real projects use Grid for the overall page skeleton and Flexbox for arranging content within individual sections — a card's internal layout, a button group, a navbar. They're complementary tools, not rivals.
Browser Support
Flexbox has been reliably supported across all major modern browsers for years now, so compatibility really isn't a major concern for most projects anymore. The one place older syntax sometimes lingers is in very old codebases that predate widespread support, where you might spot vendor-prefixed properties like -webkit-box-flex. For any new project today, you can use standard Flexbox syntax without worrying about it.
How to Practice
The best way to internalize Flexbox is to genuinely play with it, not just read about it. A few suggestions:
Open up your browser's developer tools on a site you visit often and look for display: flex in the styles panel — you'll start spotting it everywhere once you know what to look for.
Try rebuilding a simple navbar or card layout from a real website using nothing but Flexbox, without peeking at their actual CSS first.
Deliberately break things. Set flex-direction: column on something laid out as a row and watch how justify-content and align-items swap their effective behavior. Seeing it firsthand cements the main-axis/cross-axis concept far better than reading about it ever will.
Wrapping Up
Flexbox solves a problem that used to take an uncomfortable amount of CSS trickery to work around, and it does it with a small, learnable set of properties. The container controls the big-picture layout decisions — direction, wrapping, spacing — while individual items can opt into their own growing, shrinking, or alignment behavior when needed.
Once the main-axis and cross-axis mental model clicks, the rest of Flexbox tends to fall into place pretty naturally. Build a few real layouts with it, get comfortable with justify-content and align-items working together, and you'll find yourself reaching for Flexbox almost automatically the next time you need to arrange anything on a page.