X Xerobit

CSS Flexbox — Complete Guide to flex-direction, justify-content, and align-items

Flexbox aligns items in one dimension — a row or a column. Here's how flex-direction, justify-content, align-items, flex-grow, and the other Flexbox properties work with visual...

Mian Ali Khalid · · 9 min read
Use the tool
Flexbox Generator
Visual CSS Flexbox playground. Live preview + copyable CSS. Control direction, wrap, justify, align, gap. Example child layouts.
Open Flexbox Generator →

Flexbox (Flexible Box Layout) is the CSS layout model for one-dimensional layouts — either a row or a column. It replaced the float-and-clear hacks developers used for years and made centering elements trivially easy.

Use the Flexbox Generator to build Flexbox layouts visually and copy the generated CSS.

How Flexbox works

Flexbox operates on two elements: the flex container and its flex items.

.container {
  display: flex; /* Activate flexbox */
}

Any element with display: flex becomes a flex container. Its direct children become flex items automatically. No class needed on the children.

<div class="container">   <!-- flex container -->
  <div>Item 1</div>       <!-- flex item -->
  <div>Item 2</div>       <!-- flex item -->
  <div>Item 3</div>       <!-- flex item -->
</div>

The two axes

Every Flexbox layout has two axes:

  • Main axis: the direction flex items are placed (controlled by flex-direction)
  • Cross axis: perpendicular to the main axis

Understanding which axis is “main” and which is “cross” is essential for using justify-content and align-items correctly.

With flex-direction: row (default):

  • Main axis: horizontal (left → right)
  • Cross axis: vertical (top → bottom)

With flex-direction: column:

  • Main axis: vertical (top → bottom)
  • Cross axis: horizontal (left → right)

flex-direction

Controls the direction of the main axis:

.container {
  flex-direction: row;            /* Default: left to right */
  flex-direction: row-reverse;    /* Right to left */
  flex-direction: column;         /* Top to bottom */
  flex-direction: column-reverse; /* Bottom to top */
}

Changing flex-direction also changes which property controls which axis. After flex-direction: column:

  • justify-content controls vertical alignment (main axis is now vertical)
  • align-items controls horizontal alignment (cross axis is now horizontal)

This is the most common Flexbox confusion — what justify-content does visually depends entirely on flex-direction.

justify-content

Controls alignment along the main axis (how items are distributed in the row/column direction):

.container {
  justify-content: flex-start;     /* Default: pack items to start */
  justify-content: flex-end;       /* Pack items to end */
  justify-content: center;         /* Center items */
  justify-content: space-between;  /* Even gaps between items, no edge gap */
  justify-content: space-around;   /* Even gaps around items (half-gap at edges) */
  justify-content: space-evenly;   /* Perfectly equal gaps everywhere */
}

Visual reference (flex-direction: row)

flex-start:     [1][2][3]        ← items at left
flex-end:               [1][2][3] ← items at right
center:           [1][2][3]      ← items centered
space-between:  [1]    [2]    [3] ← full gaps between, no edge gaps
space-around:   _[1]__[2]__[3]_  ← half-gaps at edges
space-evenly:   _[1]___[2]___[3]_ ← equal gaps everywhere

align-items

Controls alignment along the cross axis (perpendicular to flex-direction):

.container {
  align-items: stretch;      /* Default: fill cross axis */
  align-items: flex-start;   /* Align to start of cross axis */
  align-items: flex-end;     /* Align to end of cross axis */
  align-items: center;       /* Center on cross axis */
  align-items: baseline;     /* Align text baselines */
}

With flex-direction: row, align-items controls vertical alignment.

The most useful pattern: justify-content: center + align-items: center = perfectly centered content (horizontal and vertical).

.container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh; /* Make container fill viewport */
}

This replaces the old position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) hack.

flex-wrap

By default, flex items try to fit on one line, shrinking if needed. flex-wrap controls what happens when they overflow:

.container {
  flex-wrap: nowrap;       /* Default: all items on one line, may shrink */
  flex-wrap: wrap;         /* Items wrap to next line when needed */
  flex-wrap: wrap-reverse; /* Items wrap to previous line */
}

Use flex-wrap: wrap for responsive layouts where items should flow to the next row rather than shrink below a minimum size.

The flex shorthand

flex is shorthand for three properties: flex-grow, flex-shrink, and flex-basis.

