TH The Sass Way
Layout: Flexbox & Grid

A Complete Guide to Flexbox: Every Property, Explained

A Complete Guide to Flexbox: Every Property, Explained
tldrFlexbox is a one-dimensional CSS layout system for arranging items along a single axis. Set display: flex on a container and its children become flex items that line up in a row or column. Container properties like justify-content, align-items, and gap position the group; item properties like flex-grow, flex-shrink, and flex-basis (usually via the flex shorthand) decide how each item shares the space.

Flexbox in one sentence

Flexbox is a one-dimensional CSS layout system that distributes space and aligns items along a single axis. You set display: flex on a container, and its direct children become flex items that line up in a row (or column) and can grow, shrink, and align to fill the available space. That is the whole idea: a parent that lays out its children along one axis, with fine-grained control over spacing and alignment.

Everything else in Flexbox is just properties that adjust how that distribution and alignment behave. This guide walks through every one of them, grouped by whether it lives on the container or on the items, with copy-pasteable code and the mental model that makes each property click.

.container {
  display: flex;
}

That single declaration turns children into flex items. Below we make it do real work.

The two axes: main and cross

Flexbox has no fixed "horizontal" and "vertical." Instead it has a main axis and a cross axis, and which direction they point depends on flex-direction.

This matters because the alignment properties are named after axes, not directions. justify-content always works along the main axis; align-items always works along the cross axis. Switch flex-direction to column and their real-world effect swaps, because the axes rotated. Once you internalize main-vs-cross, the property names stop feeling arbitrary.

Concept Default (row) direction With flex-direction: column
Main axis Left → right Top → bottom
Cross axis Top → bottom Left → right
justify-content controls Horizontal spacing Vertical spacing
align-items controls Vertical alignment Horizontal alignment

Container properties

These go on the element with display: flex. They govern how the whole group behaves.

flex-direction

Sets the main axis and the order items flow in.

.container {
  display: flex;
  flex-direction: row; /* row | row-reverse | column | column-reverse */
}

flex-wrap

By default flex items refuse to wrap — they shrink to fit on one line instead. flex-wrap changes that.

.container {
  display: flex;
  flex-wrap: wrap; /* nowrap (default) | wrap | wrap-reverse */
}

Set wrap and items that no longer fit drop to a new line. This is the foundation of a simple responsive grid: give each item a flex-basis and let wrapping handle the rest, no media queries required. The shorthand flex-flow: row wrap combines flex-direction and flex-wrap in one line.

justify-content

Distributes leftover space along the main axis.

.container {
  display: flex;
  justify-content: space-between;
}
Value What it does
flex-start Items packed to the start (default)
flex-end Items packed to the end
center Items centered as a group
space-between First and last item touch the edges; equal gaps between
space-around Equal space around each item (edges get half-gaps)
space-evenly Truly equal gaps, including at the edges

space-between is the workhorse for navbars: logo hard left, links hard right, one line of CSS.

align-items

Aligns items along the cross axis — for a row, that is vertical alignment.

.container {
  display: flex;
  align-items: center;
}

align-content

Only does anything when items wrap onto multiple lines. It distributes space between the lines along the cross axis, using the same value set as justify-content (flex-start, center, space-between, and so on). On a single-line flex container it has no effect, which trips up a lot of people expecting it to behave like align-items.

gap

The cleanest way to space flex items. gap adds consistent spacing between items without adding it on the outer edges.

.container {
  display: flex;
  gap: 1rem;        /* row-gap and column-gap at once */
  /* gap: 1rem 2rem;  row-gap column-gap */
}

Before gap was supported in Flexbox, people used margins and negative-margin hacks. Those days are over: gap works in every current browser (Chrome, Firefox, Safari, and Edge have supported flex gap since 2021). Reach for it instead of margins on items.

Item properties

These go on the flex items — the children — and control how each one behaves within the shared space.

flex-grow

How much an item grows to absorb extra free space, relative to its siblings. It is a unitless proportion, not a size.

.item {
  flex-grow: 1; /* default is 0 (do not grow) */
}

If every item has flex-grow: 1, they share leftover space equally. Give one item flex-grow: 2 and it takes twice as large a share of the extra space (not twice the total width). A single flex-grow: 1 on one child is the standard way to make it fill the remaining room — think a search input that expands while buttons stay their natural size.

flex-shrink

The mirror image: how much an item shrinks when there is not enough room.

.item {
  flex-shrink: 1; /* default is 1 (allowed to shrink) */
}

Default 1 means items shrink proportionally to avoid overflow. Set flex-shrink: 0 to forbid an item from shrinking — handy for a fixed-width sidebar or an icon that must never squish.

flex-basis

The item's size along the main axis before grow and shrink are applied. It is the starting point.

.item {
  flex-basis: 200px; /* auto (default) | <length> | <percentage> */
}

auto means "use my width/height or my content size." Set flex-basis: 0 together with flex-grow: 1 on several items and they become perfectly equal columns, because they all start from zero and divide space evenly.

The flex shorthand

You will almost always set these three through the flex shorthand rather than individually. Learn these values and you have covered 95% of real usage.

Shorthand Expands to Meaning
flex: 1 1 1 0% Grow and shrink from a zero basis — equal-width columns
flex: auto 1 1 auto Grow and shrink from natural size
flex: none 0 0 auto Fixed at natural size, never grow or shrink
flex: 0 1 auto (the default) Shrink but don't grow
flex: 1 1 200px 1 1 200px Start at 200px, then flex
/* Three equal columns */
.column {
  flex: 1;
}

align-self

