What is the difference between justify-content and align-items?
Advertisement
🧩 Scenario
Architecture Walkthrough
Different Axes, Different Jobs
justify-content operates on the main axis, the direction set by flex-direction. align-items operates on the cross axis, always perpendicular to it. In the default flex-direction: row, that means justify-content works horizontally and align-items works vertically; in column, the two swap.
Beyond the axis, they do genuinely different jobs.
justify-content distributes free space along the main axis. It answers: given leftover room after the items are sized, where does that room go? Before it, after it, between items, or spread evenly? Because it operates on free space, it has no effect at all when the items already consume the full main-axis size, which is one of the two common reasons it appears to do nothing.
align-items positions each item within the line's cross-axis extent. It answers: given that the line is a certain thickness, where does each item sit within it? Top, bottom, centre, on the text baseline, or stretched to fill?
Value Sets Differ, and the Differences Are Meaningful
The two properties share flex-start, flex-end, and center, but their remaining values reveal what each one is for.
Only justify-content has the distribution values space-between, space-around, and space-evenly, because only it deals with free space to distribute. space-between puts the first item at the start, the last at the end, and splits the remainder equally between them. space-around gives each item equal space on both sides, so the outer gaps look half the size of the inner ones. space-evenly makes every gap identical, including the outer ones.
Only align-items has stretch and baseline. stretch is its default value and is the reason flex children fill their container's cross axis without being asked, which is where equal-height columns come from for free. baseline aligns items by the baseline of their first line of text, which matters when items have different font sizes and you want their text to sit on a shared line rather than their boxes to be centred.
Why stretch Is the Default on One and Absent on the Other
The asymmetry is deliberate. On the main axis, growth is already controlled per item by flex-grow, so a container-level "stretch everything" value would be redundant and would conflict with per-item control. On the cross axis there is no per-item growth property, so stretching has to be expressed at the container level, which is where align-items: stretch lives. Individual items opt out with align-self.
This also explains why the equal-height card row works with no height declarations anywhere. align-items defaults to stretch, so every card grows to the height of the tallest one. Setting align-items: flex-start collapses them all to their content heights, and setting align-self: start on one card opts just that one out.
align-items Versus align-content
One more distinction, since it is where the confusion usually lands. align-items aligns items within their line. align-content distributes the lines themselves along the cross axis, and only does anything when flex-wrap has produced more than one line and the container has extra cross-axis space. On a single-line container, align-content appears to do nothing at all, which is not a bug.
Key Code Explained
/* The single most common Flexbox pattern in production */
.navbar {
display: flex;
justify-content: space-between; /* main axis: logo left, links right */
align-items: center; /* cross axis: everything vertically centred */
}
/* Distribution values exist ONLY on justify-content */
.toolbar {
display: flex;
justify-content: space-evenly; /* equal gaps including the outer ones */
}
/* space-between: no outer gaps
space-around: outer gaps look half the inner ones
space-evenly: all gaps identical */
/* stretch and baseline exist ONLY on align-items */
.equal-height-cards {
display: flex;
/* align-items: stretch is the DEFAULT — this is why cards match
the tallest one with no height declared anywhere */
}
.content-height-cards {
display: flex;
align-items: flex-start; /* opt out: each card takes its content height */
}
.one-card {
align-self: start; /* opt a single item out */
}
/* baseline: align by the first line of text, not by the box */
.price-row {
display: flex;
align-items: baseline; /* $ and 99 and /mo share a text baseline */
}
/* justify-content does nothing when there is no free space */
.filled {
display: flex;
justify-content: space-between;
}
.filled > * {
flex: 1; /* items consume all main-axis space -> nothing to distribute */
}
/* align-items vs align-content on a wrapped container */
.gallery {
display: flex;
flex-wrap: wrap;
height: 500px;
align-content: space-between; /* distributes the LINES */
align-items: center; /* positions items WITHIN each line */
}
The .navbar rule is the pattern to be able to write instantly, because it is the archetype: one property pins content to opposite ends of the main axis, the other centres it across the cross axis. Almost every header, toolbar, and list row in a real product is a variation of those two lines.
The .filled example explains the most common "justify-content is broken" report. flex: 1 on the children makes them absorb all available main-axis space, and since justify-content only distributes what is left over, there is nothing for it to do. The declaration is correct, the free space is not there.
Tradeoffs
| Aspect | justify-content | align-items |
|---|---|---|
| Axis | Main | Cross |
| Job | Distributes leftover free space | Positions items within the line |
| Default value | flex-start | stretch |
Has space-between / around / evenly | Yes | No |
Has stretch / baseline | No | Yes |
| Per-item override | None; use margin-inline-start: auto | align-self |
| Effect with no free space | None | Still applies |
What Interviewers Actually Check
- Whether you tie each property to an axis rather than to horizontal and vertical
- Whether you can articulate the different jobs: distributing free space versus positioning within the line
- Whether you know which values belong to which property and why
- Whether you know
align-itemsdefaults tostretchand can connect that to equal-height columns - Whether you can explain why
justify-contentsometimes has no visible effect
Follow-Up Questions
- There is no
justify-selfin Flexbox. How do you push a single item to the far end of the main axis? - What exactly does
align-items: baselinealign, and what happens to items that contain no text? - How do
space-aroundandspace-evenlydiffer numerically for three items in a 600px container? - Does
gapinteract withjustify-content: space-between, and if so, how? - How do the same two properties behave differently in Grid, where
justify-itemsandjustify-selfboth exist?
Common Candidate Mistakes
- Memorising
justify-contentas the horizontal property andalign-itemsas the vertical one, which breaks immediately in a column container - Expecting
justify-contentto reposition items that already fill the container, when it only distributes free space that in that case does not exist - Trying
space-betweenonalign-items, where the distribution values do not apply because items are positioned individually rather than distributed - Not knowing
align-itemsdefaults tostretch, and therefore adding explicit heights to make cards match when the behaviour was already free - Using
align-itemswhen the intent was to distribute wrapped lines, which isalign-content, and concluding the property is broken on a single-line container
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you state which axis each property operates on, for any
flex-direction? - Can you explain the different jobs of distributing free space versus positioning within the line?
- Can you list the values unique to each property and say why they belong there?
- Can you explain why
align-itemsdefaults tostretchand how a single item opts out? - Can you explain two reasons
justify-contentmight appear to do nothing?
Summary
justify-content aligns along the main axis and align-items aligns along the cross axis, so in a default row container the first works horizontally and the second vertically, and in a column container they swap. Binding them to axes rather than to screen directions is what makes them predictable across responsive direction changes.
They also do different jobs. justify-content distributes leftover free space, which is why it owns space-between, space-around, and space-evenly, and why it has no visible effect when the items already fill the container. align-items positions each item within its line's cross-axis extent, which is why it owns stretch and baseline. stretch being its default is the reason equal-height flex columns happen without any height declarations, and align-self is the per-item escape hatch. There is no equivalent justify-self in Flexbox, so pushing one item to the far end of the main axis is done with an auto margin.
The remaining distinction worth holding is align-items versus align-content: the first positions items inside a line, the second distributes wrapped lines across the container's cross axis, and the latter does nothing at all until flex-wrap has produced more than one line.
Why does align-items have a stretch value but justify-content does not?
Stretching along the main axis is handled by flex-grow on the items themselves, so the main axis distribution property does not need it. The cross axis has no equivalent per-item growth property, so stretch lives on align-items.
Does justify-content do anything when items fill the container?
No. It distributes free space, so if the items already consume all available main-axis space there is nothing left to distribute and it has no visible effect.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement