React Sticky Box

See it in Action

Sidebar

The sidebar to the left stays in a sticky position.

Go ahead and hit the expand button to see how it behaves once the sidebar becomes greater than the viewport.

What the Code looks like

import React from "react";
import StickyBox from "react-sticky-box";

const Page = () => (
  <div className="row">
    <StickyBox offsetTop={20} offsetBottom={20}>
      <div>Sidebar</div>
    </StickyBox>
    <div>Content</div>
  </div>
);

How to install

yarn add react-sticky-box
npm install react-sticky-box

How it works

React Sticky Box relies on position: sticky. It’s widely supported these days.

For all browsers without sticky support, it falls back to a position: relative behaviour. This could be considered “good enough” in a lot of cases. Alternatively you have to provide a custom solution to get sticky behaviour here. Either via polyfills or even targeting IE10+ Browsers via @media queries like presented here by Olivr3.

position: sticky isn’t without it’s issues though. If the content is bigger than the viewport, it simply gets cut off and becomes inaccessible. This library solves this issue by allowing the content to scroll along with all the other content until the the corresponding end is visible and continues to be sticky.

You still might encounter browser bugs with position: sticky if your content lies within certain combinations of z-index, overflow and especially transform.

What kind of scroll containers can be used

Window

<body>
  <div style={{display: "flex", alignItems: "flex-start"}}>
    <StickyBox>Sidebar</StickyBox>
    <div>Main Content</div>
  </div>
</body>

Scroll Container as offset parent

<div style={{height: 200, position: "relative", overflow: "auto"}}>
  <div style={{position: "absolute", top: 0, left: 0, right: 0, minHeight: "100%"}}>
    <div style={{display: "flex", alignItems: "flex-start"}}>
      <StickyBox>Sidebar</StickyBox>
      <div>Main Content</div>
    </div>
  </div>
</div>

Scroll Container as non-offset parent

<div style={{height: 200, overflow: "auto"}}>
  <div style={{display: "flex", alignItems: "flex-start"}}>
    <StickyBox>Sidebar</StickyBox>
    <div>Main Content</div>
  </div>
</div>

Troubleshooting

The StickyBox is not sticky!

Make sure that the <StickyBox> is only as high as its content, and not as high as the parent node. To quickly check this you can do:

<StickyBox style={{border: "3px solid green"}}>content</StickyBox>

Good

Sidebar
Main Content
<div style={{display: "flex", alignItems: "flex-start"}}>
  <StickyBox style={{border: "3px solid green"}}>Sidebar</StickyBox>
  <div style={{height: 150, border: "3px solid blue"}}>Main Content</div>
</div>

Bad

This time without alignItems: "flex-start"

Sidebar
Main Content
<div style={{display: "flex"}}>
  <StickyBox style={{border: "3px solid green"}}>Sidebar</StickyBox>
  <div style={{height: 150, border: "3px solid blue"}}>Main Content</div>
</div>

Is this production-ready?

This library is heavily used within Codecks, so expect it to be fairly stable.