Overrides the container's align-items for a single item. Same value set (auto, flex-start, flex-end, center, baseline, stretch). Use it when one item needs to break from the group — say, pinning a "New" badge to the top of a row of centered items.

.special {
  align-self: flex-start;
}

order

Changes the visual position of an item without touching the HTML. Lower numbers come first; the default is 0, so order: -1 pulls an item to the front.

.promoted {
  order: -1;
}

Use order sparingly. Like row-reverse, it changes visual order but not DOM or keyboard tab order, so heavy use creates a confusing, inaccessible experience for keyboard and screen-reader users. It shines for small, responsive rearrangements, not wholesale reordering.

Two patterns you'll use constantly

Perfect centering

The reason Flexbox first won people over. Three lines center anything, horizontally and vertically, with no magic numbers:

.center {
  display: flex;
  justify-content: center; /* main axis  */
  align-items: center;     /* cross axis */
}

This works regardless of the child's size. For a deeper tour of the alternatives and their trade-offs, see our guide on how to center a div.

A responsive card row with no media queries

Combine flex-wrap, gap, and a flex-basis and the layout adapts on its own:

.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.card {
  flex: 1 1 250px; /* grow, shrink, never narrower than ~250px */
}

Each card wants to be at least 250px wide. Three fit on a wide screen, two on a tablet, one on a phone — the browser recalculates as the viewport changes, and you never wrote a breakpoint. This is Flexbox at its best: intrinsically responsive.

Flexbox in Sass

If you use Sass, wrap common Flexbox patterns in a mixin so you stop repeating the same three lines everywhere:

@mixin flex-center($direction: row) {
  display: flex;
  flex-direction: $direction;
  justify-content: center;
  align-items: center;
}

.hero {
  @include flex-center(column);
  gap: 1rem;
}

That compiles to standard CSS but keeps your intent readable. If you are new to writing mixins, our complete guide to Sass covers the syntax and when a mixin beats a plain class.

When Flexbox is the wrong tool

Flexbox is one-dimensional: it excels at laying out items along a single row or column. When you need to control rows and columns at the same time — a true two-dimensional grid where things line up both horizontally and vertically — CSS Grid is the better fit. A common, sensible approach is Grid for the overall page skeleton and Flexbox for the components inside each region.

Read Flexbox vs CSS Grid for a decision framework, and the step-by-step CSS Grid tutorial when you're ready to build a two-dimensional layout.

Browser support and gotchas

Flexbox is universally supported — every browser in current use has shipped unprefixed Flexbox for years, so you can use it in production without fallbacks or vendor prefixes. A few things still catch people out:

Quick reference cheat sheet

Property Goes on Controls
flex-direction Container Direction of the main axis
flex-wrap Container Whether items wrap to new lines
justify-content Container Spacing along the main axis
align-items Container Alignment along the cross axis
align-content Container Spacing of wrapped lines (multi-line only)
gap Container Space between items
flex-grow Item Share of extra space
flex-shrink Item How much it shrinks when cramped
flex-basis Item Starting size on the main axis
flex Item Shorthand for grow / shrink / basis
align-self Item Override cross-axis alignment for one item
order Item Visual position (not DOM order)

Master the two axes and the container-versus-item split, and the rest of Flexbox is just picking the property that names what you want. Set display: flex, decide your direction, then reach for justify-content, align-items, and gap to place things — and the flex shorthand to decide how items share the space.

FAQ

What is the difference between justify-content and align-items?

They control different axes. justify-content distributes items along the main axis (horizontal by default, so it handles left-to-right spacing), while align-items aligns items along the cross axis (vertical by default). Switch flex-direction to column and their real-world directions swap, because the main and cross axes rotate. To center something both ways, set justify-content: center and align-items: center together.

How does flexbox work?

You set display: flex on a container, turning its direct children into flex items that flow along a main axis. The browser distributes free space and aligns items using container properties (flex-direction, justify-content, align-items, gap) and item properties (flex-grow, flex-shrink, flex-basis). Items can grow to fill extra room, shrink to avoid overflow, and wrap to new lines, which makes flexbox naturally responsive without media queries.

What does the flex: 1 shorthand mean?

flex: 1 expands to flex: 1 1 0%, meaning flex-grow: 1, flex-shrink: 1, and flex-basis: 0%. Because every item starts from a zero basis and grows equally, applying flex: 1 to several siblings produces perfectly equal-width columns. It is the most common flexbox shorthand. Use flex: auto to flex from natural content size, or flex: none for a fixed, non-flexing item.

When should I use flexbox instead of CSS Grid?

Use flexbox for one-dimensional layouts, arranging items in a single row or column such as navbars, button groups, and card rows. Use CSS Grid when you need to control rows and columns at the same time in a true two-dimensional layout. A common pattern is Grid for the overall page structure and flexbox for the components inside each region. They work well together, not as rivals.

Why is my flex item overflowing its container?

By default a flex item will not shrink below its content's intrinsic minimum size, so long text or wide images can push past the container. The fix is to add min-width: 0 (or min-height: 0 in a column) to the affected item, which lets it shrink properly. Setting flex-shrink: 0 does the opposite and prevents shrinking, so use min-width: 0 when you want the item to compress.

Do I still need vendor prefixes for flexbox?

No. Every browser in current use has shipped unprefixed flexbox for years, so you can write display: flex in production without -webkit- or -ms- prefixes or fallbacks. Older prefixed and legacy syntaxes only matter if you must support very old browsers like Internet Explorer 10, which is effectively gone. For modern projects, use the standard properties directly and reach for gap instead of margin hacks for spacing.