flex-wrap is Flexbox pitfall #4. See the full list of Flexbox Pitfalls and How to Avoid Them.
Just when you think you're starting to get the hang of flexbox, you run into a situation where you don't have enough space on a single line for all of your flex items. No problem right, flex items can wrap! You add flex-wrap: wrap;
and watch as your precious flexbox confidence crumbles to ashes. But don't worry - there are just a couple of things to know about flex-wrap (and one thing to avoid entirely) and you'll be feeling flex-awesome again in no time.
The first gotcha with flex-wrap
is that flex items will only begin to wrap if their sum total flex-basis
is greater than the size of the flex container. So to understand how flex-wrap
works you also need to have a solid understanding of flex-basis
, which has many nuances of its own. For example flex-basis
falls back to width
when not defined. Also flex-basis
is bounded by the item's min-width
and max-width
which affects its "final" flex-basis
value. See The Difference Between Width and Flex-Basis to fully understand this property. If your flex items aren't wrapping like you expect them to, it's because of flex-basis
.
Flexbox Zombies chapter 7 covers flex-basis
in detail.
By default flex items shrink as soon as their total accumulative flex-basis
is lower than the size of their parent flex container. In this example our container is 400px
, and the sum total of all our final flex-basis
values is 800px
(4
200px
). Since that total (800px
) is larger than the container size (400px
) the flex items shrink* to fit.
.container { display: flex; width: 400px; } .item { height: 200px; /* items have shrunk already */ flex-basis: 200px; }
But once you tell your container to flex-wrap
, then wrapping become a priority over shrinking. Keep this formula in mind:
Notice in this example since we're wrapping, the items haven't even started to shrink yet because they just keep wrapping to get their own line and their flex-basis
(200px
) is still lower than the container size (250px
).
.container { display: flex; flex-wrap: wrap; width: 250px; } .item { height: 200px; /* items haven't starting shrinking yet */ flex-basis: 200px; }
But as soon as each item has its own line and the container size is lower than an items flex-basis
, then the item will shrink:
.container { display: flex; flex-wrap: wrap; width: 100px; } .item { height: 200px; /* items haven't starting shrinking yet */ flex-basis: 200px; }
flex-wrap
also affects how flex items grow. By default if you have items set to grow (flex-grow: 1
) they'll grow on a single line whenever there's free space available. In this example the flex-basis
sum is 400px
(2 * 200px
) and the container is 500px
. So the items grow to fill the extra space:
.container { display: flex; width: 500px; } .item { height: 200px; flex-basis: 200px; flex-grow: 1; }
But when you use wrapping, items treat the row they're on as the only space that matters, and will grow to fill any free space in their own line.
In this example there are three items at 200px
flex-basis
each, inside of a 500px
wide container. There's not enough room for them to all fit so the last item drops to a new line. It then treats that line as its very own and grows to fill the entire space inside of it. The remaining two items in the original line also grow a little bit to share the newly created 100px
of free space in the first line.
.container { display: flex; width: 500px; flex-wrap: wrap; } .item { height: 150px; flex-basis: 200px; flex-grow: 1; }
Once your flex items wrap to a new line, you need to learn how to control the positioning of those lines. This is what align-content
is all about. With it you can do cool things like space out the wrapped lines:
.container { display: flex; width: 200px; height: 350px; flex-wrap: wrap; align-content: space-between; } .item { height: 100px; flex-basis: 100px; }
Or bunch the lines together in the center of the flex container:
.container { display: flex; width: 200px; height: 350px; flex-wrap: wrap; align-content: center; } .item { height: 100px; flex-basis: 100px; }
Check out how I used flex-wrap
and align-content
to great effect on Link's hearts in the Zelda UI:
Flexbox Zombies chapter 10 covers all the align-content
options in detail.
The final flex-wrap
pitfall to watch out for is that flex-wrap: wrap-reverse
will give your brain a proper mind-bending from which you may never recover. The purpose of this value is to let you reverse the order of your wrapped lines. Usually new wrap lines are created after the existing line(s) like in all the examples above. That means new lines usually show up below when doing horizontal layouts, or to the right when doing vertical layouts. But setting flex-wrap: wrap-reverse
changes that so that new lines show up before existing lines. Above existing line(s) for horizontal layouts, and to the left of them for vertical layouts.
In this example the blue item didn't have enough room to fit on the same line so it moved to its own line. But this time because of flex-wrap: wrap-reverse
that new line is placed above the original line.
.container { display: flex; width: 500px; flex-wrap: wrap-reverse; } .item { height: 150px; flex-basis: 200px; flex-grow: 1; }
If that was all that flex-wrap: wrap-reverse
changed it wouldn't be too bad. But get this. It also reverses the align-items
and align-self
properties! Even when there's plenty of space so no wrapping is needed. Up is down, down is up, right is left, left is right.
See for yourself: in the code below delete the flex-wrap: wrap-reverse;
line and watch your world flip back right-side up.
.container { outline: 5px solid black; display: flex; width: 500px; height: 500px; flex-wrap: wrap-reverse; /* wrap-reverse made flex-end be at the TOP! */ align-items: flex-end; } .item { height: 100px; flex-basis: 100px; } .purple { /* wrap-reverse made flex-start be at the BOTTOM! */ align-self: flex-start; }
flex-wrap: wrap-reverse
does this same dizzying trick to the align-content
property - reversing the alignment axis - making flex-end
bunch the lines to the top of the container rather than to the bottom. Thanks wrap-reverse
!
.container { display: flex; width: 500px; height: 400px; flex-wrap: wrap-reverse; /* flex-wrap: wrap-reverse made flex-end be the TOP */ align-content: flex-end; } .item { height: 100px; flex-basis: 100px; }
If you're some kind of super human genius and can think of a way to keep things straight - "the enemy gate is down" or something - then go for it. Otherwise wrap-reverse
should be avoided. I haven't found a use for it yet myself. Most of the time you can just change the source order of your DOM elements anyways to get the effect you're after. Even if you never use it yourself, it's good to know what it does in case you encounter it in the wild.
Wrap your brain (see what I did there...) around the nuances of flex-wrap
by getting some practice: find a UI you like where flex-wrap
would be a good fit and build it yourself. Cement these concepts into muscle memory by shooting the crossbow in Flexbox Zombies chapter 9.
Flexbox is incredibly powerful. But it's also crazy hard to master. So we all end up depending on a cheat sheet and guessing in the dev tools. Enough of that! Time to master it once and for all, in a way that actually sticks, so you can build any layout you can imagine with flexbox.Master Flexbox