CSS Flex Container Properties
A flex container is any element with display: flex or display: inline-flex applied to it. Once an element becomes a flex container, its direct children become flex items, and a whole new set of layout rules takes over: instead of floats, inline flow, or absolute positioning, the browser distributes space along a main axis and a cross axis according to properties you set on the container. Understanding these container-level properties is the foundation of nearly all modern CSS layout, so mastering them thoroughly pays off across every project you build.
Overview / How it works
When you set display: flex on an element, the browser’s rendering engine switches that element into a flex formatting context. Two invisible axes are established: the main axis (the direction items are laid out along, controlled by flex-direction) and the cross axis (perpendicular to the main axis). By default, flex-direction is row, so the main axis runs left-to-right (in left-to-right languages) and the cross axis runs top-to-bottom.
Every flex item still has a box model — content, padding, border, margin — but the flex algorithm overrides how widths, heights, and positions along the main axis are ultimately resolved. Rather than each child simply stacking in normal flow, the container asks: how much space is available, how much do the items want, and how should any leftover (or missing) space be distributed? That distribution is governed entirely by container properties: justify-content controls spacing along the main axis, align-items and align-content control alignment along the cross axis, and flex-wrap decides whether items are forced onto a single line or allowed to wrap onto multiple lines (creating multiple “flex lines”).
It helps to remember that display: flex only affects the immediate children of the container — grandchildren are unaffected unless they themselves are made into flex containers. Also note that inline-flex behaves like flex for the children inside, but the container itself participates in the surrounding layout as an inline-level box rather than a block-level box.
Syntax
The general shape of a flex container ruleset looks like this (the pipe characters below just indicate the set of possible values — they aren’t literal syntax):
.container {
display: flex | inline-flex;
flex-direction: row | row-reverse | column | column-reverse;
flex-wrap: nowrap | wrap | wrap-reverse;
flex-flow: <flex-direction> <flex-wrap>;
justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;
align-items: stretch | flex-start | flex-end | center | baseline;
align-content: stretch | flex-start | flex-end | center | space-between | space-around;
gap: <row-gap> <column-gap>;
}
| Property | Purpose |
|---|---|
display |
Turns the element into a flex (or inline-flex) container. |
flex-direction |
Sets the main axis direction: row, row-reverse, column, or column-reverse. |
flex-wrap |
Allows items to wrap onto new flex lines instead of shrinking to fit one line. |
flex-flow |
Shorthand combining flex-direction and flex-wrap in one declaration. |
justify-content |
Distributes free space along the main axis. |
align-items |
Aligns items along the cross axis, within a single flex line. |
align-content |
Aligns entire flex lines within the container when there is extra space on the cross axis (only matters when wrapping produces multiple lines). |
gap, row-gap, column-gap |
Adds fixed spacing between items, without needing margins. |
Examples
Example 1: A simple navigation bar
Suppose you have a nav element containing several links, and you want them spaced evenly with vertical centering.
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
padding: 12px 24px;
background-color: #1f2937;
}
Result: The links sit in a single horizontal row. The first link is pushed to the far left, the last link to the far right, with any remaining links spread out evenly between them. All links are vertically centered within the bar’s height, even if one link happens to wrap to two lines of text.
Here, justify-content: space-between places the first and last items flush against the container edges and distributes leftover main-axis space evenly between the remaining items. align-items: center handles cross-axis (vertical) centering automatically — no need for old tricks like vertical-align or manual margin math.
Example 2: A wrapping card grid
.card-grid {
display: flex;
flex-flow: row wrap;
gap: 20px;
justify-content: flex-start;
align-items: stretch;
}
.card-grid > .card {
flex: 1 1 220px;
}
Result: Cards line up left-to-right. When the container becomes too narrow to fit another 220px-wide card, the next card drops to a new line beneath, instead of squeezing every card smaller and smaller. Every card in a row stretches to match the height of the tallest card in that same row.
flex-flow: row wrap is shorthand for flex-direction: row; flex-wrap: wrap;. Allowing wrap means the browser creates as many flex lines as needed. align-items: stretch (the default value) is what makes cards in the same row share equal height — a classic flexbox win over floats.
Example 3: Aligning multiple wrapped lines with align-content
.gallery {
display: flex;
flex-flow: row wrap;
height: 400px;
gap: 12px;
justify-content: center;
align-content: space-around;
}
Result: Thumbnail images wrap into several rows inside a 400px-tall gallery box. Because the combined height of all the rows is less than 400px, the extra vertical space is distributed around each row (equal space above and below every line), rather than all the rows bunching at the top.
This is the property most people forget: align-items only aligns items within their own line, while align-content aligns the lines themselves relative to each other. It only has a visible effect when flex-wrap produces two or more lines and the container has extra cross-axis space (for example, a fixed height taller than the content needs).
How it works step by step / Under the hood
When the rendering engine lays out a flex container, it roughly follows these steps:
- 1. Establish axes.
flex-directionis read first to determine which physical direction is “main” and which is “cross.” - 2. Collect flex items. Every direct child element (that isn’t
display: noneor absolutely positioned out of flow) becomes a flex item, in source order unlessorderis used on the item. - 3. Compute hypothetical sizes. Each item’s preferred main-axis size is calculated from its content and any
flex-basis/width/heightdeclared on the item. - 4. Decide on wrapping. If
flex-wrapisnowrap(the default), all items are forced onto a single line and may shrink viaflex-shrink. If wrapping is allowed, items that don’t fit are pushed onto additional flex lines. - 5. Distribute main-axis space. Once each line’s items have their final main-axis sizes, any leftover space (or, in
space-between/space-around/space-evenlycases, the gaps) is distributed according tojustify-content. - 6. Align on the cross axis. Within each line,
align-itemspositions items (or stretches them, the default). If there are multiple lines and extra cross-axis space,align-contentthen positions the lines relative to one another.
Because this all happens as one coordinated algorithm, changing a single container property — say, flipping flex-direction from row to column — effectively swaps what “main” and “cross” mean for every other property, which is why justify-content suddenly starts controlling vertical spacing once you switch to a column layout.
Common Mistakes
Mistake 1: Forgetting flex-wrap and getting squished items
Without flex-wrap, flex items shrink to fit a single line no matter how many there are, which often crushes content instead of overflowing gracefully:
.toolbar {
display: flex;
}
.toolbar > .button {
width: 180px;
}
If there are six 180px buttons in a narrow toolbar, every button shrinks below 180px to squeeze onto one line, making labels wrap awkwardly or truncate. The fix is to allow wrapping so extra buttons flow onto new lines instead of being crushed:
.toolbar {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.toolbar > .button {
flex: 0 0 180px;
}
Mistake 2: Using align-content when you meant align-items
A very common confusion is reaching for align-content to vertically center items in a plain, single-line row — but with only one flex line, align-content has no visible effect at all:
.row {
display: flex;
height: 120px;
align-content: center;
}
Because flex-wrap defaults to nowrap, there is only ever one flex line, so align-content is simply ignored by the layout algorithm. The correct property for aligning items within a single line is align-items:
.row {
display: flex;
height: 120px;
align-items: center;
}
Best Practices
- Use
gapinstead of margins on flex items for spacing — it avoids extra margin on the outer edges and works identically for rows and columns. - Reach for
flex-flowas a shorthand once you’re comfortable withflex-directionandflex-wrapindividually, to keep rulesets shorter. - Only rely on
align-contentwhen you know the container will wrap into multiple lines and has extra cross-axis space (usually a fixedheightormin-height). - Set
flex-wrap: wrapwhenever item count is dynamic or content-driven, so the layout degrades gracefully on small screens instead of crushing items. - Remember
display: flexonly changes how direct children are laid out — nested content still needs its own flex or block rules. - Prefer
justify-content: space-between/space-evenlyover manual margins when you want evenly distributed items with no wrapper elements.
Practice Exercises
- Build a page header with a logo on the left and three navigation links on the right, all vertically centered, using a single flex container rule.
- Create a flex container with
flex-direction: columnthat fills the full height of the viewport (height: 100vh) and vertically centers three stacked boxes usingjustify-content. - Take a row of eight fixed-width tags/badges and make them wrap into multiple centered lines, with equal spacing between every line, using
flex-wrapandalign-content.
Summary
display: flex(orinline-flex) turns an element into a flex container and its direct children into flex items.flex-directionsets the main axis;flex-wrapdecides whether items wrap onto multiple flex lines.flex-flowis shorthand forflex-directionplusflex-wrap.justify-contentdistributes space along the main axis;align-itemsaligns items within a line on the cross axis.align-contentonly matters with multiple flex lines and extra cross-axis space — it aligns the lines themselves, not individual items.gap(orrow-gap/column-gap) is the modern, margin-free way to space out flex items.
