Two properties seem to do exactly the same thing at first glance: hiding an element. Yet visibility: hidden and display: none produce very different results on the rest of the page, and mixing the two up creates layout bugs that are hard to spot, since nothing flags the mistake in the console.
The choice between the two comes down to a single question: should the rest of the page react to the element disappearing, or should its spot stay intact, like a ghost that is invisible but still there?
Definition
visibility: hidden makes an element completely invisible on screen, without ever removing the space it occupies in the layout. Its neighbours do not move a single pixel, exactly as if the element were still there, simply made transparent to sight and to the mouse.
Comparison with display: none
| Property | Space reserved | Reacts to hover and click |
|---|---|---|
visibility: visible (default) | Yes | Yes |
visibility: hidden | Yes | No |
display: none | No | No |
A child of an element set to visibility: hidden can become visible again by carrying its own visibility: visible, something a child of an element set to display: none can never do: once an ancestor disappears entirely from the layout, everything it contains disappears with it, with no possible exception.
Concrete example: a badge that keeps its space before appearing
.notification-badge {
visibility: hidden;
}
.notification-badge.active {
visibility: visible;
}On a bell icon carrying a notification badge, this choice avoids a small visual shift: the badge's space stays reserved at all times, so its appearance never pushes the bell or its neighbours by a pixel or two the moment the badge becomes visible. With display: none, that same change would have caused a tiny layout jump, especially noticeable on a dense interface where several icons sit side by side.
Use case: why choose visibility over display
visibility can be transitioned, unlike display, which makes it valuable for an opacity fade: combining opacity: 0 with visibility: hidden truly hides an element from the mouse and keyboard once the fade finishes, while keeping the animation smooth all the way through.
A third value reserved for tables: collapse
visibility accepts a third value, collapse, which behaves like hidden on most elements but takes on a special meaning on a table row or column: the row disappears entirely, and the remaining rows shift up to fill the gap, as if the row had been removed from the HTML. This is a very narrow case, useful for dynamically hiding a table row without recalculating the whole structure in JavaScript, but rarely encountered outside that precise context.
Frequently asked questions
Is visibility: hidden read by a screen reader?
No, an element set to visibility: hidden is ignored by assistive technology just like an element set to display: none. The only difference between the two concerns the visual layout, never accessibility: neither value is right for hiding an element visually while keeping it readable another way.
Can a visibility: hidden element be clicked?
No. Even though its space stays reserved in the layout, the element no longer intercepts any mouse or keyboard event: it cannot be hovered, clicked, or focused as long as its visibility stays set to hidden.
When should display: none be preferred over visibility: hidden?
The moment the empty space left behind would itself be a problem, for example a whole menu that should only appear on click, without leaving a large blank area on the page permanently. In that case, removing the element from the layout entirely with display: none is the right choice, even at the cost of losing the ability to transition its appearance smoothly.