How to make Flexbox items the same size
Asked Answered
S

12

523

I want to use Flexbox that has some number of items that are all the same width. I've noticed that Flexbox distributes the space around evenly, rather than the space itself.

For example:

.header {
  display: flex;
}

.item {
  flex-grow: 1;
  text-align: center;
  border: 1px solid black;
}
<div class="header">
  <div class="item">asdfasdfasdfasdfasdfasdf</div>
  <div class="item">z</div>
</div>

The first item is a lot bigger than the second. If I have three items, four items, or n items, I want them all to appear on the same line with an equal amount of space per item.

Any ideas?

http://codepen.io/anon/pen/gbJBqM

Supralapsarian answered 7/4, 2015 at 23:33 Comment(0)
C
750

Set them so that their flex-basis is 0 (so all elements have the same starting point), and allow them to grow:

flex: 1 1 0px;

Your IDE or linter might mention that the unit of measure 'px' is redundant. If you leave it out (like: flex: 1 1 0), IE will not render this correctly. So the px is required to support Internet Explorer, as mentioned in the comments by @fabb;

Chilon answered 7/4, 2015 at 23:35 Comment(13)
It should be noted that the flex property is a shorthand property for the flex-grow, flex-shrink and flex-basis properties. It's recommended to use the shorthand over the individual properties as the shorthand correctly resets any unspecified components to accommodate common uses. The initial value is 0 1 auto.Lion
note that IE11 ignores unitless flex-basis values: github.com/philipwalton/flexbugs#flexbug-4Szabo
I had to add min-width: 0 to achieve using child elements with overflowing text. See css-tricks.com/flexbox-truncated-textStrobila
This is working fine unless you're not using Safari on iPhone. I came up with the same workaround and I almost got a heart attack after I saw how the site looks on iPhone. p.s. It looks just fine when you make mobile preview on Chrome/Firefox, only iPhone's Safari doesn't understand flex-basis: 0.Gamin
I just want to mention that the Internet Explorer (IE) should be ignored. This is an obsolete browser like e.g. Netscape Navigator. The IE was officially replaced by Microsoft Edge. The latest version also runs on Chromium (like Chrome and other modern browsers.) We as web developers should no longer support IE. It is your responsibility to get rid of this crutch. :) Never talk about IE again.Donavon
This does not seem to work if some of the items contain padding. Removing the padding makes all the child elements the same size. Any suggestions to avoid removing padding?Pea
@Pea put the padding on an inner element instead of the flex child itself. For example, inside your flex child put a div and gave that wrap all the inner contents, then put the padding you want on the div.Chilon
@Adam, thanks yes, I ended up having to put another wrapper around the child elements, rather than changing the padding on the child elements, as that would have too many other knock on effects elsewhere the component is used. Was hoping Flexbox would in the 'modern options' of putting an end to unnecessary div's but guess not!Pea
This is a best answer!Eurhythmics
Definitely we this best.. if any hint to make any particular column increased compared with others.Wether
I wrote a sample code here that do the same you want codesandbox.io/s/serene-bush-6tqte?file=/index.htmlLachrymatory
You can use flex: 1 instead of flex: 1 1 0px as it means the same thing. See developer.mozilla.org/en-US/docs/Web/CSS/flex.Ordnance
It would be good to add the selector around the CSS declaration in your answer, so the reader can see right away whether you're referring to the .item or the parent wrap .header. Also, 0 works as shorthand for 0px.Inserted
R
216

You need to add width: 0 to make columns equal if contents of the items make it grow bigger.

.item {
  flex: 1 1 0;
  width: 0;
}

Detail: flex: 1 1 0 is the same as flex-grow: 1; flex-shrink: 1; flex-basis: 0; and if the parent container can not provide enough space for the native-size added together of every item (no space to grow), we need to make the width: 0 to give every item the same start point to grow.

Radiopaque answered 16/11, 2017 at 6:59 Comment(11)
This appears to be necessary if the contents of .item cause it to grow wider than .item would naturally be.Florina
I think you mean flex: 1 1 0;, instead of flex-grow: 1 1 0;Quarles
setting the flex-basis to 0 serves the same purpose as setting the width to 0.Exchequer
In my use case, I was wrapping charts that dynamically adjust to their parent's size and width 0 was necessary for them to work. I am not sure how they were determining the proper width, but flex basis certainly did not work on chrome 72 (released Jan 2019).Millar
I think, this reply more satisfying when it fulfilled with text context that don't have any space.Predigest
This was the only solution that worked on my situation. Seems that in my case, children was growing bigger than it naturally would, and flex: 1 1 0 was not having any effectKirimia
Some of my child elements have padding, and that is messing with the calculations. The width:0 does not seem to fix this. Any ideas?Pea
wow today i learned, thank you I also see this necessary in my case where text overflowedShirleyshirlie
Works but what an absolutely mess coding wise. Impossible to figure out by looking at the code "oh of course this is width 0"Filberte
This is absolutely necessary when item contents are too big and push the flex item to become big. Also, this looks like a flaw in flex that it would not be solvable with flex properties. You wrapped up my 1 hour of Googling!Libertarian
@Filberte Its wierd that this is not achievable with flex properties themselves and we have to resort to such hacks to get this done. Something seems missing in the Flex standard.Libertarian
A
153

