What's the difference between visibility: hidden and visibility: collapse in flexbox?

Note on browser support: As of July 2017, Chrome (59) does not support visibility: collapse. The code samples below with collapse work in Firefox and Edge, but fail in Chrome (they behave just like hidden). UPDATE: As of July 2020, this is note is still valid. Chrome and Safari treat visibility: collapse like hidden. caniuse.com


Flex items are laid out in a row or column, depending on flex-direction.

Each row / column is considered a flex line.

In the examples below, a flex container has four flex items in row-direction. The fourth item wraps, creating a second flex line:

.container {
  display: flex;
  flex-wrap: wrap;
  width: 200px;
  border: 1px dashed black;
}
.box {
  height: 50px;
  flex: 0 0 50px;
  margin: 5px;
  background-color: lightgreen;
  display: flex;
  justify-content: center;
  align-items: center;
}
<div class="container">
  <div class="box box1">1</div>
  <div class="box box2">2</div>
  <div class="box box3">3</div>
  <div class="box box4">4</div>
</div>


display: none

With display: none, a flex item isn't rendered by the browser.

If all items on the flex line have display: none, the line also collapses, which affects the rest of the layout. Surrounding elements may shift when a flex line collapses.

With display: none applied to the third item, the fourth item takes its place on the upper line, and the lower line collapses:

.container {
  display: flex;
  flex-wrap: wrap;
  width: 200px;
  border: 1px dashed black;
}
.box {
  height: 50px;
  flex: 0 0 50px;
  margin: 5px;
  background-color: lightgreen;
  display: flex;
  justify-content: center;
  align-items: center;
}
.box3 { display: none; }
<code>display: none</code>
<div class="container">
  <div class="box box1">1</div>
  <div class="box box2">2</div>
  <div class="box box3">3</div>
  <div class="box box4">4</div>
</div>


visibility: hidden

With visibility: hidden, a flex item is rendered by the browser but is fully transparent. It's hidden from view but takes up the space it would normally use in the layout. Hence, surrounding elements see this item as fully intact.

In this example, when the last two boxes have visibility: hidden, the rest of the layout (including the second flex line) remains unchanged.

.container {
  display: flex;
  flex-wrap: wrap;
  width: 200px;
  border: 1px dashed black;
}
.box {
  height: 50px;
  flex: 0 0 50px;
  margin: 5px;
  background-color: lightgreen;
  display: flex;
  justify-content: center;
  align-items: center;
}
.box3 { visibility: hidden; }
.box4 { visibility: hidden; }
<code>visibility: hidden</code>
<div class="container">
  <div class="box box1">1</div>
  <div class="box box2">2</div>
  <div class="box box3">3</div>
  <div class="box box4">4</div>
</div>


visibility: collapse

With visibility: collapse, a flex item is not rendered (same as display: none), but the flex algorithm checks the cross size of the item and then uses that data to keep the flex line stable (i.e., what the cross size of the line would be if the flex item was visible).

The difference with display: none is that collapse allows one piece of the item – its cross size – to be preserved. This is referred to in the spec as the strut.

So if all flex items on the line have visibility: collapse, the cross size of the line (whether it be the width or height) does not collapse, and the rest of the layout isn't affected.

Note that although collapse guarantees the stability of the line's cross size, it provides no such assurance for the line's main size. This is a key difference between collapse and hidden.

Below are some examples. (As mentioned above, these won't work in Chrome. Test in FF or Edge.)

In this example, the first two items have visibility: collapse.

.container {
  display: flex;
  flex-wrap: wrap;
  width: 200px;
  border: 1px dashed black;
}
.box {
  height: 50px;
  flex: 0 0 50px;
  margin: 5px;
  background-color: lightgreen;
  display: flex;
  justify-content: center;
  align-items: center;
}
.box1, .box2 {
  visibility: collapse;
}
<code>visibility: collapse</code>
<div class="container">
  <div class="box box1">1</div>
  <div class="box box2">2</div>
  <div class="box box3">3</div>
  <div class="box box4">4</div>
</div>

The layout renders like display: none. The second line collapses because the main size of the items is gone, allowing the last item to move up naturally.

In the following example, all items get visibility: collapse. Hence, the second line collapses because the items' main size is gone, but the cross size of the first line remains.

.container {
  display: flex;
  flex-wrap: wrap;
  width: 200px;
  border: 1px dashed black;
}
.box {
  height: 50px;
  flex: 0 0 50px;
  margin: 5px;
  background-color: lightgreen;
  display: flex;
  justify-content: center;
  align-items: center;
}
.box {
  visibility: collapse;
}
<code>visibility: collapse</code>
<div class="container">
  <div class="box box1">1</div>
  <div class="box box2">2</div>
  <div class="box box3">3</div>
  <div class="box box4">4</div>
</div>

jsFiddle