yarn add @faceless-ui/jumplist
To track elements as they enter and exit the viewport, you first need to wrap your app with the <JumplistProvider>
. This does not render anything in the DOM and is where the global settings are defined. This will maintain the state of every jumplist node in your app and provide this state through context.
import React from 'react'; import { JumplistProvider } from '@faceless-ui/jumplist'; export const MyApp = () => { return ( <JumplistProvider threshold={0.05} rootMargin="-100px 0px 0px 0px" > ... </JumplistProvider> ) }
Then, use a <JumplistNode>
for every element you want to track. The only required prop of this component is the nodeID
, a unique string identifying each node.
import React from 'react'; import { JumplistNode } from '@faceless-ui/jumplist'; export const MyComponent = () => { return ( <div> <JumplistNode nodeID="node-1"> Node 1 </JumplistNode> <JumplistNode nodeID="node-2"> Node 2 </JumplistNode> </div> ) }
Every node is a wrapper around the Intersection Observer API, reporting its isIntersecting
status to the jumplist context. You can access this from anywhere with the useJumplist
hook.
import React from 'react'; import { useJumplist } from '@faceless-ui/jumplist'; export const MyComponent = () => { const { jumplist } = useJumplist(); return ( <div> {jumplist.map((node, index) => { const { isIntersecting } = node; return ( <div key={index}> {`Node ${index} is intersecting: ${isIntersecting}`} </div> ) })} </div> ) }
You could also pass a function as a child to the <JumplistNode>
to immediately return the context of that node.
import React from 'react'; import { JumplistNode } from '@faceless-ui/jumplist'; export const MyComponent = () => { return ( <JumplistNode nodeID="node-1"> {({ isIntersecting }) => ( <div> {`Is intersecting: ${isIntersecting}`} </div> )} </JumplistNode> ) }
To navigate jumplist nodes we rely entirely on native HTML behavior. By adding a hash to the URL that matches the id of an element in the document, the browser will automatically scroll to that element. You can also enable smooth scrolling.
import React from 'react'; export const MyComponent = () => { return ( <a href="#node-1"> Go to node 1 </a> ) }
Alternatively, you can navigate jumplist nodes directly using the scrollToID
method. This does not inject a hash into the URL, but instead relies on the native browser scrollIntoView API.
import React from 'react'; import { useJumplist } from '@faceless-ui/jumplist'; export const MyComponent = () => { const { scrollToID } = useJumplist(); return ( <button onClick={() => { scrollToID('node-1'); }}> Jump to Node 1 </button> ) }
This package does not perform any smooth-scrolling of its own, and instead relies on native CSS scroll-behavior property.
Use the smoothScroll prop on the provider.
<JumplistProvider smoothScroll > ... </JumplistProvider>
It sets the following property as inline CSS.
html: { scroll-behavior: smooth; }
There may also be cases where you want to temporarily disable smooth-scrolling, like between page transitions. To do this, you will need to add and remove this property dynamically using your app's existing router.
import { Router } from "next/router"; import { useEffect } from "react" export const ScrollToTopOnRouteChange = () => { useEffect(() => { Router.events.on('routeChangeStart', () => { // temporarily disable smooth-scrolling document.documentElement.style.scrollBehavior = 'auto'; }) Router.events.on('routeChangeComplete', () => { // scroll instantly to the top of the page window.scroll({ top: 0, left: 0, }); // resume smooth-scrolling document.documentElement.style.removeProperty('scroll-behavior'); }); }, []); return null; }