In real life, we often need to make a flexible item that scales to a certain width with its content. If we had to define the width of the flex item manually, flexbox would not be such a holy grail that it is. It's possible to manipulate the flex items' width and flexibility just by setting their flex-grow or flex-shrink properties.
Flex-basis
First of all, let's discuss flex-basis. This property defines the initial size of the item along the main axis.
Here's an example:
<div class="flex-container">
<div class="flex-item item1">50px</div>
<div class="flex-item item2">100px</div>
<div class="flex-item item3">50px</div>
<div class="flex-item item4">70px</div>
</div>.flex-container {
border: 1px solid black;
display: flex;
width: 400px;
}
.flex-item {
border: 1px solid black;
text-align: center;
margin: 5px;
}
.item1 {
flex-basis: 50px;
}
.item2 {
flex-basis: 100px;
}
.item3 {
flex-basis: 50px;
}
.item4 {
flex-basis: 70px;
}There are four items inside the container with widths given them by flex-basis. Note there is some space left inside the container because the sum of items' lengths is less than 400px even with items' margins, and no correcting properties have been applied.
Flex-grow
What if we want items to occupy all available space inside the container? The flex-grow property defines how to increase the width of an item: in other words, it defines how much of the remaining free space the item takes.
If the size of the parent container is more than the width of all the items combined, the remaining space will be distributed among them in proportion to their flex-grow factor. For example, if for all the elements the property is flex-grow: 1, they will all have the same size because each will take the same part of the free space. However, if one of them has 2 as a flex-grow value, it will take twice as much free space as the others. The default value of flex-grow is 0.
As an example, let's take a container with four initially identical blocks:
<div class="flex-container">
<div class="flex-item item1">item1</div>
<div class="flex-item item2">item2</div>
<div class="flex-item item3">item3</div>
<div class="flex-item item4">item4</div>
</div>.flex-container {
border: 1px solid black;
display: flex;
width: 400px;
}
.flex-item {
border: 1px solid black;
text-align: center;
margin: 5px;
flex-basis: 50px;
}The result is:
Then add flex-grow: 1 to all items:
.flex-container {
border: 1px solid black;
display: flex;
width: 400px;
}
.flex-item {
border: 1px solid black;
text-align: center;
margin: 5px;
flex-basis: 50px;
flex-grow: 1;
}Now items share free space between themselves equally:
And, to see the difference, let's add another value of flex-grow to item2:
.flex-container {
border: 1px solid black;
display: flex;
width: 400px;
}
.flex-item {
border: 1px solid black;
text-align: center;
margin: 5px;
flex-basis: 50px;
flex-grow: 1;
}
.item2 {
flex-grow: 2;
}The final result is:
This time the free space is distributed among the items not equally. The second item is bigger than the others because its flex-grow value is more than that of the others.
The remaining space is divided by the sum of the values, which in this case is 1 + 2 + 1 + 1 = 5. The result of the division is the size of one part. So items 1, 3 and 4 will get one part because their flex-grow property value is 1, and the second item with flex-grow: 2 will get two parts.
Flex-shrink
Now consider the case when the width of all the items combined is more than the size of their parent. Here you can use flex-shrink, if it is possible to sacrifice some width of items. flex-shrink and flex-grow work in pretty similar ways dividing the value "width of the container minus the width of all the items" between items in some proportion. Since in the considered case the width of the container is smaller than the width of all the items, we get the negative value.
There is a special formula for calculating how much an element will shrink according to its flex-shrink value. Let's say the container has width: 400px, and the four elements have widths of 100, 200, 100, and 150 px respectively.
<div class="flex-container">
<div class="flex-item item1">100px</div>
<div class="flex-item item2">200px</div>
<div class="flex-item item3">100px</div>
<div class="flex-item item4">150px</div>
</div>.flex-container {
border: 1px solid black;
display: flex;
width: 400px;
}
.flex-item {
border: 1px solid black;
text-align: center;
margin: 5px;
}
.item1 {
flex-basis: 100px;
}
.item2 {
flex-basis: 200px;
}
.item3 {
flex-basis: 100px;
}
.item4 {
flex-basis: 150px;
}The result is:
Although the first three items combined are 400px, and the container too, item 3 is partially out of the container because some space inside it has already been taken by the items' margins.
Now let's also give items flex-shrink of 1, 2, 3, and 4:
.flex-container {
border: 1px solid black;
display: flex;
width: 400px;
}
.flex-item {
border: 1px solid black;
text-align: center;
margin: 5px;
}
.item1 {
flex-shrink: 1;
flex-basis: 100px;
}
.item2 {
flex-shrink: 2;
flex-basis: 200px;
}
.item3 {
flex-shrink: 3;
flex-basis: 100px;
}
.item4 {
flex-shrink: 4;
flex-basis: 150px;
}To start calculation, let's find the value of space deficiency. Sum final widths of each item (flex-basis, margin-left and margin-right in our case) and reduce by the container width:
(100 + 5 + 5) + (200 + 5 + 5) + (100 + 5 + 5) + (150 + 5 + 5) – 400 = 190
Then calculate the sum of flex-shrink * final width of each item:
1 * (100 + 5 + 5) + 2 * (200 + 5 + 5) + 3 * (100 + 5 + 5) + 4 * (150 + 5 + 5) = 1500
And there is a formula for each item reducing: space deficiency * flex-shrink * final width of item / the last sum.
item1: 190 * 1 * (100 + 5 + 5) / 1500 = 13.9
item2: 190 * 2 * (200 + 5 + 5) / 1500 = 53.2
item3: 190 * 3 * (100 + 5 + 5) / 1500 = 41.8
item4: 190 * 4 * (150 + 5 + 5) / 1500 = 81
And finally calculate each item's width:
item1: 100 – 13.9 = 86.1
item2: 200 – 53.2 = 146.8
item3: 100 – 41.8 = 58.2
item4: 150 – 81 = 69
If you try the example and use developers tools in your browser, you will see that items' width values above appear with slight deviations. This is due to how the browser renders the page.
The bigger the value of the flex-shrink property, the bigger the reduction of the width.
If you have really read and understood the previous mathematical text, the author deeply admires you.
At first sight, it doesn't seem like both of the properties are used very often or used at all, but actually, there are a lot of examples of their usage! Even the main page of Google:
If those two buttons were items of a flex container (maybe they are), they would probably look weird without flex-grow:
The contents of any item, as well as, for example, the number of the items, may change, which will lead to overflow or, otherwise, to items filling the container without overflow. And that implies a change of the CSS properties or even of the whole structure. With flex-grow or flex-shrink, you don't have to worry about manually changing the width of the items.
Flex
There is also a shorthand property flex, which combines all the properties listed above. You should use flex-grow as the first value, flex-shrink as the second value, and eventually flex-basis as the third one.
If none of the properties are set, the default values will be applied. For flex-grow it's 0, for flex-shrink it's 1, and for flex-basis it's auto, which is the size of the item's contents. There are some keyword values:
flex: initial; = flex: 0 1 auto;
flex: auto; = flex: 1 1 auto;
flex: none; = flex: 0 0 auto;
Conclusion
flex-grow and flex-shrink are the properties which are responsible for the width of the items. The size of an item by default is defined by either the width of its content or by the flex-basis value. If there is remaining space, it will be divided between the items in proportion to their flex-grow or flex-shrink values.
It turns out that these properties are not easy to use, especially when it comes to math and negative values. But the key word here is proportion, and if you bear it in mind, you'll successfully use these properties every time.