Mastering Scrolling in Framer X Mastering Scrolling in Framer X
Parallax, dynamic, or sticky headers. Learn how to prototype commonly used scroll interactions in Framer X.
May 23, 2019
In this article we’ll look at the different ways we can use Framer’s scroll component to create the following:
- Parallax scroll
- Scroll gestures
- Dynamic app bars
- Sticky headers
Along the way, you’ll learn more about how React components work in Framer X. The code examples in this article rely on the new Framer Library API, which is now available with the latest version of Framer X.
First let’s talk about some theory
Behind the scenes, everything you see in Framer X is a React component. Components have properties such as height, width, and border radius. We call them
props. When you add a frame to the canvas and pick a background color in the properties panel, you are setting the frame’s
Framer lets you apply code to a layer that will override the layer’s props when it is displayed in the preview window. An override is simply a function that returns new values for any properties you want to override. For example, to move a layer
100px down along the y-axis we need to set its
y prop to
Here’s the code override to do that:
All my examples use overrides. If you are new to Framer X you can read more about code overrides in the Framer Docs. If you need an introduction to React, you might enjoy reading Koen Bok’s introduction to React for web designers.
Let’s get our content scrolling
Drop a scroll component onto the canvas and connect it to a frame containing your scroll content. As you scroll, Framer moves the content frame relative to the scroll component. We can access the distance our content is offset through two props:
Parallaxes are all about relative movement. Let’s take a simple example where we are scrolling vertically and our parallax layer sits on the background of the scroll content. Under normal circumstances, the background layer moves with the main content. If we want it to move at half the speed of the main content then, for every
100px it is scrolled up, we need to add a downwards translation of
50px. In other words, whatever the current value of
contentOffsetY, we need to take that value, halve it, and apply the transformed value to the parallax layer’s
Keeping track of the scroll distance
Framer Library, allows us to track the state and velocity of a given property with a special variable called a MotionValue. We track
contentOffsetY like this:
First create a
MotionValue (set to zero initially):
Then use it to override
contentOffsetY in our scroll component:
Whenever we scroll, the
MotionValue will update with the content offset value.
The Framer Library also gives us a way to transform
MotionValues with the
useTransform creates a
MotionValue by transforming the output of another
contentOffsetY is within the range
-100, its value will be transformed to create a
y value in the range
The input range is negative because
contentOffsetY is measured top down, so scrolling up gives us a negative offset.
y will be clamped so it is only transformed when
contentOffsetY is within the input range. We want to transform
y no matter how much we scroll. To achieve this, we need to pass the function an additional argument:
Here’s the complete code:
More scroll-driven transformations
The same technique can be used to make vertical scrolling drive horizontal movement. Just apply a transformed
x instead of y. When applied to a layer inside the scroll content, the layer could drift sideways as it scrolls up. But the layer doesn’t need to sit inside the scroll content to move with the parallax. Applying the below code to the
y property of a layer on top of the scroll component would make it move with the scroll content, but at twice the speed.
Dynamic app bar
This is another great use case for scroll-driven transformations. Here’s what you need to make an iOS app bars shrink from
Normally, iOS app bars shrink to a minimum size but, pulled in the other direction, they will stretch as far as you are able to pull them. In other words, we only want to clamp the value in one direction. Here’s a little trick to make that work:
Putting it all together:
Triggering animations at set scroll positions
Another feature of app bars in iOS is the title, which fades in when the bar reaches it’s minimum size. How can we animate the opacity of the title so it appears only when
contentOffsetY passes a threshold?
This is a good time for a bit more theory
So if you’ve come from Framer Classic you are about to discover a whole new programming model.
In Classic, code is imperative, it tells the prototype what to do:
When I scroll, if
contentOffsetY is greater than a threshold, then animate the opacity of my title to a new value.
React requires a new way of thinking. Instead of specifying what to do, the code is declarative; it tells the prototype how to be:
I want to animate my title depending on a condition.
React takes care of everything else, making sure the title animates when the condition changes.
So our code needs three parts:
- A data object to keep track of the state of our application:
contentOffsetYpast a limit?
- Something to listen for changes to
contentOffsetYand update the application state if the limit is passed.
- A function telling the title to animate to either
0depending on whether
contentOffsetYis past the limit.
One important thing to note. Framer doesn’t allow you to override the opacity of a text layer. So you will need to wrap a frame around the text layer and apply to override to the frame instead. Get used to seeing this:
In React, this is your best friend.
Scroll smoothly to a fixed position
Let’s say our prototype has a button to automatically scroll content back to it’s starting position. To implement this, we need the
useAnimation function which returns an animation controller with
start method is explained in the API docs. It takes an argument with the property and value we want to animate to. In our case, we are animating the
y translation of the scroll content layer directly.
So the code has three parts:
- Create the animation controller
- Override the scroll component’s
scrollAnimateproperty with this controller
- Override the button’s
onTapproperty with a function to start the animation
Here is the code in full:
I’m going to end by showing you how to add Sticky Headers to your prototypes. The code is almost identical to the solution for parallax, we just need to apply a different transformation.
If the header travels
400px before it sticks, then we need the following transform:
Here’s the code in full:
There’s a better way!
This approach works fine when you have multiple section headers, where each one sticks once it reaches the top, pushing the previous header out the way. But, depending on your layout, you might need to calculate and apply a different travel value for each one.
This sounds hard, but don’t panic! I’ve made a Framer Store package to save you the effort.
After you install this package you will see two components:
StickyScroll works like a native scroll component.
StickyElement should be placed inside your scroll content and connected to another canvas element. This will make the connected content appear inside the scroll content and stick when you scroll past the top of the scroll component.
You can use multiple
StickyElements in your scroll content. When each
StickyElement reaches the top, it pushes the
StickyElement above it up and off the top of the scroll component.
Time to put it all together
Congratulations! You’ve mastered scrolling. With the techniques and concepts you’ve learned here, you have the building blocks of almost every kind of scroll interaction.
Here are some other helpful resources to advance your Framer X knowledge:
Want more design articles?
Join our newsletter and get resources, curated content, and design inspiration delivered straight to your inbox.