.item {
  flex: 1;           /* flex-grow:1, flex-shrink:1, flex-basis:0 */
  flex: 2;           /* flex-grow:2, flex-shrink:1, flex-basis:0 */
  flex: 0 0 200px;   /* flex-grow:0, flex-shrink:0, flex-basis:200px (fixed width) */
  flex: 1 0 auto;    /* flex-grow:1, flex-shrink:0, flex-basis:auto */
}

flex-grow

Controls how much an item expands to fill available space (relative to other items):

/* Three items: ratios are 1:2:1 */
.item-a { flex-grow: 1; } /* Gets 25% of extra space */
.item-b { flex-grow: 2; } /* Gets 50% of extra space */
.item-c { flex-grow: 1; } /* Gets 25% of extra space */

flex: 1 on all items makes them equal-width (like width: 33.33% but responsive).

flex-shrink

Controls how much an item shrinks when there’s not enough space (relative to other items):

.item-a { flex-shrink: 1; } /* Default: shrinks proportionally */
.item-b { flex-shrink: 0; } /* Never shrinks — maintains flex-basis size */

Use flex-shrink: 0 for items that have a fixed size (like a sidebar or logo).

flex-basis

Sets the initial size of an item before grow/shrink:

.item { flex-basis: 200px; }  /* Start at 200px, then grow/shrink */
.item { flex-basis: 30%;    } /* Start at 30% of container */
.item { flex-basis: auto;   } /* Use item's natural size (default) */
.item { flex-basis: 0;      } /* All space is "extra" — grow from zero */

flex: 1 (which sets flex-basis: 0) and flex: 1 1 auto behave differently when items have varying content lengths. With flex-basis: 0, all items start at the same size and grow equally. With flex-basis: auto, items start at their content size and then grow by the same amount — which can result in unequal final sizes.

align-self

Override align-items for individual items:

.container { align-items: center; }

.special-item {
  align-self: flex-end; /* This item aligns to the bottom while others are centered */
}

gap

gap sets the spacing between flex items (replaces the margin hacks):

.container {
  display: flex;
  gap: 16px;              /* Same gap in both directions */
  gap: 16px 8px;          /* row-gap: 16px, column-gap: 8px */
  row-gap: 16px;          /* Only row gaps */
  column-gap: 8px;        /* Only column gaps */
}

gap is supported in all modern browsers (Chrome 84+, Firefox 63+, Safari 14.1+). It’s now the preferred way to add spacing between flex items — much cleaner than margin: 0 8px on each item.

order

Control the visual order of items without changing the HTML:

.item-last-in-html { order: -1; } /* Appears first visually */
.item-first-in-html { order: 2; } /* Appears last visually */
/* Default order for all items is 0 */

Use sparingly — changing visual order while leaving DOM order unchanged affects screen reader navigation and keyboard focus order.

Common Flexbox patterns

.nav {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 24px;
}

.nav-logo { margin-right: auto; } /* Pushes nav links to the right */

The margin-right: auto on the logo pushes all other items to the far right, creating a common nav pattern.

Card grid (equal-height cards)

.card-container {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
}

.card {
  flex: 1 1 280px; /* Grow, shrink, min 280px */
  display: flex;
  flex-direction: column; /* Stack content vertically */
}

.card-footer {
  margin-top: auto; /* Push footer to bottom of card */
}

flex: 1 1 280px creates a fluid grid: cards fill the row at minimum 280px each, wrapping to the next row when they don’t fit.

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

main { flex: 1; } /* Main grows to fill available space */
footer { /* Stays at bottom */ }

Centered hero section

.hero {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  min-height: 80vh;
  gap: 24px;
}

Flexbox vs Grid

Flexbox is one-dimensional (row OR column). CSS Grid is two-dimensional (rows AND columns).

Use Flexbox whenUse Grid when
Laying out items in a single row or columnCreating a two-dimensional layout
Content-driven layout (items determine size)Layout-driven content (grid determines size)
Navigation bars, card rows, button groupsFull-page layouts, card grids with defined columns
You want items to naturally wrapYou need explicit column/row control

They’re complementary — use Grid for the page layout, Flexbox for components within grid areas.

The Flexbox Generator

The Flexbox Generator lets you visually toggle all the container and item properties and see the result in real time. Copy the generated CSS for use in your project. It’s useful for:

  • Learning what each property does visually
  • Finding the right combination of justify-content and align-items for a specific layout
  • Generating starter CSS for a new component

Related posts

Related tool

Flexbox Generator

Visual CSS Flexbox playground. Live preview + copyable CSS. Control direction, wrap, justify, align, gap. Example child layouts.

Written by Mian Ali Khalid. Part of the Frontend & Design pillar.