Centring is the problem flexbox is best known for, and the reason people are surprised by it is that there are two axes and the two properties have confusing names. Once you know which axis each one controls, centring is a two-line rule — and vertical centring stops being a special case.
Which property does what?
justify-content distributes the items along the main axis. align-items aligns them along the cross axis. In a row, the main axis is horizontal, so justify-content moves things left and right. In a column, the main axis is vertical, so justify-content moves things up and down.
.centred {
display: flex;
justify-content: center; /* along the main axis */
align-items: center; /* along the cross axis */
}
That pair centres an item in both directions inside its container, whatever the direction of the flow. In a row, the item ends up horizontally centred and vertically centred. In a column, vertically centred and horizontally centred. The names do not tell you which is which, which is why the rule of thumb is to say "main axis" and "cross axis" out loud.
What are the values?
/* along the main axis */
justify-content: flex-start; /* the default: pack to the start */
justify-content: center; /* pack to the middle */
justify-content: flex-end; /* pack to the end */
justify-content: space-between;/* first at the start, last at the end */
justify-content: space-around; /* equal space around each item */
justify-content: space-evenly; /* equal space between every item */
/* along the cross axis */
align-items: stretch; /* the default: fill the cross axis */
align-items: flex-start;
align-items: center;
align-items: baseline; /* align the text baselines */
The three space values are the ones that need explaining. space-between puts the first item at the start and the last at the end, with equal gaps between them — so a navigation row spreads its links out to the edges. space-around gives each item equal space around it, which means the outer gaps are half the size of the inner ones. space-evenly gives every gap the same size, including the ones at the ends.
<nav class="nav">
<a href="/docs">Docs</a>
<a href="/blog">Blog</a>
<a href="/about">About</a>
</nav>
.nav {
display: flex;
justify-content: space-between; /* first left, last right */
}
How do I centre one element in its parent?
For a single child, the same two properties work, and there is a second approach worth knowing: auto margins.
/* approach one: the alignment properties */
.hero {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
}
/* approach two: auto margins on the child */
.hero {
display: flex;
min-height: 60vh;
}
.hero > * {
margin: auto; /* absorbs the free space on both axes */
}
Approach two is useful when the child should be centred in one axis and the other items should be aligned differently, because it targets the child rather than the group. It also works for absolutely positioned elements, where it is the standard technique.
How do I align one item differently?
align-self overrides align-items for a single item, and it takes the same values.
.card {
display: flex;
flex-direction: column;
align-items: stretch; /* most items fill the width */
}
.card .badge {
align-self: flex-start; /* except this one, which hugs its text */
}
A common case is a row of cards where you want a badge in the corner of each card rather than stretched across it. align-self: flex-start does that without a wrapper element.
How do I centre a page on the screen?
Vertically centring a block in the viewport used to be genuinely hard, and flexbox made it a single line.
body {
margin: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
Note that the height comes from min-height rather than height, so the page still grows when the content is taller than the screen instead of clipping it. And the old position: absolute; top: 50%; transform: translateY(-50%) trick is still valid for absolutely positioned elements, where flexbox cannot help.
What is baseline alignment for?
Aligning items by their text baseline rather than by their edges, which is what you want when a row contains text of different sizes. It is the difference between a heading and a paragraph that look like they belong together and two pieces of text whose bottoms line up but whose tops do not.
.row {
display: flex;
align-items: baseline; /* align the text baselines */
gap: 0.5rem;
}
.row h2 { font-size: 1.5rem; margin: 0; }
.row p { margin: 0; }
Baseline is also the right choice for a row of icons next to a label, because the icon then sits on the text's baseline rather than floating in the middle of the line box.
What is the difference between space-around and space-evenly?
Both spread the items out, and the difference is in the gaps at the ends. space-around gives every item the same amount of space around it, so the space at the two ends is half the size of the space between items. space-evenly makes every gap the same size, including the end gaps.
space-around
| space | item | space | space | item | space |
the end gaps are half the size of the middle gaps
space-evenly
| space | space | item | space | space | item | space | space |
every gap is the same size
The practical difference shows up in a row of buttons at the bottom of a card. space-around pushes the first button half a gap from the edge, which looks like a mistake, while space-evenly looks deliberate. space-between is the other common choice: it puts the first item flush with the start and the last flush with the end, which is what a navigation row usually wants.
What does align-content do in a wrapping row?
It applies to the cross axis when the items wrap onto several lines, and it decides how those lines are distributed. With a single line it does nothing at all, which is why most people meet it late.
.cards {
display: flex;
flex-wrap: wrap;
align-content: space-between; /* spread the rows apart vertically */
min-height: 30rem;
}
The default is stretch, which stretches the rows to fill the container — and for a wrapping flex row, that means the items themselves grow taller, which is rarely what you want. Setting align-content: flex-start keeps the rows at their natural height and leaves the space at the bottom, which is the usual fix for a flex row that mysteriously stretches its cards.
Quick reference
justify-contentis the main axis;align-itemsis the cross axis.- Both set to
centercentres an item in both directions. space-betweenpushes to both ends;space-evenlyspaces every gap equally.- Auto margins on the child centre it without affecting the other items.
align-selfoverrides the container's alignment for one item.- Use
min-height: 100vhso a tall page still scrolls.
Try it yourself: centre a card in the viewport, then switch the container to a column and check that it is still centred in both directions. Next, replace one of the properties with an auto margin on the card and confirm the result is identical. Finally, use the developer tools overlay to see the main and cross axes change.