You could add flex-basis: 100% to achieve this.

Updated Example

.header {
  display: flex;
}

.item {
  flex-basis: 100%;
  text-align: center;
  border: 1px solid black;
}

For what it's worth, you could also use flex: 1 for the same results as well.

The shorthand of flex: 1 is the same as flex: 1 1 0, which is equivalent to:

.item {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 0;
  text-align: center;
  border: 1px solid black;
}
Adenosine answered 7/4, 2015 at 23:35 Comment(9)
This is not correct. The default flex value is flex: 0 1 auto. Which means setting flex: 1 will result in flex: 1 1 auto. This will not work. The correct answer is flex: 1 1 0 as stated above by AdamClinician
@Clinician No, you are the one who is incorrect. As I stated in my answer, the shorthand of flex: 1 results in flex: 1 1 0 not flex: 1 1 auto like you are claiming. Take a look at the official W3 specification under the 'flex shorthand' section: when flex-basis is "omitted from the flex shorthand, its specified value is 0", not auto. Thanks for the random downvote.Adenosine
@Clinician - Also, take a look at this example I created to visualize the variations.Adenosine
Mate, I don't do random down-vote. I test before I write. And I did test it. Your answer does not work - simple as that. I had exactly the same issue not long ago with flex: 1 and flex-direction: column. The children height was not the same when other elements are placed within children. The only thing that fixed it was setting flex: 1 1 0. It still stand today with chromium 55.0.2883.87Clinician
@JoshCrozier confirmed that flex: 1 has the same behavior as flex: 1 1 0, and flex: 0 1 auto has different behavior.Filippa
I had flex: 1 in my code, but items with more content were still wider, ruining the grid effect. My search landed me here, and adding flex-grow: 1 fixed the problem. Turns out I need both.Anthology
When using whitespace: nowrap then the item will still grow.Raceway
BTW: if want more than 1 row of items, let's say 2 rows of 2 items, you can set flex-basis to 50% (or whatever portion of the parent width you want the items to take up), or a bit smaller if you are using a gap setting between them.Aceae
Old but, @JoshCrozier your answer is completely accurate. ThanksBusboy
M
45

The accepted answer by Adam (flex: 1 1 0) works perfectly for flexbox containers whose width is either fixed, or determined by an ancestor. Situations where you want the children to fit the container.

However, you may have a situation where you want the container to fit the children, with the children equally sized based on the largest child. You can make a flexbox container fit its children by either:

  • setting position: absolute and not setting width or right, or
  • place it inside a wrapper with display: inline-block

For such flexbox containers, the accepted answer does NOT work, the children are not sized equally. I presume that this is a limitation of flexbox, since it behaves the same in Chrome, Firefox and Safari.

The solution is to use a grid instead of a flexbox.

When you run this snippet, make sure to click on full page to see the effect properly.

body {
  margin: 1em;
}

.wrap-inline-block {
  display: inline-block;
}

#div0, #div1, #div2, #div3, #div4 {
  border: 1px solid #888;
  padding: 0.5em;
  text-align: center;
  white-space: nowrap;
}

#div2, #div4 {
  position: absolute;
  left: 1em;
}

#div0>*, #div1>*, #div2>*, #div3>*, #div4>* {
  margin: 0.5em;
  color: white;
  background-color: navy;
  padding: 0.5em;
}

#div0, #div1, #div2 {
  display: flex;
}

#div0>*, #div1>*, #div2>* {
  flex: 1 1 0;
}

#div0 {
  margin-bottom: 1em;
}

#div2 {
  top: 15.5em;
}

#div3, #div4 {
  display: grid;
  grid-template-columns: repeat(3,1fr);
}

#div4 {
  top: 28.5em;
}
<p>Normal scenario — flexbox where the children adjust to fit the container — and the children are made equal size by setting {flex: 1 1 0}</p>

<div id="div0">
  <div>
    Flexbox
  </div>
  <div>
    Width determined by viewport
  </div>
  <div>
    All child elements are equal size with {flex: 1 1 0}
  </div>
</div>

<p>Now we want to have the container fit the children, but still have the children all equally sized, based on the largest child. We can see that {flex: 1 1 0} has no effect.</p>

<div class="wrap-inline-block">
<div id="div1">
  <div>
    Flexbox
  </div>
  <div>
    Inside inline-block
  </div>
  <div>
    We want all children to be the size of this text
  </div>
</div>
</div>

<div id="div2">
  <div>
    Flexbox
  </div>
  <div>
    Absolutely positioned
  </div>
  <div>
    We want all children to be the size of this text
  </div>
