How I put the scroll percentage in the title bar

Scroll down!




I mean it! Just scroll!







And then look up. A bit further. Yes, to the area of your tab bar where the <title> content ends up. And then scroll again.

There's a percentage there. And it changes when you are scrolling. Kinda cool isn‘t it? Now you can tab away from this site and still have a sense of how far you have read. That surely makes your existence a tad more pleasant, doesn‘t it?

The idea to put the scroll percentage in the title bar came from this tweet by @round.

I didn’t make a browser extension, rather, I just put the mechanics into my own blog post template. Since I’m on Gatsby and React, it was not that hard. Here’s how to do it:

First I installed the react-scroll-percentage package. That takes care of most of the heavy lifting. Then, and I’m honestly feel a bit out of depth here, I thought it made sense to put the actual mechanics into useEffect. It may be that it would have been possible to make Gatsby and React rerender the <title> part in another way, but I was just following my gut here (tell me on Twitter if there‘s a better way). So I ended up with something like this (actual code here):

import React, {useEffect} from 'react'
import {useScrollPercentage} from 'react-scroll-percentage'
import Container from '../components/container'
import BlogPost from '../components/blog-post'
import SEO from '../components/seo'
import Layout from '../containers/layout'
import {toPlainText} from '../lib/helpers'
const BlogPost = ({post}) => {
const {title, _rawExcerpt} = post
const [ref, percentage] = useScrollPercentage()
useEffect(() => {
if (post) {
const percent = Math.round(percentage * 100)
document.title = `${percent}% ${post.title}`
}, [percentage])
return (
{post && <SEO title={` ${title}` || 'Untitled'} description={toPlainText(_rawExcerpt || [])} />}
<div ref={ref}>
{post && <BlogPost {} />}
export default BlogPost

Since react-scoll-percentage uses Intersection Observer, which is a relatively new browser API, we also need a polyfill for browsers that doesn’t support it yet. First install it as a dependency.

npm i intersection-observer

Then we want Gatsby only to use the polyfill in the browser, and import it dynamically when it‘s needed. We’ll do that by inserting this into gatsby-browser.js:

// gatsby-browser.js
export const onClientEntry = async () => {
if (typeof IntersectionObserver === `undefined`) {
await import(`intersection-observer`);

And that’s it! Now, marvel at your new updating reading lenght!

Moar posts!