Skip to content
C Codeloom
HTML & CSS

CSS Flexbox: A Practical Beginner's Guide

Flexbox is the modern tool for one-dimensional layout. This guide covers the container and item properties you need to align, distribute, and wrap any row or column of elements.

·9 min read · By Yash Kesharwani
Beginner 11 min read

What you'll learn

  • What Flexbox is and the problem it solves
  • The main axis, cross axis, and how flex direction works
  • How to align and distribute items with justify-content and align-items
  • How gap, flex-wrap, and flex-grow let items adapt
  • A few small, realistic layouts built from scratch

Prerequisites

For most of the web’s history, lining up two elements side by side was harder than it should have been. Developers used floats, tables, and a long list of tricks. Flexbox — short for the Flexible Box Layout — replaced all of that with a single, predictable system for one-dimensional layout.

If you ever need to put a row of items side by side or a column of items in a stack, Flexbox is the tool to reach for.

What Flexbox is

Flexbox is a layout mode that arranges items along a single line — a row or a column. You turn it on by setting display: flex on a container, and its direct children become flex items that line up automatically.

<div class="row">
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
</div>
.row {
  display: flex;
}

That is it. The three items sit in a row, top-aligned, with no gap. Everything else in Flexbox is fine-tuning that default behavior.

The two axes

Flexbox has two axes, and almost every property is named after one of them.

  • The main axis runs in the direction items flow.
  • The cross axis runs perpendicular to the main axis.

By default, the main axis is horizontal (left to right) and the cross axis is vertical. You can flip them with flex-direction:

.row {
  display: flex;
  flex-direction: row;        /* default — items in a row */
}

.column {
  display: flex;
  flex-direction: column;     /* items stacked vertically */
}

row-reverse and column-reverse are also available. They reverse the order without you having to change the HTML.

Once you know which way the main axis points, every other property becomes intuitive.

Aligning along the main axis

justify-content controls how items are distributed along the main axis. The values you will reach for constantly:

.row {
  display: flex;
  justify-content: flex-start;     /* default — items at the start */
  justify-content: flex-end;       /* items at the end */
  justify-content: center;         /* items centered */
  justify-content: space-between;  /* equal space between items, none at edges */
  justify-content: space-around;   /* equal space around each item */
  justify-content: space-evenly;   /* equal space between and at edges */
}

A header with a logo on the left and navigation on the right:

<header class="header">
  <div class="logo">Codeloom</div>
  <nav class="nav">
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</header>
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
}

That is the entire layout. The logo sits on the left, the nav sits on the right, and the browser handles the space between them.

Aligning along the cross axis

align-items controls how items sit on the cross axis. The values:

.row {
  align-items: stretch;       /* default — items fill the cross axis */
  align-items: flex-start;    /* top of the row, or left of a column */
  align-items: flex-end;      /* bottom of the row, or right of a column */
  align-items: center;        /* centered vertically (for a row) */
  align-items: baseline;      /* aligned to the text baseline */
}

align-items: center is the answer to the most-asked question in CSS: “how do I vertically center this?”

