Building a Parallax Header in React

Kevin Simpson
4 min readJan 23, 2019

Headers are important. They’re the first thing that people see when your screen loads, so making an effort to ensure that the header is immediately engaging is worth the time and energy expended. One of the easiest ways to do this is with a parallax effect on the header itself.

For the uninitiated

In the context of web design, a parallax effect is where layered objects appear to move up or down the page at different relative speeds when you scroll. The speed at which you move your different layers can make this effect as subtle or dramatic as you like.

In the above example, we have two containing elements in the header: the background image with the keyboard and headphones, and the wrapper for my name and title. As we scroll down, the background image scrolls up, albeit at a slower speed than one would normally expect. The text wrapper, however, scrolls upward at a slightly faster speed.

This is the bread-and-butter of parallax effects; things in foreground scroll at a faster speed than things in the background.

Here we see the same effect taking place, this time in the context of an old video game. There are several layers of background here, all moving at different speeds relative to the user’s point of view. Trees along the side of the road move so fast that we barely catch a glimpse of them. The mountains in the background move a bit slower due to their larger size and greater distance from the user’s point of view. The cloud layer behind the mountains moves even slower than that. Finally, we have the moon (or sun?) which doesn’t move at all.

This combination of different layers moving at different speeds gives the user the illusion of three dimensions when we’re really just dealing with two-dimensional elements stacked on top of each other being individually manipulated in accordance to z-index, size, and abstracted distance.

How to Build it:

The JSX for this is pretty simple. You need a containing element for the background, a container for the text, and the two text elements themselves.

return (
<header
className='header-background'
style={{ backgroundPositionY: this.state.offset}}
>
<section
className='info-container'
style={{ bottom: this.state.offset / 2 }}
>
<h1>Kevin Simpson</h1>
<h3>Front End Developer</h3>
</section>
</header>
)

You can see two separate instances of using this.state.offset to manipulate the style of the above JSX. One is being used for the <header> element with the background, where this.state.offset is used to determine the vertical positioning of the background image at a one-to-one ratio. In the case of the <section> containing the two text elements, we are using this.state.offset to manipulate its positioning at a two-to-one ratio.

If you’re thinking ‘doesn’t the foreground image need to move faster than the background image?’, you’re correct, and it is (sort of). With this above setup, the background will appear to be a static image that disappears into the background of the containing sibling below it. For all intents and purposes, it will look like it’s staying still, and that we’re simply sliding the rest of the application over it. Meanwhile, the foreground container will be moving up and out of view at a faster rate than would appear normal since we are pushing it up against the bottom of the container while also scrolling down past it.

Next we need to setup our state.

class Header extends React.Component {
constructor() {
super()

this.state = {
offset: 0
};
}
}

This gives us the value we need to leverage in our JSX for determining the background position in the <header> and the relative positioning in the <section>. However, this alone won’t do anything. We need to be changing this.state.offset as we scroll, which means we need some kind of an event listener on the DOM to fire so that we can call a function to update on scroll.

componentDidMount() {
window.addEventListener('scroll', this.parallaxShift);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.parallaxShift);
}
parallaxShift = () => {
this.setState({
offset: window.pageYOffset
});
};

Since we’re using the componentDidMount() and componentWillUnmount() lifecycle methods, this means that we need to be using a stateful, class-based component (unless you are brave enough to dive into the new React Hooks…).

And… that’s it! If you’d like to make the effect more dramatic, you’ll want to multiply the value of this.state.offset on the <header> and the <section> elements. To make the effect more subtle, reduce these values.

--

--

Kevin Simpson

Front End/JS Developer for Trimble SketchUp, Turing School graduate, 8th-level Bard