Updated Jul 6, 2026

Flexbox: One-Dimensional Layout

Flexbox arranges children of a container along a single line - a row or a column. That's the whole premise: one dimension at a time. It's the right tool anytime you're thinking "put these things next to each other" or "stack these and space them out."

Turning it on

One property starts it:

.navbar {
  display: flex;
}

Every direct child of .navbar is now a flex item. By default they line up in a row, left to right, each sized to its own content - no floats, no inline-block, no clearfix.

Main axis vs cross axis

Flexbox thinks in two axes, and almost every property is "along the main axis" or "along the cross axis":

  • Main axis - the direction items flow. Row by default (left to right). Set flex-direction: column to flow top to bottom instead, which swaps which axis is "main."
  • Cross axis - perpendicular to the main axis. Row layout → cross axis is vertical. Column layout → cross axis is horizontal.
flex-direction: row  (default)
┌─────────────────────────────┐
│  [Item] [Item] [Item]   →   │  ← main axis (horizontal)
│    ↕ cross axis (vertical)  │
└─────────────────────────────┘

Once that clicks, justify-content and align-items stop being two properties to memorize and become one idea applied twice.

Positioning items: justify-content and align-items

  • justify-content - spacing along the main axis: flex-start, center, flex-end, space-between, space-around.
  • align-items - alignment along the cross axis: flex-start, center, flex-end, stretch (default).
.navbar {
  display: flex;
  justify-content: space-between; /* push children to opposite ends */
  align-items: center;            /* vertically center them */
}

What just happened: in a row layout, justify-content controls left-right spacing and align-items controls up-down alignment. space-between is what makes "logo left, links right" a one-line fix instead of a positioning puzzle.

💡 Key point. "Center a div" - the CSS joke for a decade - is two lines:

.center-me {
  display: flex;
  justify-content: center;
  align-items: center;
}

align-self overrides align-items for one specific item, when everyone else stays aligned one way and a single item needs to sit differently:

.navbar .help-link {
  align-self: flex-end; /* this one link sits low while everything else centers */
}

Build it: a real navbar

The logo-left, links-right navbar, done properly:

<nav class="navbar">
  <div class="logo">Acme</div>
  <ul class="nav-links">
    <li><a href="/">Home</a></li>
    <li><a href="/docs">Docs</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem 2rem;
}

.nav-links {
  display: flex;
  gap: 1.5rem;
  list-style: none;
}

What just happened: the outer .navbar flex container pushes .logo and .nav-links to opposite ends and centers them vertically. The inner .nav-links is also a flex container, laying its <li>s out in a row with gap for spacing - no margin math, no :last-child { margin-right: 0 } cleanup. Flex containers nest freely; each one only manages its own children.

Wrapping: flex-wrap

By default, flex items shrink to fit on one line, however cramped that gets. flex-wrap: wrap lets them drop to a new line instead:

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

Without flex-wrap, five cards in a narrow viewport squeeze into an unreadable sliver. With it, cards that don't fit flow onto the next row, like text wrapping at the edge of a paragraph.

Sizing items: flex-grow, flex-shrink, flex-basis

Three properties control how an item's size responds to available space, almost always used through the flex shorthand (flex-grow flex-shrink flex-basis):

  • flex-grow - how much of the leftover space this item claims, relative to siblings. 0 (default) means it won't grow.
  • flex-shrink - how much this item shrinks when there isn't enough space. 1 (default) means it shrinks proportionally.
  • flex-basis - the item's starting size before growing/shrinking, like width but flex-aware.

Build it: a row of equal-width cards

<div class="card-row">
  <div class="card">Card 1</div>
  <div class="card">Card 2</div>
  <div class="card">Card 3</div>
</div>
.card-row {
  display: flex;
  gap: 1rem;
}

.card {
  flex: 1;
  padding: 1.5rem;
  border: 1px solid #ddd;
  border-radius: 8px;
}

What just happened: flex: 1 is shorthand for flex-grow: 1; flex-shrink: 1; flex-basis: 0%. Each card starts at zero width, then grows to fill the row equally - three cards, three equal thirds, recalculated automatically whether you have three cards or five. Change one card to flex: 2 and it claims twice the leftover space of its siblings; a common pattern for a "featured" card that's wider than the rest.

⚠️ Gotcha. flex: 1 and width: 33% look similar but behave differently on resize: percentage widths are fixed fractions of the container, while flex: 1 items renegotiate space with their siblings every time one shrinks, grows, or wraps. For equal-width items in a flex row, flex: 1 is the right tool - it rebalances space instead of dividing it once and freezing.

Check your intuition with a quick quiz:

[
  {
    "q": "In a default (row) flex container, what does justify-content control?",
    "choices": ["Vertical alignment", "Horizontal spacing along the main axis", "The stacking order of items", "The font size of items"],
    "answer": 1,
    "explain": "justify-content works along the main axis, which is horizontal in a row layout. align-items handles the cross (vertical) axis."
  },
  {
    "q": "Three items each have flex: 1 in a flex row. What happens if you change one to flex: 2?",
    "choices": ["It disappears", "It becomes exactly twice as wide in pixels, always", "It claims roughly twice the leftover space of its siblings", "Nothing, flex-grow is ignored without flex-basis set separately"],
    "answer": 2,
    "explain": "flex-grow values are compared as ratios among siblings sharing the leftover space, not absolute sizes."
  },
  {
    "q": "Your flex row of five cards is unreadable on a narrow screen because they're all squeezed onto one line. What fixes it?",
    "choices": ["flex-direction: column-reverse", "flex-wrap: wrap", "justify-content: space-around", "align-self: stretch"],
    "answer": 1,
    "explain": "flex-wrap: wrap lets items that don't fit flow onto a new line instead of shrinking indefinitely."
  }
]

Guide overview · Phase 2: CSS Grid: Two-Dimensional Layout →

Check your understanding 3 questions

1. In a default (row) flex container, what does justify-content control?

2. Three items each have flex: 1 in a flex row. What happens if you change one to flex: 2?

3. Your flex row of five cards is unreadable on a narrow screen because they're all squeezed onto one line. What fixes it?