Git Product home page Git Product logo

next-router-scroll's Introduction

next-router-scroll

NPM version Downloads Build Status Coverage Status Dependency status Dev Dependency status

Take control of when scroll is updated and restored in your Next.js projects.

Installation

$ npm install @moxy/next-router-scroll

This library is written in modern JavaScript and is published in both CommonJS and ES module transpiled variants. If you target older browsers please make sure to transpile accordingly.

Motivation

There are some cases where you need to take control on how your application scroll is handled; namely, you may want to restore scroll when user is navigating within your application pages, but you need to do extra work before or after the page has changed, either by using some sort of page transition or any other feature.

@moxy/next-router-scroll makes it easy to update the window scroll position just like a browser would, but programmatically.

This package is built on top of scroll-behavior and it's meant to be used in Next.js applications. It actively listens to Next.js router events, writing the scroll values associated with the current location in the Session Storage and reading these values whenever updateScroll() is called.

Usage

First install the provider in your app:

// pages/_app.js
import { RouterScrollProvider } from '@moxy/next-router-scroll';

const App = ({ Component, pageProps }) => (
    <RouterScrollProvider>
        <Component { ...pageProps } />
    </RouterScrollProvider>
);

export default App;

Then use the hook or HOC to update the scroll whenever you see fit.

// pages/index.js
import { useRouterScroll } from '@moxy/next-router-scroll';

const Home = () => {
    const { updateScroll } = useRouterScroll();

    useEffect(() => {
        updateScroll();
    }, []);
};

export default Home;

⚠️ By default, <RouterScrollProvider /> monkey patches Next.js <Link /> component, changing the scroll prop default value to false. You can disable this behavior by setting the disableNextLinkScroll prop to false.

API

<RouterScrollProvider />

A provider that should be used in your app component.

shouldUpdateScroll?

Type: function

A function to determine if scroll should be updated or not.

// pages/_app.js
import { RouterScrollProvider } from '@moxy/next-router-scroll';

const App = ({ Component, pageProps }) => {
    const shouldUpdateScroll = useMemo((prevContext, context) => {
        // Both arguments have the following shape:
        // {
        //     location,
        //     router: { pathname, asPath, query }
        // }
    }, []);

    return (
        <RouterScrollProvider shouldUpdateScroll={ shouldUpdateScroll }>
            <Component { ...pageProps } />
        </RouterScrollProvider>
    );
};

export default App;

Check custom scroll behavior for more information.

⚠️ Please note that prevContext might be null on the first run.

disableNextLinkScroll?

Type: boolean
Default: true

True to set Next.js Link default scroll property to false, false otherwise. Since the goal of this package is to manually control the scroll, you don't want Next.js default behavior of scrolling to top when clicking links.

children

Type: ReactNode

Any React node to render.

useRouterScroll()

A hook that returns an object with the following shape:

{
    updateScroll(prevContext?, context?),
    registerElement(key, element, shouldUpdateScroll?, context?),
    unregisterElement(key)
}

updateScroll(prevContext?, context?)

Call updateScroll function whenever you want to update the scroll. You may optionally pass prevContext and context objects which will be available inside shouldUpdateScroll.

Please note that prevContext and context have default values and any values you pass will be mixed with the default ones.

Use With Async Rendering:

If you're asyncronously loading DOM elements and need to wait for an element you can utilize React's approach for measuring DOM nodes. Here is an example of what that could look like:

const MyComponent = () => {
    const { updateScroll } = useRouterScroll();
    const divRef = useCallback((node) => {
        if (node) {
            updateScroll();
        }
    }, [updateScroll]);


    return someCondition ? <div ref={ divRef }>hi</div> : null;
};

registerElement(key, element, shouldUpdateScroll?, context?)

Call registerElement method to register an element other than window to have managed scroll behavior. Each of these elements needs to be given a unique key at registration time, and can be given an optional shouldUpdateScroll callback that behaves as above. This method can optionally be called with the current context if applicable, to set up the element's initial scroll position.

unregisterElement(key)

Call unregisterElement to unregister a previously registered element, identified by key.

withRouterScroll(Component)

A HOC that injects a routerScroll prop, with the same value as the hook variant.

import { withRouterScroll } from '@moxy/next-router-scroll';

const MyComponent = ({ routerScroll }) => {
    // ...
};

export default withRouterScroll(MyComponent);

Tests

$ npm test
$ npm test -- --watch # during development

Demo

A demo project is available in the /demo folder so you can try out this component.

First, build the next-router-scroll project with:

$ npm run build

Note: Every time a change is made to the package a rebuild is required to reflect those changes on the demo. While developing, it may be a good idea to run the dev script, so you won't need to manually run the build after every change

$ npm run dev

To run the demo, do the following inside the demo's folder:

$ npm i
$ npm run dev

License

Released under the MIT License.

next-router-scroll's People

Contributors

afonsovreis avatar satazor avatar therynamo avatar tiagodinis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

next-router-scroll's Issues

Upgrading to Next.js V12

Will next-router-scroll be updated so it is compatible with Next.js version 12? Currently peer and dev dependencies are atmost version 11.

proposal: Update README Examples

Hi there team!

Your package here is stupendous and valuable, so thank you for your hard work! This topic is, somehow, not super easy for some reason. You'd think, the browser should just do its thing and ✅ ?

A common use case that I ran into is waiting for a specific element to render before updateScroll is called. I read over the README and I read over your tests (thanks for adding those!). Unfortunately, I couldn't quite piece together just "how" registering an element would allow me to "wait for it to render" before updating scroll.

So what I did instead was use the update ref pattern that React outlines here.

My implementation ended up looking a little like this:

const MyComponent = () => {
  const divRef = useCallback(
    (node) => {
      if (node !== null) {
        updateScroll();
      }
    },
    [updateScroll]
  );

  return (
    <div ref={divRef}>hi</div>
  );
}

This ended up working pretty well for me, and allowed me to explicitly wait for a node to appear before updating scroll.

The reason I'm opening this issue, is I'm wondering if the README could be more clear about how to use registerElement and updateScroll together. If the intended use case for those is not similar to the above implementation, would it be valuable to add an example that showed how to do something like this?

Thanks again for all your hard work!

[ASK]Typescript Definition File Required

Please tell me about the plan to open the declaration file for typescript user.

If you don't have any plan for now, then can I pull request the declare file?

Thank you for your attention.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.