How to make a sticky header in Shopify?
One of my clients needed a sticky header for his shop. The sticky header must contain the navigation as well. I usually use Headroom.js for this type of task because not only does it stick the header to the top of the page when needed but also removes it when it is not needed.
First step I started searching the web for a solution on how to integrate the script on Shopify.
Couldn’t find anything so after I’ve made the integration I’ve decided to write a tutorial to help those of you who are in need of a solution.
What is Headroom.js?
Headroom.js is a small JavaScript script that lets you add some cool effects to your website header. The script is lightweight, it has only 1.38KB for production meaning the code is compressed. Uncompressed, the script has 2.78KB.
What is Headroom.js good for?
As a great user experience it is recommended that you always keep the website navigation in the user’s view. Why? Because if you make the user think too much when navigating your online shop you may lose him.
If he starts asking questions like: “Hmm, how do I get back to the home page right now?” or “How can I see more info about the company behind the shop” or “Where is the link to the contact page to ask about the stock of this product?” and so on, it is recommended that your website offers him instant answers via the navigation menu or he will hit the browser back button and you lose him, possibly forever.
Lose the user and you could lose one sale now and probably many more in the future from the same client.
Please keep in mind that the majority of users are “lazy” and they do not like or want to put too much effort when visiting your website and why should they have too? In this time and age everything needs to be easy to use and fast, nobody has time to wait because time is scarce.
To always display the menu (user navigation) to the user you need to make the website header fixed to the top of the screen.
Now when he navigates the website he will always have the website menu displayed in his view and he can click anytime any link to be taken to the desired page or back to the home page.
OK. Problem solved!
Do you think so? Not really.
Making the menu fixed means that your navigation will always be fixed to the top of the screen and it will cover a lot of information, sometimes critical for making a sale.
On small devices the issues are even bigger because the screen is even smaller. When you switch the device to landscape mode a significant part on the top of the screen will be covered making the UX a nightmare for your customers.
This is where Headroom.js comes into play since it’s allowing the display of the header only when is needed based on user gestures.
By displaying the header only when it is needed, the user can better focus on the content and he has solely the decision when to display the header to navigate to other pages.
How does it work?
The script adds and removes CSS classes from an element in response to a scroll event (scrolling down or up) done by website visitors.
Headroom.js Integration
Let’s start adding the feature.
Please do not dive into modifying the code on your theme if you are not a developer or you do not have previous experience with this type of work, especially if you plan on using this on a production environment (live site). You can remove by mistake code that should not be deleted and this could result in a broken website.
My recommendation is to always hire an experienced developer to do this type of work for you. You don’t save money by not hiring a developer, on the contrary you’ll lose a lot more. On the other hand if you are learning Shopify development feel free to do it but do it first in a staging environment.
I’ll do the integration on the Debut theme which at this time is the default Shopify theme.
I assume that you know how to use a code editor, you know HTML, CSS, JavaScript and the theming Shopify language Liquid.
Connect to the theme
For this tutorial I’ll work via my code editor. To connect to a Shopify theme you need 2 things: Theme Kit and an API key.
Please install Theme Kit on your computer and generate an API key.
You can follow the documentation here on how to do that: https://shopify.dev/themes/tools/theme-kit/getting-started
I’ll write a tutorial about how to connect to a Shopify theme with Theme Kit in the future. For the moment this is out of the scope of this tutorial on how to make a sticky header for your Shopify store.
Please make a copy of your theme and work on that one and not on the live one.
OK. I assume you are already set up and connected to your shop theme. I also assume that the theme is at this point downloaded in a folder on your computer.
If everything looks good on your part, let’s start the integration.
Start the “theme watch” command
Before doing anything else please start the theme watch command from your terminal. This will watch for any modification you make to the theme directory and will upload the changes to Shopify servers.
Download the script
The script has a CDN version as well: https://unpkg.com/headroom.js . If you want you can deliver it from here.
I’ll not use that one since Shopify has its own CDN and it will deliver the files from there.
First we need to download the headroom.js script from here: https://wicky.nillia.ms/headroom.js/ .
Please download the production version. This one is already minified, meaning that it will weigh less than the development version.

After you click the button a new window will open displaying the script:

