254

I was stuck on this for a little bit and thought I'd share this position: sticky + flexbox gotcha:

My sticky div was working fine until I switched my view to a flex box container, and suddenly the div wasn't sticky when it was wrapped in a flexbox parent.

.flexbox-wrapper {
  display: flex;
  height: 600px;
}
.regular {
  background-color: blue;
}
.sticky {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  background-color: red;
}
<div class="flexbox-wrapper">
  <div class="regular">
    This is the regular box
  </div>
  <div class="sticky">
    This is the sticky box
  </div>
</div>

JSFiddle showing the problem

TylerH
  • 20,799
  • 66
  • 75
  • 101
bholtbholt
  • 11,281
  • 6
  • 22
  • 32

9 Answers9

487

Since flex box elements default to stretch, all the elements are the same height, which can't be scrolled against.

Adding align-self: flex-start to the sticky element set the height to auto, which allowed scrolling, and fixed it.

Currently this is supported in all major browsers, but Safari is still behind a -webkit- prefix, and other browsers except for Firefox have some issues with position: sticky tables.

.flexbox-wrapper {
  display: flex;
  overflow: auto;
  height: 200px;          /* Not necessary -- for example only */
}
.regular {
  background-color: blue; /* Not necessary -- for example only */
  height: 600px;          /* Not necessary -- for example only */
}
.sticky {
  position: -webkit-sticky; /* for Safari */
  position: sticky;
  top: 0;
  align-self: flex-start; /* <-- this is the fix */
  background-color: red;  /* Not necessary -- for example only */
}
<div class="flexbox-wrapper">
  <div class="regular">
    This is the regular box
  </div>
  <div class="sticky">
    This is the sticky box
  </div>
</div>

JSFiddle showing the solution

TylerH
  • 20,799
  • 66
  • 75
  • 101
bholtbholt
  • 11,281
  • 6
  • 22
  • 32
  • 55
    this only works when scrolling within the containing flex element -- when scrolling the entire window it does NOT stick (at least in Firefox) -- so for those who are experiencing contradictory behavior it is more likely you're experiencing contradictory expectations (like i was) – aequalsb Sep 28 '18 at 16:31
  • @aequalsb Do you know of a way to get it to stick when scrolling the whole page? – Douglas Gaskell Mar 19 '19 at 20:17
  • @Douglas the question is misleading because it suggests the problem is with `flexbox` but the problem has more to do with containing the `sticky` element. look at this fiddle: https://jsfiddle.net/9y87zL5c/ where the SECOND red box works as expected but the FIRST red box not stick -- so... this is not related to flexbox (thus contradictory expectations)... ALSO... i added a bunch of jabberwocky to see results in a more accurate manner. – aequalsb Mar 19 '19 at 21:04
  • 2
    I have no problem using it in Firefox 87. Works Correctly. – Shahriar Mar 03 '21 at 12:16
  • But when used `align-self: flex-start`, for example, `top: 100px;` is not working. How can I do that? – Siri May 25 '22 at 16:17
  • this worked a lot to me – Jenuel Ganawed Aug 05 '22 at 12:24
  • 1
    @Ms. Siri, the answer works in Edge using both `top: 0` and `top: 100px`. My Flexbox test does not use either `overflow` or `height`. You absolutely need `top` and `align-self` as the answer shows. – Goal Man Aug 23 '22 at 03:26
81

In my case, one of the parent containers had overflow-x: hidden; applied to it, which will break position: sticky functionality. You'll need to remove that rule.

No parent element should have the above CSS rule applied to it. This condition applies to all parents up to (but not including) the 'body' element.

TheoPlatica
  • 1,060
  • 7
  • 11
33

If you are using flex in the parent element use align-self: flex-start for the element which you want to make sticky.

position: sticky;
align-self: flex-start;
top: 0;
overflow-y: auto;
MD SHAYON
  • 7,001
  • 45
  • 38
  • But when used `align-self: flex-start`, for example, `top: 100px;` is not working. How can I do that? – Siri May 25 '22 at 16:16