</div>

<br><br><br><br><br><br>
<p>So let's try a grid instead. Aha! That's what we want!</p>

<div class="wrap-inline-block">
<div id="div3">
  <div>
    Grid
  </div>
  <div>
    Inside inline-block
  </div>
  <div>
    We want all children to be the size of this text
  </div>
</div>
</div>

<div id="div4">
  <div>
    Grid
  </div>
  <div>
    Absolutely positioned
  </div>
  <div>
    We want all children to be the size of this text
  </div>
</div>
Motteo answered 23/5, 2019 at 1:50 Comment(2)
Is there a way to get the same effect without hardcoding the number of children in css?Madcap
@Madcap grid-template-columns: repeat(auto-fill, minmax(100px, auto)); More details at css-tricks.com/…Petersburg
I
9

I’m not an expert with Flexbox, but I got there by setting the basis to 50% for the two items I was dealing with. Grow to 1 and shrink to 0.

Inline styling: flex: '1 0 50%',

Insatiable answered 14/4, 2020 at 13:42 Comment(1)
This was the one that surprisingly worked for me. Tyty.Rajkot
D
7

None of these solutions worked for me, but this did:

.header {
  display: flex;
}

.item {
  width: 100%;
}


/* Demo styles, for aesthetics. */

.demo {
  margin: 3rem;
}

.demo .item {
  text-align: center;
  padding: 3rem;
  background-color: #eee;
  margin: 0 1.5rem;
}
<div class="demo">
  <div class="header">
    <div class="item">
      1
    </div>
    <div class="item">
      2
    </div>
    <div class="item">
      3
    </div>
  </div>
</div>

<div class="demo">
  <div class="header">
    <div class="item">
      1
    </div>
    <div class="item">
      2
    </div>
  </div>
</div>

<div class="demo">
  <div class="header">
    <div class="item">
      1
    </div>
    <div class="item">
      2
    </div>
    <div class="item">
      3
    </div>
    <div class="item">
      4
    </div>
    <div class="item">
      5
    </div>
  </div>
</div>
Denaedenarius answered 12/8, 2021 at 2:37 Comment(0)
I
5

None of these answers solved my problem, which was that the items weren't the same width in my makeshift flexbox table when it was shrunk to a width too small.

The solution for me was simply to put overflow: hidden; on the flex-grow: 1; cells.

Instrument answered 13/8, 2020 at 19:48 Comment(1)
Or alternatively, you could use @Josh_Crozier's answer and conditionally set flex-wrap: wrap; when the browser window is below a certain width. This will cause the items to wrap and take up 100% of the parent width.Aceae
M
4

On the child element of flex,

flex: 1 1 25%

this will allow to have four items.

If you want to add more items then you can decrease the %.

Marshallmarshallese answered 20/5, 2022 at 9:24 Comment(1)
This worked for me adding a max-width value for items that content was also a flex container and its size was not big enough.Glottalized
O
3

It can be very difficult to reliably distribute columns evenly with flexbox for complicated reasons involving padding, border and margin which this article explains in detail.

One alternative suggested there is to instead use a grid with these styles:

.grid-container {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
}
Ortolan answered 19/9, 2023 at 1:43 Comment(0)
P
1

This will work even if you wrapping items, like a Grid, but not so simple you should show where it will wrap in media queries)).

Example:

.flex-item {
    flex: 0 0 calc(25% - (45px / 4))
}

It works like this:

$n: 4; // Number of columns
$gap: 15px; // Margin pixels

.flex-parent {
    display: flex;
    gap: $gap;
}
.flex-item {
    flex: 0 0 calc(100% / $n - (($n - 1) * $gap / $n));
}
Pedestal answered 22/1, 2022 at 19:18 Comment(1)
4 cols) flex: 0 0 calc(25% - (30px / 4 ) ); 3 cols) flex: 0 0 calc(33.3% - (20px / 3) ); 2 cols) flex: 0 0 calc(50% - (10px / 2) );Pedestal
I
0

I was having a similar issue and found a way to cheat.

As others have said, flex-basis and flex-grow: 1 are the way to keep items the same size, but the last row is an exception when there are too few items (they fill all available space, making them larger than the other items). To stop this from happening, I added an empty spacer item with visibility: hidden and set the flex-grow value inline based on a quick calculation of how many items there were.

This is easier if you know the width of the parent container but even if you don't, you can set the flex-grow value on the spacer using media queries and breakpoints.

Inmesh answered 14/9, 2022 at 3:9 Comment(0)
L
-1
.container{
display:flex;
flex-wrap:wrap;
}

.item{
flex basis:(add required width px)
}

If the last row has just one element and it is taking up all the width, then remove 'flex-grow' from flex item and try adding just 'flex-basis:(req px)' or 'flex-shrink:1'. It worked for me!

Louiselouisette answered 2/8, 2023 at 10:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.