.hero {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

That centers any element inside .hero both horizontally and vertically. Two lines of CSS that used to require ugly workarounds.

Try it yourself. Create a <div> 400 pixels tall containing the text “Hello.” Use Flexbox to perfectly center the text both horizontally and vertically. Try changing justify-content and align-items to every value above and watch what happens.

The gap property

For years, spacing flex items meant adding margin to each one and trying to keep the math straight. Modern Flexbox supports gap, which sets the space between items directly:

.row {
  display: flex;
  gap: 1rem;
}

Every item now has 1rem of space between it and the next one. No margin tricks required. You can also use row-gap and column-gap separately when working with wrapped content.

Always reach for gap first. It is supported in every modern browser and is far easier to maintain than margins.

Letting items wrap

By default, Flexbox tries to fit all items on a single line, shrinking them if needed. When you want items to wrap to a new line when they run out of space, use flex-wrap:

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

.gallery img {
  width: 200px;
}

Now the images flow into multiple rows as the container width changes. This is the foundation of most responsive image galleries.

Item properties

So far we have set properties on the container. Each flex item has its own properties too. The two you will use most often are flex-grow and flex.

flex-grow tells an item how greedy it should be when there is leftover space:

.row {
  display: flex;
  gap: 1rem;
}

.spacer {
  flex-grow: 1;
}
<div class="row">
  <button>Cancel</button>
  <div class="spacer"></div>
  <button>Save</button>
</div>

The .spacer div has no content but expands to fill all available horizontal space, pushing the two buttons to the edges of the row.

The shorthand flex combines flex-grow, flex-shrink, and flex-basis in one line. The most common values:

.item {
  flex: 1;        /* grow to take an equal share of available space */
  flex: 0 0 200px; /* don't grow, don't shrink, start at 200px */
  flex: auto;     /* size based on content, allow growing and shrinking */
}

A three-column layout where the middle column takes twice as much space as the others:

.layout {
  display: flex;
  gap: 1rem;
}

.layout > * {
  flex: 1;
}

.layout > .main {
  flex: 2;
}

Two flex: 1 columns plus one flex: 2 column means the middle one is twice as wide as each side. Resize the browser and the proportions hold.

A small worked example

A reusable card component with a header, body, and a row of action buttons at the bottom — the kind of layout you build daily:

<article class="card">
  <h2 class="card__title">Project</h2>
  <p class="card__body">
    A short description of the project that may be one or more
    sentences long.
  </p>
  <div class="card__actions">
    <button class="card__button card__button--ghost">Cancel</button>
    <button class="card__button card__button--primary">Save</button>
  </div>
</article>
.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  width: 320px;
  padding: 1.5rem;
  border: 1px solid #ddd;
  border-radius: 0.5rem;
  background: white;
}

.card__title,
.card__body {
  margin: 0;
}

.card__actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: auto;
}

.card__button {
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
  border: 1px solid transparent;
  cursor: pointer;
}

.card__button--ghost {
  background: transparent;
  border-color: #ccc;
}

.card__button--primary {
  background: steelblue;
  color: white;
}

Two things to notice. The outer .card is a vertical Flexbox, so its children stack. The .card__actions is itself a horizontal Flexbox that pushes its buttons to the right. Nesting Flex containers is fine and extremely common — that is how most real interfaces are built.

The margin-top: auto on .card__actions is a small trick: when applied to a flex item, it pushes the item to the far end of its container. The actions stick to the bottom of the card no matter how tall the card grows.

Try it yourself. Build the card above. Then put three of them side by side in a row Flexbox with a gap. Resize the browser window. The cards stay neatly spaced. Add flex-wrap: wrap to the row so they reflow to a new line on narrow screens.

When to use Flexbox

A useful heuristic:

  • Use Flexbox when you are laying out items along a single line — a row or a column.
  • Use Grid when you are laying out items in two dimensions — rows and columns at the same time.

Most real pages use both. A page might use Grid for the overall layout and Flexbox inside each region. You do not have to choose one — they cooperate well.

A few practical tips

  • Reach for gap first rather than margin between items.
  • Center anything with display: flex; justify-content: center; align-items: center;.
  • Use flex: 1 on a child to make it fill remaining space.
  • Use margin-top: auto on a flex item to push it to the far end of the container.
  • Nest freely. A flex item can itself be a flex container.
  • Inspect with developer tools. Chrome and Firefox both have a Flexbox overlay that draws the axes and shows how each item is sized.

These five habits handle the vast majority of day-to-day layout work.

Recap

You now know:

  • Flexbox arranges items along a main axis and a cross axis
  • display: flex turns a container into a flex container
  • flex-direction controls whether items flow as a row or a column
  • justify-content aligns items along the main axis; align-items along the cross axis
  • gap adds space between items without margin tricks
  • flex-wrap: wrap lets items reflow to multiple lines
  • flex-grow and flex: 1 let items take a share of leftover space
  • Nesting flex containers is normal and powerful

Next steps

Flexbox handles one-dimensional layout beautifully. For two-dimensional layouts — rows and columns at the same time, the kind of structure used for full-page templates — the right tool is CSS Grid. That is where the next chapter of your CSS journey begins.

Keep building small layouts until Flexbox is automatic. Every hour spent practicing here saves a week of fighting CSS later.

Questions or feedback? Email codeloomdevv@gmail.com.