A Practical Guide to CSS Flexbox

Flexbox simplifies CSS layout by enabling efficient control of space distribution between items in a container, allowing for easy alignment, wrapping, and responsive design without complex calculations.

Centring something vertically in CSS was, for years, a running joke about how hard a seemingly simple task could be. Distributing space evenly between a row of buttons meant fiddling with margins and hoping. Flexbox put an end to that. It is a layout system designed around a single idea: efficiently distributing space among items in a container, even when you do not know their sizes in advance. Once you understand how the container and its items share control, alignment and spacing stop being a fight.

Container and Items

Flexbox works as a relationship between a parent and its children. You turn an element into a flex container by setting display: flex, and its direct children automatically become flex items:

.container {
display: flex;
}

That is the whole setup. From there, the container controls how the items are arranged as a group, and each item can override certain behaviours for itself. Most of Flexbox is just learning which property lives where.

There are two ways to create the container. display: flex makes a block-level flex container that takes up the full width available, while display: inline-flex makes one that sits inline alongside other content. The flex behaviour inside is identical. The only difference is how the container itself behaves in the surrounding flow.

The Two Axes

Everything in Flexbox is organised around two axes, and understanding them is the key to not getting confused by the alignment properties. The main axis runs in the direction your items flow, and the cross axis runs perpendicular to it.

flex-direction sets which way the main axis points:

.container {
flex-direction: row;
}

The default is row, which lays items out horizontally from left to right. row-reverse flows them right to left. column stacks them vertically from top to bottom, and column-reverse does so bottom to top. This matters more than it first appears, because when you switch to a column direction, the main and cross axes swap, and so do the effects of the alignment properties that depend on them.

Aligning Items as a Group

Three properties control how items are positioned within the container, and which axis each one acts on depends on your flex-direction.

justify-content aligns items along the main axis. With the default row direction, this controls horizontal positioning:

.container {
justify-content: center;
}

Beyond center, it accepts flex-start and flex-end to push items to either end, and three distribution values that are where Flexbox really shines. space-between spreads items out with the first and last pinned to the edges and equal gaps between. space-around gives each item equal space on both sides. space-evenly makes every gap, including the edges, identical. These three solve the even-spacing problem that used to require manual margin calculations.

align-items aligns items along the cross axis, which in row direction means vertical positioning:

.container {
align-items: center;
}

The default is stretch, which makes items fill the container’s height. flex-start and flex-end align them to the top or bottom, center centres them, and baseline aligns them by the baseline of their text, which is useful when items contain text at different sizes. This single property is what finally made vertical centring trivial.

The third, align-content, only comes into play when items wrap onto multiple lines. It controls how those rows are distributed in the cross axis:

.container {
align-content: space-between;
}

It takes the same kinds of values as the others. The thing to remember is that align-items positions items within a single line, while align-content positions multiple lines as a group, so it does nothing unless wrapping is active.

Wrapping and Spacing

By default, flex items all try to fit on one line, shrinking if they have to. flex-wrap changes that:

.container {
flex-wrap: wrap;
}

With wrap, items that do not fit move onto a new line instead of being squeezed. nowrap is the default single-line behaviour, and wrap-reverse wraps onto new lines in the opposite direction. Wrapping is what makes Flexbox responsive: a row of cards can flow onto multiple lines as the screen narrows without any media queries.

For spacing between items, the gap property handles it cleanly:

.container {
gap: 20px;
}

This adds consistent space between items without putting unwanted margins around the outer edges, the same way it works in Grid.

Controlling Individual Items

So far everything has been about the container. Items have their own set of properties for fine-grained control, and the most important is flex:

.item {
flex: 1;
}

flex is shorthand for three separate properties working together, and it is worth understanding them individually because the shorthand hides what is actually happening.

flex-grow controls how an item expands to fill spare space. The default is 0, meaning items do not grow. A higher number means the item claims a larger share of the available space relative to its siblings:

.item {
flex-grow: 2;
}

An item with flex-grow: 2 grows twice as fast as one with flex-grow: 1.

flex-shrink controls the opposite, how much an item gives up space when the container is too small. The default is 1, so items shrink as needed. Setting it to 0 locks an item at its size and prevents it from shrinking:

.item {
flex-shrink: 0;
}

flex-basis sets an item’s starting size before growing or shrinking is applied:

.item {
flex-basis: 200px;
}

It can be a pixel value, a percentage, or auto. Setting it to 0 is a common trick that lets items grow into perfectly equal widths regardless of their content, because they all start from nothing and share the space according to their grow values.

When you write flex: 1, you are setting all three at once: grow to fill space, shrink when needed, and start from a zero basis. That single declaration is why flex: 1 is such a common way to make items share space equally.

Reordering and Overriding

Two more item properties round things out. order changes the visual order of items without touching the HTML:

.item-5 { order: 2; }
.item-1 { order: -1; }

The default is 0, and items are arranged from lowest to highest. A negative value pulls an item to the front, a positive value pushes it back. This lets you rearrange items for different screen sizes purely in CSS.

align-self lets a single item break from the container’s align-items setting:

.item {
align-self: flex-end;
}

It takes the same values as align-items but applies to just that one item. This is how you align most items one way while letting one or two sit differently, without restructuring anything.

Putting It Together

Here is a typical responsive row that combines the core properties:

.container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 10px;
}
.item {
flex: 1;
padding: 20px;
background: lightblue;
text-align: center;
}
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>

The container lays out its items in a row, spreads them across the available width, centres them vertically, and allows wrapping on small screens. Each item uses flex: 1 to claim an equal share of the space. With a handful of lines, you have a layout that would have taken considerable effort before Flexbox existed.

When Flexbox Fits

Flexbox is the right tool for one-dimensional layouts, anything where content flows in a single direction. Aligning elements horizontally or vertically, distributing space between items, reordering elements, and building components that adapt responsively are all squarely in its wheelhouse.

It is less suited to full two-dimensional page layouts where you need to control rows and columns at the same time. That is what CSS Grid is for. The simplest way to decide: if you are thinking about a single line of content, whether a row or a column, reach for Flexbox. If you are thinking about rows and columns together, reach for Grid. In practice the two are often used together, with Grid structuring the page and Flexbox aligning the contents of each region.

Cheat Sheet

PropertyWhat it does
display: flexEnables Flexbox on the container
flex-directionSets the main axis direction (row, column, and reverses)
justify-contentAligns items along the main axis
align-itemsAligns items along the cross axis
align-contentAligns multiple lines when wrapping is on
flex-wrapControls whether items wrap onto new lines
gapSets spacing between items
flex-growControls how an item grows to fill space
flex-shrinkControls how an item shrinks when space is tight
flex-basisSets an item’s starting size
flexShorthand for grow, shrink, and basis together
orderChanges the visual order of items
align-selfOverrides align-items for a single item

See you soon

View Comments (5)

Leave a Reply

Subscribe to My Newsletter

Subscribe to my email newsletter to get the latest posts delivered right to your email. Pure inspiration, zero spam.