dereck quock

Using Sticky Positioning

2 min read

Creating a sticky experience

🛒 I'm on the Checkout web team at PayPal and we're working on the next generation of our checkout experience. The review page can be fairly content-heavy if the customer is eligible for the many features that we display on our single-page app.

This is how a checkout review page looks for my account:

Checkout review page

As you can see, there's a lot on the page. And this isn't even including other features like currency conversion, applying for PayPal Credit, or if I have some PayPal balance and want to apply that to the transaction.

The Continue button to continue checking out is all the way at the bottom. And if the customer is checking out in a smaller viewport on their phone, they'd have to scroll all the way down to continue. Adding any friction during a checkout experience is asking for lower conversion. Enter position: sticky.


Not only is sticky positioning great for headings in a list, but it's the perfect use case for our Continue button.

In aliqua eiusmod commodo consequat ipsum minim Lorem dolore culpa sint do est Lorem. Est in officia esse pariatur consectetur enim laboris eu ipsum ex. Laboris consequat eiusmod ex sunt labore in proident. Velit et in deserunt sint reprehenderit id. Duis culpa do non dolore mollit consectetur consectetur tempor non anim minim occaecat. Ad magna excepteur dolore dolor tempor non duis voluptate ad incididunt nostrud amet cupidatat. Proident quis ipsum culpa qui officia mollit reprehenderit mollit eu cupidatat ad. Exercitation consectetur esse id voluptate aliquip duis excepteur cupidatat officia sint elit ut. Proident exercitation cillum anim pariatur labore qui reprehenderit ut proident. Laboris deserunt ex amet minim voluptate tempor incididunt aute veniam aliqua nisi fugiat sit sit. Est cillum exercitation commodo anim non non mollit excepteur. Do nulla occaecat aliqua velit ea sit laboris elit tempor proident. Amet mollit exercitation aliqua adipisicing. Incididunt aute tempor reprehenderit dolore ex labore est esse irure. Elit in aliqua magna sunt pariatur quis incididunt ad adipisicing proident. Fugiat ea incididunt ipsum in incididunt magna adipisicing excepteur aliqua reprehenderit veniam dolor proident. Reprehenderit Lorem aliqua laborum cupidatat pariatur non nulla elit do nostrud culpa ex. Minim consequat sit commodo reprehenderit enim labore id dolor ad consectetur. Ullamco consequat non eu excepteur magna ut consectetur reprehenderit magna dolore pariatur voluptate. Excepteur veniam dolor ex tempor nostrud. Velit adipisicing aliquip qui veniam ex duis officia nostrud. Aute Lorem ea in ea. Ad irure minim sit mollit tempor minim dolore aute ea est veniam velit. Quis proident consectetur reprehenderit do fugiat ut adipisicing. Ullamco aliqua amet elit sit in cillum ad. Commodo officia non id excepteur consectetur anim quis in cupidatat minim. Sint occaecat minim labore magna officia mollit. Ex irure eu incididunt do nostrud cillum ut sit ut ipsum. Ullamco id ut esse adipisicing. Est duis ut ea ipsum laboris tempor laboris. Qui non anim anim aliqua reprehenderit aute sit dolore commodo eu proident exercitation et in. Anim officia velit laborum est amet consequat cillum culpa sunt labore amet anim anim. Nisi magna ut ea dolore proident aliqua.

Laborum aute in nisi consequat mollit qui sit ad culpa laboris aliquip officia aute ea. Nisi fugiat reprehenderit nostrud magna minim velit do officia occaecat deserunt enim adipisicing sunt. Irure aliqua minim ullamco voluptate commodo enim tempor officia occaecat Lorem voluptate ipsum magna irure. Cupidatat do eiusmod eiusmod pariatur sunt tempor esse est ut. Laborum anim aliqua deserunt ullamco enim cillum elit. Exercitation ut consectetur in laboris officia quis do.

Edit this on CodeSandbox

Notice how the button stays stuck to the bottom until more content under it pushes is up 🔥 and all we had to do was add some CSS:

.sticky {
position: sticky;
bottom: 0;

This will be ignored in browsers that don't have support (💩 IE) and will behave like position: relative

Now the customer doesn't necessarily need to scroll all the way down if they want to check out quickly. By making our Continue button easily accessible, our users that don't need to change their default shipping options or funding option were able to check out without any friction. This is the sticky experience that we're looking for! 🕸

Let's explore other uses for sticky positioning

Sticky headings

The example from the MDN docs shows the common use for sticky headings of a list. This is a pattern that's used for lists in native apps, and this is easy to do in web apps as well.

Spider-Man Characters 🕷

Aunt May
Black Cat
Charles Standish
Doctor Octopus
Harry Osborn
Isaac Delaney
J. Jonah Jameson
Jefferson Davis
Mary Jane Watson
Miles Morales
Mister Negative
Morgan Michaels
Norman Osborn
Peter Parker
Rio Morales
Short Order Cook
Silver Sable
Uncle Ben
Walter Hardy
Yuriko Watanabe

Edit this on CodeSandbox

Just as we set bottom: 0 to make our Continue button stick to the bottom, setting top: 0 on a sticky positioned heading causes it to stick to the top.

Here we have some styled components:

const Dt = styled.dt`
padding: 6px 16px;
position: sticky;
top: 0;
background: #666666;
color: white;
/* don't have a border for the first item in the section */
+ dd {
border-top: 0;
const Dd = styled.dd`
margin: 0 0 0 16px;
padding: 10px 0;
border-top: 1px solid #ccc;

and the corresponding markup:

<Dd>Aunt May</Dd>
<Dd>Black Cat</Dd>

If you want to explore more of how to create awesome sticky headers, Eric Bidelman has an awesome post about using an IntersectionObserver to observe when sticky positioned elements become stuck.

Sticky header rows and columns of a table

Suppose you want to show a table of information, such as a schedule. You could have the table headers be stuck, but also have a sticky column with the times of the day.

8:00 AMWake upWake upWake upSleep inSleep in
8:30 AMBrush teethBrush teethBrush teethSleep in moreWake up
9:00 AMEat breakfastEat breakfastReadSleep in moreBrush teeth
9:30 AMGet coffeeGet coffeeEat breakfastWake upEat breakfast
10:00 AMCodeCodeCodeEat breakfastGet coffee
10:30 AMCodeCodeCodeCodeChill

Edit this on CodeSandbox

By setting top: 0 for the column headers and left: 0, we can get the table headers to stick correctly.

To make the table headers stick, we just have to do something like this:

const TableHeader =`
padding: 20px;
position: sticky;
background: #666666;
color: white;
text-align: center;
&[scope='col'] {
top: 0;
&[scope='row'] {
left: 0;

with a table defined like this:

<TableHeader scope="row"></TableHeader>
<TableHeader scope="col">Wednesday</TableHeader>
<TableHeader scope="col">Thursday</TableHeader>
<TableHeader scope="col">Friday</TableHeader>
<TableHeader scope="col">Saturday</TableHeader>
<TableHeader scope="col">Sunday</TableHeader>
<TableHeader scope="row">8:00 AM</TableHeader>
<TableCell>Wake up</TableCell>
<TableCell>Wake up</TableCell>
<TableCell>Wake up</TableCell>

Now get out there and create some sticky experiences!