Gatsby React Scroll toTop

Donald Boulton

Written by Donald Boulton who lives and works in OKC OK. 

 April 22, 2019   4 min read

Gatsby React Scroll toTop or Bottom

Have you tried Scroll to the top of the page react module's, I have and all of them use the .window function to go to top. Which does not work in Gatsby without a work around.

import Scroll from 'react-scroll'

Then tell Gatsby that, window !== undefined.

// eslint-disable-next-line valid-typeof
if (typeof window !== undefined) { require('react-scroll') }

Such as the errors I got from react-scroll, and the workaround I used, "Which Works", but I did not like the implementation

So I came up with my own digging through Gatsby Repos. With React we can use handleScroll and window.pageYOffset to go to top or bottom of the page or anything.

import React, { useState, useEffect } from 'react'

// and then use handleScroll and window.pageYOffset to go to top or bottom

const scroll = ({ mode, to }) =>
    window[`scroll` + mode]({ top: to, behavior: `smooth` })

  const handleScroll = () => {
    if (window.pageYOffset > showBelow) {
      if (!show) setShow(true)
    } else {
      if (show) setShow(false)
    }
  }

The Component using a styled icon styledArrow

src/components/Scroll
import React, { useState, useEffect } from 'react'import { Arrow } from './styles'
const Scroll = ({
  direction = `up`,  by,
  to,
  showBelow,
  className,
  size = `1.7em`,
}) => {
  if (![`up`, `down`].includes(direction))
    throw TypeError(
      `Scroll component's direction prop must be either 'up' or 'down'`
    )
  if (to && (typeof to !== `number` || to <= 0))
    throw TypeError(`Scroll component's to prop must be a positive number`)
  if (by && typeof by !== `number`)
    throw TypeError(`Scroll component's by prop must be a number`)

  const [show, setShow] = useState(showBelow ? false : true)

  const scroll = ({ mode, to }) =>
    window[`scroll` + mode]({ top: to, behavior: `smooth` })

  const handleScroll = () => {
    if (window.pageYOffset > showBelow) {
      if (!show) setShow(true)
    } else {
      if (show) setShow(false)
    }
  }

  const handleClick = () => {
    if (to) scroll({ mode: `To`, to: to * window.innerHeight })
    else if (by) scroll({ mode: `By`, to: by * window.innerHeight })
    else if (direction === `up`) scroll({ mode: `To`, to: 0 })
    else scroll({ mode: `To`, to: document.body.scrollHeight })
  }

  useEffect(() => {
    if (showBelow) {
      window.addEventListener(`scroll`, handleScroll)
      return () => window.removeEventListener(`scroll`, handleScroll)
    }
  })

  const arrowProps = { show, direction, className, size }
  return <Arrow onClick={handleClick} {...arrowProps} />
}

export default Scroll

The styles.js Code

src/components/Scroll/styles
import styled from 'styled-components'
import { ArrowDownCircle as Down } from 'styled-icons/feather/ArrowDownCircle'
import { ArrowUpCircle as Up } from 'styled-icons/feather/ArrowUpCircle'

export const Arrow = styled(Down).attrs(({ direction, size }) => ({
  as: direction === `up` && Up,
  size,
}))`
  ${({ theme, show, size }) => `
  z-index: 2;
  background: ${theme.darkOrange};
  color: ${theme.white};
  border-radius: 50%;
  transition: ${theme.shortTrans};
  position: fixed;
  bottom: 2.5em;
  opacity: ${show ? 1 : 0};
  visibility: ${show ? `visible` : `hidden`};
  :hover {
    transform: scale(1.15);
    background: ${theme.orange};
  }
  right: calc(1vw - ${size} / 1);`}
`

Add Scroll to Layout

src/components/Scroll
  <Scroll
    showBelow={1500}
    css='position: fixed; right: 1.3em; bottom: 1.3em;'
  />

Scroll Down

src/components/ScrollDown
import React, { useState, useEffect } from 'react'
import { Arrow } from './styles'

const ScrollDown = ({
  direction = `down`,
  by,
  to,
  showBelow,
  className,
  size = `1.7em`,
}) => {
  if (![`up`, `down`].includes(direction))
    throw TypeError(
      `Scroll component's direction prop must be either 'up' or 'down'`
    )
  if (to && (typeof to !== `number` || to <= 0))
    throw TypeError(`Scroll component's to prop must be a positive number`)
  if (by && typeof by !== `number`)
    throw TypeError(`Scroll component's by prop must be a number`)

  const [show, setShow] = useState(showBelow ? false : true)

  const scroll = ({ mode, to }) =>
    window[`scroll` + mode]({ top: to, behavior: `smooth` })

  const handleScroll = () => {
    if (window.pageYOffset > showBelow) {
      if (!show) setShow(true)
    } else {
      if (show) setShow(false)
    }
  }

  const handleClick = () => {
    if (to) scroll({ mode: `To`, to: to * window.innerHeight })
    else if (by) scroll({ mode: `By`, to: by * window.innerHeight })
    else if (direction === `up`) scroll({ mode: `To`, to: 0 })
    else scroll({ mode: `To`, to: document.body.scrollHeight })
  }

  useEffect(() => {
    if (showBelow) {
      window.addEventListener(`scroll`, handleScroll)
      return () => window.removeEventListener(`scroll`, handleScroll)
    }
  })

  const arrowProps = { show, direction, className, size }
  return <Arrow onClick={handleClick} {...arrowProps} />
}

export default ScrollDown

Scroll Down styles.js

src/components/ScrollDown/styles
export const Arrow = styled(Up).attrs(({ direction, size }) => ({
  as: direction === `down` && Down,
  size,
}))`
  ${({ theme, show, size }) => `
  z-index: 2;
  background: ${theme.darkOrange};
  color: ${theme.white};
  border-radius: 50%;
  transition: ${theme.shortTrans};
  position: fixed;
  top: 4.5em;
  opacity: ${show ? 1 : 0};
  visibility: ${show ? `visible` : `hidden`};
  :hover {
    transform: scale(1.15);
    background: ${theme.orange};
  }
  right: calc(1vw - ${size} / 1);`}
`

Added Down to ArticleTemplate

Only my post's are long enough to need scroll down

add
  <ScrollDown
    direction='down' to={15}
    showAbove={1500}
    css='position: fixed; right: 1em; top: 4.5em;'
  />

Scroll down is visible at the top of the page and is fixed. I think its cool and somewhat original. Not static like the Netlify build page has scroll to bottom of the build log or back to top which is pretty Cool.


Is this a useful post? Please give us a rating!
Is this post Cool? Please Tweet About it!

Comments

Issues

PubliusLogic

Heatmaps by Hotjar

Hotjar is the fast and visual way to understand your users..

Hotjar