Copy the script to the clipboard.
Please note that I’ll be adding all scripts inside the “assets” folder for easier learning, but I recommend using a JavaScript task runner like Gulp or Grunt to concatenate the JavaScript files and serve only one file from the theme. This will reduce your browser requests making the website faster. On the other hand, if the scripts will be served only on certain pages then they should be in separate files.
Open the “assets” folder:

Create a file and give it the name “headroom.js”:

Open the file in the code editor:

Paste the code that you’ve copied inside and save the file:

Add the script to theme
Now we need to integrate the script into our “theme.liquid” file. You can find this file in the “layout” folder:

Open the file:

We have the following code:
<script src="{{ 'headroom.js' | asset_url }}" defer="defer"></script>
The code calls the script file “headroom.js” which is located in the assets folder.
asset_url – is a Shopify filter that returns the URL of a file from the “assets” folder located in your theme.
defer=”defer” – We use the “defer” attribute to execute the script when the page has finished parsing. We use this to help the page load faster.
Add the code above in the <head></head> tag:

Verify that the script is correctly added to your theme
Go to your store’s front-end. I’m using the Debut theme since it is Shopifyʼs default theme. My website front-end looks like this:

I’m using the Chrome browser. Open the “Developer Tools” by typing ⌥ + ⌘ + C on Mac and F12 or Ctrl + ⇧ + I on Windows/Linux:

Now type ⌘ + F on Mac or CTRL + F on Windows/Linux. A search box will display:

Type headroom.js inside the “Search” box:

Maybe you are wondering what is with the strange URL:
src="//cdn.shopify.com/s/files/1/0263/8623/5463/t/4/assets/headroom.js?v=2293252637246958684"
Well, Shopify serves the script from a CDN. The number “v=2293252637246958684” is the script version that Shopify cached on the CDN server.
OK. Now we are sure that we did a great job adding the script and Shopify loaded “Headroom.js” into the theme.
Initialise the script
Let’s create a new file called “headroom-init.js”. This will contain the script initialization. The file should be placed in the “assets” folder.

Add the following code to the file:
// Headroom initialization const header = document.querySelector( '.site-header' ); const headroom = new Headroom( header ); headroom.init();
Let me explain the code:
const header = document.querySelector( '.site-header' );
Here we grab an element and assign it to a constant called header. The element is the Debut theme header with the class “.site-header” as shown in the image below:

const headroom = new Headroom( header );
Here we construct an instance of Headroom, passing the element selected from the DOM.
headroom.init();
This will initialise the script.
Now this file will have no effect on our theme header since it is not linked into the theme. Let’s do that by opening the “theme.liquid” file located in the “layout” folder:

Add the following line:
<script src="{{ 'headroom-init.js' | asset_url }}" defer="defer"></script>

You can verify if the script was included by repeating the same process as we did early but it will no longer be necessary since now if you did everything correctly you should see on the header element the new classes added by Headroom.js.
This is how the “header” element looked before:

And this is how it should look once the “headroom.js” script is active:

As you noticed there are new classes added to the “header” element like:
"headroom headroom--not-bottom headroom--not-top headroom--unpinned"
If you see all these new classes it means that you’ve done the integration correctly until now and that the script is working perfectly, from a developer point of view. From a user point of view the script does nothing at this point.
Add the styles
If you scroll up and down the page you’ll see that nothing happens except that new classes are added and removed from the header element in the “Developer Tools” panel.
In conclusion for the moment the header element doesn’t do anything from a website user point of view.
This is why the next step is to add the styles so we can make the effects visible on the page interaction.
I’ll add all the styles to “theme.scss.liquid” for ease of learning. I recommend having all source SASS files separated and compiling them into a single file with a task runner like Gulp or Grunt since it’s much easier to maintain them in the long run.
We need to add the following code:
// Headroom .headroom { transition: transform 200ms linear; } .headroom--pinned { transform: translateY(0%); } .headroom--unpinned { transform: translateY(-100%); } .headroom--not-top { position: fixed; z-index: 1000; }
I’ll explain the code so you understand what you are doing:
.headroom { transition: transform 1s ease-in-out; }
We use the “transition” shorthand property to create a smooth transition effect for our shop header element.
This property will help the header element to change the given values for the “transform” property over a specified amount of time which in our case is “1s”, animating the property changes, rather than having them occur immediately. The “ease-in-out” is the effect applied to the element during the transition.
I say the slower the transition the better. A fast transition could annoy users.
You can adjust all the properties as you please and as you need to match your project.
.headroom--pinned { transform: translateY(0%); } .headroom--unpinned { transform: translateY(-100%); }
In the code above we modify the position of the element on a vertical axis. When the header has the class “.headroom–pinned” and is at position 0, meaning it is showing on the page.
Once we scroll on the page the class “.headroom–unpinned” is added which means that we’ll hide the header with “translateY(-100%)”.
.headroom--not-top { position: fixed; z-index: 1000; }
This class is added by the Headroom.js script when the header element is not at the top of the page. We give a “position: fixed;” so it can stay fixed to the top of the page when scrolling the page. We also give a “z-index: 1000;” to the element so it can stick above all other elements on the page in any website page.
Please note that you may have elements with a higher index than 1000 that will create conflicts and the header will not display correctly. To fix this issue check the index for the other element and increase the z-index for the header element as needed.
Add the code to “theme.scss.liquid” file:

That’s it. If you did everything correctly your header should become sticky.
Headroom.js options
Headroom.js also has some useful options if you want to use them. In many cases for sure you need them.
You can set options parameters for: offset, tolerance and classes.
To add options please open the “headroom-init.js” file and add the following code:
const options = { }
And modify the headroom instance like this by adding the “options”:
const headroom = new Headroom( header, options );
You should have something like this in you code editor:

Offset
This will let you control the vertical offset in pixels before the element is first unpinned and fixed to the top of the page as you scroll.
For example if you have an offset of 100px the targeted element (header element in our case) will be unpinned (removed) from its initial position and pinned (fixed) to the top of the page after you scroll 100 pixels.
Tolerance
This will let you control the scroll tolerance in pixels before the state changes. Tolerance can be set for both up/scroll events or individually with different values.
Depending on the project, I usually use an offset of 10 pixels and a tolerance of 15. In this way if the user scrolls slowly the header will be kept hidden from view and if he scrolls fast the header will be displayed.
Please test and use what works for you.
With this 2 values this is how my code will look:

If you want to specify the tolerance separate for up and down scroll events you need to add the following code:
const options = { 'offset': 10, 'tolerance' : { up : 5, down : 0 }, };
This is how the code will look in the code editor:

Classes
Another option lets you change the classes that the script adds to the targeted element, in our case the “header”.
If you do not like the name given to the CSS classes that are added to your target element, or maybe you have similar ones and you try to avoid conflicts, you can change them to whatever you wish.
If you look at the source code in the “headroom.js” file these are the default CSS classes that the script adds to your selected element:
classes: { frozen: "headroom--frozen", pinned: "headroom--pinned", unpinned: "headroom--unpinned", top: "headroom--top", notTop: "headroom--not-top", bottom: "headroom--bottom", notBottom: "headroom--not-bottom", initial: "headroom", },
I’ll explain the code:
“initial” – happens when the element is initialised.
“pinned” – when scrolling up.
“unpinned” – when scrolling down.
“top” – when above offset.
“notTop” – when below offset.
“bottom” – when at the bottom of the scroll area.
“notBottom” – when not at the bottom of the scroll area.
“frozen” – when the frozen method has been called.
To change them just add the desired one in the headroom-init.js file.
I’ll change the prefix “headroom” to “sticky”. To do that I need to add the following code:
'classes' : { initial : 'sticky', pinned : 'sticky--pinned', unpinned : 'sticky--unpinned', top : 'sticky--top', notTop : 'sticky--not-top', bottom : 'sticky--bottom', notBottom : 'sticky--not-bottom', frozen: 'sticky--frozen', pinned: 'sticky--pinned foo bar' },
This is how it looks inside the code editor:

You can even add multiple CSS classes if you want. You just need to add a space between the classes. I’ll add 3 CSS classes to the element when it is pinned. The classes that I’ll add are “sticky–pinned”, “foo” and ”bar”. All classes will look like this:
'classes' : { pinned: 'sticky--pinned foo bar' },
In the code editor they will look like:

This is how multiple classes look on the front-end:

Conclusion
I hope this helps you on your projects. I consider this integration a better way to obtain a sticky header than adding an app. This little script is clean, it works directly with the theme and it doesn’t add a lot of weight to the page loading time.
If you have trouble integrating this script on your shop or if you want to integrate it on a different Shopify free theme, premium theme, a custom theme or maybe you also want to add to it theme settings in the theme panel to give you more control, please send an email via my contact page.