16

You can also try adding a child div to the flex item with the contents inside and assign position: sticky; top: 0; to that.

That worked for me for a two column layout where the contents of the first column needed to be sticky and the second column appear scrollable.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • Totally agree, this is the most simple and reliable option I could find. This way you will also be able to control when to stop sticking by controlling the height of the container. – Ibrahim ben Salah Jan 07 '20 at 19:24
8

For my situation, the align-self: flex-start (or justify-self: flex-start) solution does not work. I need to keep overflow-x: hidden as well since some containers swipe horizontally.

My solution required nested display: flex with overflow-y: auto to get the desired behaviors:

  • header can adjust height dynamically, which prevents playing with position: absolute or position: fixed
  • content scrolls vertically, constrained horizontally to the view width
  • sticky element can be anywhere vertically, sticking to the bottom of the header
  • other elements can slide horizontally
    • looks like the SO snippet tool can't render width on child elements to properly to demonstrate the horizontal slide, or maybe there's some other setting on my actual layout that makes it work...
    • note that a wrapper element that does nothing else is required to allow overflow-x: auto to work correctly in elements under a parent with overflow-x: hidden

body {
  height: 100vh;
  width: 100vw;
  display: flex;
  flex-direction: column;
}

body>header {
  background-color: red;
  color: white;
  padding: 1em;
}

.content {
  overflow-x: hidden;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

article {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.horizontal_slide {
  display: flex;
  overflow-x: auto;
  background-color: lightblue;
  padding: .5em;
}

.horizontal_slide>* {
  width: 1000px;
}

.toolbar {
  position: sticky;
  top: 0;
  z-index: 10;
  background-color: lightgray;
  padding: .5em;
  display: flex;
}
<header>Fancy header with height adjusting to variable content</header>
<div class="content">
  <article class="card">
    <h1>One of several cards that flips visibility</h1>
    <div class="overflow_x_wrapper">
      <div class="horizontal_slide">
        <div>Reason why `overflow-x: hidden` on the parent is required
        </div>
        <div>Reason why `overflow-x: hidden` on the parent is required
        </div>
        <div>Reason why `overflow-x: hidden` on the parent is required
        </div>
      </div>
      <div class="toolbar">Sticky toolbar part-way down the content</div>
      <p>Rest of vertically scrollable container with variable number of child containers and other elements</p>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
        dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  </article>
  </div>
OXiGEN
  • 2,041
  • 25
  • 19
8

Make sure flex-wrapper has assigned a height, and set the overflow value to auto. Then add "align-self: flex-start;" to the sticky element.

.flexbox-wrapper {
  display: flex;
  height: 600px;  //<- nessary,and don't assign with %
  overflow:auto;  //<-fix
}
.regular {
  background-color: blue;
}
.sticky {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  background-color: red;
  align-self: flex-start; // <-fix
}
<div class="flexbox-wrapper">
  <div class="regular">
    This is the regular box
  </div>
  <div class="sticky">
    This is the sticky box
  </div>
</div>
Qiluo
  • 97
  • 1
  • 2
0

I made a makeshift flexbox table and had this problem. To solve it, I simply put the sticky header row outside of the flexbox, just before it.

Andrew
  • 5,839
  • 1
  • 51
  • 72
0

Global solution/rule from my experience:

Do not put structures with sticky elements directly inside { display : flex } containers.

Instead (for flex layouts) put your tables/divs with sticky elements inside a flex sub-container (e.g. with { flex: 1 1 auto }, but without { display : flex } ), then everything should work as intended.

Ciabaros
  • 1,984
  • 13
  • 15
0

This worked for me but i have no idea if it's the issue you have. And my issue was that the centered content in flexbox was hidden when the viewport was too small

/*this is the wrapper of centered content*/
.section {
    overflow-y: auto;
    max-height: none;
    height: 100%;
    min-height: 100vh; /*optional, can be changed to anything*/
}
<div class="section">
   <div class="content">
      Some long content that is centered....
   </div>
</div>
MiftikCZ
  • 21
  • 4