Mastering CSS Flex and Grid Layouts
/ 10 min read
Remember those dark days when CSS layouts were a nightmare? float: left
paired with clear: both
, countless hacks and workarounds just to make elements behave properly. Fortunately, modern CSS blessed us with two layout superpowers: Flex and Grid.
If you’re already using these layout methods but occasionally get confused by axes and alignment, this review guide is perfect for you. We’ll start from the fundamental concepts, move to practical usage techniques, explore Tailwind’s atomic class abstractions, and wrap up with some real-world insights.
Ready? Let’s dive in!
Flex
Flex is short for “flexible layout,” and as the name suggests, it’s all about flexibility. (Cue the boing sound effect!)
Flexibility
In a flex container, we only need to set widths for some items, while others can dynamically adjust their size to fill the remaining space.
Here’s the most common use case in code:
<div class="fixed-flex"> <div class="side">Sidebar</div> <div class="main">Main Content</div></div>
<style> .fixed-flex { display: flex; } .side { width: 200px; background: #ffecb3; } .main { flex-grow: 1; flex-shrink: 1; flex-basis: 0%; background: #fff9c4; }</style>
The .side
has a fixed width of 200px
, while for .main
:
- No explicit width:
flex-basis: 0%
- Grows when extra space is available:
flex-grow: 1
- Shrinks when space is limited:
flex-shrink: 1
These 3 values can be shortened to flex: 1
. However, beginners should avoid using flex
shorthand other than flex: 1
, as the syntax can be quite complex.
Wrapping
Sometimes you don’t want to squeeze everything into a single line. Flex provides wrapping options for that.
<div class="wrap-layout"> <div>Akira</div> <div>Astro Boy</div> <div>Cowboy Bebop</div> <div>Dragon Ball Z</div> <div>Fullmetal Alchemist</div> <div>Ghost in the Shell</div> <div>Mobile Suit Gundam</div> <div>Neon Genesis Evangelion</div> <div>Princess Mononoke</div> <div>Sailor Moon</div></div><style> .wrap-layout { display: flex; flex-wrap: wrap; gap: 10px; } .wrap-layout div { width: auto; background: #c8e6c9; padding: 10px; text-align: center; }</style>
By default, flex layout is single-line, and elements get squeezed when there’s insufficient space. Adding flex-wrap: wrap;
allows items to maintain their width and wrap to new lines automatically.
Cross Axis
Flexbox is designed as a one-dimensional layout system—it’s either row or column. This means all child elements line up along the main axis. However, wrapping inevitably creates a two-dimensional layout, introducing the concept of the cross axis.
By default, the horizontal direction is the main axis, and wrapping creates a cross axis perpendicular to it.
CSS properties related to the main axis have the justify
prefix:
justify-content
CSS properties related to the cross axis have the align
prefix:
align-content
align-items
align-self
align-content
controls the distribution between lines in multi-line scenarios—it won’t take effect without wrap
; align-items
controls the alignment of elements within a single line.
Direction
We can switch the main axis direction using flex-direction: column;
.
<div class="column-layout"> <div>Top</div> <div>Middle</div> <div>Bottom</div></div>
<style> .column-layout { display: flex; flex-direction: column; gap: 10px; }</style>
It’s that simple! Just one line of flex-direction: column;
switches the main axis from horizontal to vertical. The tricky part is the mental adjustment it requires.
All the concepts we discussed—main axis, cross axis, justify, align, wrapping—remain the same. You just need to reframe your understanding with a vertical main axis. It’s not difficult, just takes time to adapt.
Centering
Flex provides the most convenient centering method of the modern era: justify-content: center;
and align-items: center;
.
Veterans who’ve been doing frontend since 2015 still remember the days when we had to memorize various CSS centering hacks. Now that browsers have caught up, Flex solves most centering problems.
As mentioned above, justify-content
controls distribution along the main axis, while align-items
controls alignment along the cross axis. Combined, they achieve perfect centering within a flex container.
<div class="center"> <p>Centered Content</p></div><style> .center { display: flex; justify-content: center; align-items: center; height: 200px; background: #f0f0f0; }</style>
Now someone might ask: “Can’t we use align-content
to center from a line perspective?” Yes, you can, but as mentioned earlier, you must set wrap
for align-content
to take effect, so you need an extra line:
.center { display: flex; justify-content: center; flex-wrap: wrap; align-content: center; height: 200px; background: #f0f0f0;}
You can play around with Flex’s wrap
, align
, and justify
properties and see their effects on element layout at this interactive learning site.
Bonus: What to do when flex containers overflow? When you set text to not wrap, flex containers often get stretched beyond their bounds. Remember to set the overflow
property in such cases. I find this quirky—when layouts are vertical, I easily remember to set overflow
, but when using Flex with horizontal layouts, I always forget! 😂
Grid
Grid layout is essentially a game of drawing grids. Let’s see how to draw these grids with an example:
.container { display: grid; grid-template-columns: 100px 200px auto; grid-template-rows: 50px auto 50px;}
In some ways, Grid is simpler than Flex because you don’t need to constantly switch between main axis and cross axis concepts—less mental overhead. With Grid, you just need to know how to set up rows and columns.
grid-template-columns
defines the grid’s columns. 100px 200px auto
means the first column is 100px, the second is 200px, and the third is auto-sized.
grid-template-rows
defines the grid’s rows. 50px auto 50px
means the first row is 50px, the second is auto-sized, and the third is 50px.
There’s also a very useful unit called fr
, which represents flexible fractions (fractional units of remaining space). For example:
.container { display: grid; grid-template-columns: repeat(3, 1fr); /* 1fr 1fr 1fr */}
This divides the columns into 3 equal parts.
Alignment
Flexbox children are always tightly packed along the main axis with no extra space for individual alignment, so Flex doesn’t have justify-items
. Alignment along the main axis is uniformly controlled by the parent container’s justify-content
property, which determines how all child elements are distributed and aligned as a whole along the main axis.
In contrast, Grid is about drawing grids. Once the grid is drawn, child elements can do their own thing within their cells, naturally supporting justify-items
.
Named Grid Areas
<div class="grid-container"> <header class="header">Header</header> <nav class="nav">Navigation</nav> <main class="content1">Content 1</main> <main class="content2">Content 2</main> <footer class="footer">Footer</footer></div><style> .grid-container { display: grid; grid-template-areas: "header header header" "nav content1 content1" "nav content2 content2" "footer footer footer";
grid-template-columns: 150px 1fr 1fr; grid-template-rows: 100px 1fr 1fr 60px;
gap: 10px; height: 90vh; } .header { grid-area: header; background-color: #f0c3a2; } .nav { grid-area: nav; background-color: #a2d2ff; } .content1 { grid-area: content1; background-color: #b7efc5; } .content2 { grid-area: content2; background-color: #b7efc5; } .footer { grid-area: footer; background-color: #f2e293; }</style>
This is quite fun, I must say! After configuring row and column dimensions, you can “draw” the page structure with text:
"header header header""nav content1 content1""nav content2 content2""footer footer footer"
Grid Spanning
Besides “drawing” with grid-template-areas
, you can also design page elements using grid spanning.
.grid-container { display: grid; grid-template-columns: 150px 1fr 1fr; grid-template-rows: 100px 1fr 1fr 60px; gap: 10px; height: 90vh;}
.header { grid-column: 1 / 4; grid-row: 1 / 2; background-color: #f0c3a2;}.nav { grid-column: 1 / 2; grid-row: 2 / 4; background-color: #a2d2ff;}.content1 { grid-column: 2 / 4; grid-row: 2 / 3; background-color: #b7efc5;}.content2 { grid-column: 2 / 4; grid-row: 3 / 4; background-color: #b7efc5;}.footer { grid-column: 1 / 4; grid-row: 4 / 5; background-color: #f2e293;}
The numbers after grid-column
represent content from the Nth grid line to the Mth grid line. This might seem abstract—why use grid lines instead of saying “Nth column”?
Actually, there is a column-based syntax too, for example:
.header { grid-column: 1 / span 3; grid-row: 1 / span 1; background-color: #f0c3a2;}
If you want to control a specific span range, you still need to start with a number, then add span number
. 1 / span 3
means starting from the 1st grid line, spanning 3 columns.
Writing grid-column: span 3;
directly only represents 3 columns but doesn’t control the starting position.
Alignment
Grid also has the align-
and justify-
alignment system, with effects similar to those mentioned in Flex. You can play around with Grid’s align
and justify
properties and see their effects on element layout at this interactive learning site.
place-content
is a CSS shorthand property that sets both align-content
and justify-content
properties simultaneously. It’s mainly used for Grid layout, since Flex layout rarely sets these two properties together. Here’s an example:
place-content: center;place-content: end space-between;
With one value, it applies to both horizontal and vertical directions. With two values, the order is place-content: <align-content> <justify-content>;
.
If there’s content
, is there also items
? Yes, my friend, as you might guess, there’s place-items
, and its usage follows the same logic—no need to elaborate further.
Grid has slightly less browser support than Flex, but the future looks promising. Users are updating their browsers more easily now, and the need to support legacy browsers is greatly reduced compared to before.
Tailwind
Tailwind wraps common Flex and Grid APIs into atomic classes that can be combined directly in class attributes. In some ways, learning Flex layout directly through Tailwind might be easier, but if you’re a Tailwind opponent, feel free to skip this section.
Let’s recreate the key examples above using class names:
Flex Basics
- Container and direction:
flexflex flex-colflex-row-reverseflex-col-reverse
- Spacing and wrapping:
gap-4gap-x-6 gap-y-2flex-wrapflex-nowrap
Flex Alignment
- Main axis/cross axis (container):
flex justify-between items-centerflex justify-center items-stretch
- Multi-line content distribution (container, only noticeable when flex-wrap is active):
flex flex-wrap content-betweenflex flex-wrap content-center
- Individual item alignment (child items):
self-startself-centerself-endself-stretch
Flex Item Sizing and Order (child items)
- Ratio/grow/shrink/basis:
flex-1growshrink-0basis-40
Grid Basics
- Container and grid tracks:
gridgrid-cols-3grid-rows-4
- Using custom column/row sizes (corresponding to 150px / 1fr / 1fr and 100px / 1fr / 1fr / 60px from above):
grid grid-cols-[150px_1fr_1fr] grid-rows-[100px_1fr_1fr_60px] gap-2 h-[90vh]
Recreating the Layout Above with Spanning
Tailwind doesn’t natively provide atomic classes for grid-template-areas
. The example above can be implemented using col-start/col-end and row-start/row-end combinations:
- Header (spans 3 columns, 1 row):
col-start-1 col-end-4 row-start-1 row-end-2
- Nav (1st column, spans from 2nd to 4th row):
col-start-1 col-end-2 row-start-2 row-end-4
- Content1 (columns 2-3, row 2):
col-start-2 col-end-4 row-start-2 row-end-3
- Content2 (columns 2-3, row 3):
col-start-2 col-end-4 row-start-3 row-end-4
- Footer (spans 3 columns, 1 row):
col-start-1 col-end-4 row-start-4 row-end-5
You can also use shorthand:
col-span-3 row-span-1
(Use start/end when determining starting lines; use col-span-/row-span- when only controlling span.)
Grid Alignment
- Set both align-content and justify-content (container):
place-content-centerplace-content-between
- Set both align-items and justify-items (container):
place-items-center
- Horizontal only/vertical only (container):
justify-items-startalign-items-end
- Auto-placement direction and dense packing:
grid-flow-colgrid-flow-row-dense
Takeaways
- Flex is better suited for one-dimensional distribution and alignment, while Grid excels at two-dimensional grids with row and column spanning. In real projects, a common strategy is “outer Grid for layout zones, inner Flex for content arrangement.”
- Remember the axes:
justify-*
works on the main axis,align-*
works on the cross axis. When switching to column direction, both “rotate” along with the main axis. - Impact of wrapping:
align-content
only takes effect whenflex-wrap
is set. Focus onalign-items/align-self
for single lines, consideralign-content
for multiple lines. - Flex item sizing principle: Fixed + adaptive = fixed width + flex: 1 (equivalent to grow-1 shrink-1 basis-0%). Except for flex: 1, beginners should use more complex flex shorthand cautiously.
- Grid’s core is “drawing grids”: grid-template-columns/rows with fr units define tracks; use grid-column/grid-row for spanning (like 1 / 4 or 1 / span 3); template-areas provides intuitive named layouts.
- Alignment overview: Grid also supports
justify-items/align-items
and shorthand combinations likeplace-content/place-items
, controlling both “content as a whole” and “within cells.” - Compatibility and practice: Grid support is slightly less than Flex, but modern browsers are generally compatible. Choose Flex conservatively for legacy environments; prioritize Grid + Flex combinations for new projects.