Manuel Nelson
Manuel Nelson

Reputation: 335

Smooth scroll in next js

How can I set the scroll effect to smooth (globally) in Next.js? I tried to do it on the global css, but it deactivates the scroll-to-top that Next js already has.

I tried this solution that i found on internet too, but it didn't worked either.

 componentDidMount() {
 Router.events.on('routeChangeComplete', () => {
    window.scroll({
       top: 0,
       left: 0,
       behavior: 'smooth'
    });
 });

}

Upvotes: 18

Views: 71401

Answers (13)

Truong Nguyen
Truong Nguyen

Reputation: 71

only need to add scroll-smooth to html tag and it works

{children}

Upvotes: 0

Ricky Anchores
Ricky Anchores

Reputation: 1

You can add className="!scroll-smooth" on the layout.js and it will work!

Upvotes: -1

Just put style={{scrollBehavior:'smooth'}} in the tag Html in '_document.tsx' file.

Like :

class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const initialProps = await Document.getInitialProps(ctx);
    return { ...initialProps };
  }

  render() {
    return (
      <Html className='scroll-smooth' style={{scrollBehavior:'smooth'}}>
        <Head>
          <link rel='icon' href='/favicon.ico' />
          <meta
            name='description'
            content='A place to find a great film to watch'
          />
        </Head>
        <body className='bg-gray-50 screen'>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

Upvotes: 22

Hashira
Hashira

Reputation: 1201

For any one using Next 13 (App dir) and above and want to be able to scroll to specific section on the current page or navigate to another page and automaticaly scroll to a specific section and like me has tried everything here and didn't work then checkout this answer.

Firstly Create a Function that Accept a Id String and Scroll the element Into View.

// Handles scrolling of Element to view
export function scrollElementToView(scrollToId: string) {
  const element = document.querySelector(`#${scrollToId}`) as HTMLElement;

  const elRect = element.getBoundingClientRect();

  const scrollDistance = elRect.top + window.scrollY;

  // Incase you want to offset the scroll To view Position.
  const offset = Number(element.getAttribute('data-scroll-to-view-offset')) || 0;

  window.scrollTo({
    top: scrollDistance + offset,
    behavior: 'smooth'
  })
}

Then create a file name ScrollToLinkGlobalComponent and add

'use client';
import React, { useEffect } from 'react';
import { useSearchParams } from 'next/navigation';
import { scrollElementToView } from //whereEver you keep the file



function ScrollToLinkGlobalComponent() {
  const searchParams = useSearchParams();

  useEffect(() => {
    // get element Id from searchParams
    const scrollToId = searchParams.get("scrollToId");

    if (!scrollToId) return; // return if there is none

    scrollElementToView(scrollToId);

  }, [searchParams])

  return null
}

export default ScrollToLinkGlobalComponent

Now Add ScrollToLinkGlobalComponent to your RootLayout. Its should look something like this

*** Imports ***


export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={nunitoSansFont.className}>
      <body>
          {children}
          <ScrollToLinkGlobalComponent />
      </body>
    </html>
  )
}

Then Create a ScrollToLink Component that extends the default Next Link Component.

'use client';
import Link from 'next/link';
import React, { ComponentPropsWithRef } from 'react';
import { useSearchParams } from 'next/navigation';
import { scrollElementToView } from //whereEver you keep the file


interface PropTypes extends ComponentPropsWithRef<typeof Link> {
    scrollToId: string
}



function ScrollToLink({ children, scrollToId, href, ...props }: PropTypes) {
    const searchParams = useSearchParams();


    const persistScrollFeature = () => {
        const urlScrollToId = searchParams.get("scrollToId");

        if (!urlScrollToId || scrollToId !== urlScrollToId) return; //let the Global Component Handle it

        scrollElementToView(urlScrollToId);
    }

    return (
        <Link {...props}
            onClick={persistScrollFeature}
            href={`${href}?scrollToId=${scrollToId}`}
            scroll={false} //very important, its disable nextJs scroll To top on navigation feature
        >
            {children}
        </Link>
    )
}

export default ScrollToLink

Now in any of your Component you can import and use ScrollToLink like this.

<ScrollToLink href="/" scrollToId='hero-section'>Hero Section</ScrollToLink> 
<ScrollToLink href="/" scrollToId='Testimony-section'>Hero Section</ScrollToLink> 

<div id='hero-section'>I am Hero Section</div>
<div id='Testimony-section'>I am Hero Section</div>


//You can offset the scrollToView position like this
<div id='Testimony-section' data-scroll-to-view-offset='-200'>I am Hero Section</div>

Upvotes: 1

aashish-cd
aashish-cd

Reputation: 1

Adding scroll={false} to Link component or simply using HTML's anchor tag solved this.

<Link scroll={false} href='#idname'>
 link name
</Link>

or

<a href='#idname'>
 link name
</a>

Upvotes: 0

saqib idrees
saqib idrees

Reputation: 91

Following code works fine for me.

<Link className="navlinks1 whitespace-nowrap" href="/#faqs" onClick={(e) => {
            e.preventDefault();
            document.getElementById("faqs").scrollIntoView({ behavior: "smooth" });
          }}>
            FAQs
          </Link>

Upvotes: 4

Gvs Akhil
Gvs Akhil

Reputation: 2610

This works fine for me in NextJS

In global.css

* {
    scroll-behavior: smooth;
}

And following code for href I have used:

<a href="#features" className={`${styles.feature} pointer`}>Features</a>

Upvotes: 9

dopecello
dopecello

Reputation: 241

Go to your global or main css folder and write:

html{scroll-behavior: smooth;}

or, if you are using Tailwind, go into your _document.js file and apply this:

import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
  return (
    <Html lang="en" className='scroll-smooth'>
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

If you want smooth scrolling between links on the same page, do something like this where you are manipulating the scroll property on the next/link component:

<Link href="/#skills" scroll={false}>
    <li className="ml-10 text-sm uppercase hover:border-b">Skills</li>
</Link>

also dont forget to set id='whatever' on the div you want to navigate to.

Hope this helps! :)

Upvotes: 1

Shubham Gaikwad
Shubham Gaikwad

Reputation: 673

Add scroll-behavior:smooth in html or body. Then adding in

<Link href="#someid" scroll={false}>

works

Upvotes: 44

Md. Al Amin
Md. Al Amin

Reputation: 1

<style global jsx>
   {`
       html {
          scroll-behavior: smooth;
        }
  `}
  </style>

Upvotes: -1

pradeexsu
pradeexsu

Reputation: 1145

Instead of inheriting we can implement Document Component in _document.tsx. creating function component worked for me.

_document.tsx

import { Html, Head, Main, NextScript } from "next/document";

export default function Document() {
  return (
    <Html className='scroll-smooth' >
      <Head/>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

https://nextjs.org/docs/advanced-features/custom-document

Upvotes: 0

Atif Khan
Atif Khan

Reputation: 224

If you're using Class Component, you may copy and paste the following code:

import React, { Component } from 'react';

export default class ScrollToTopButton extends Component {
  // this is function that will scroll to the top of the page when the button is clicked
  scrollToTop = () => {
    window.scrollTo({
      top: 0,
      behavior: 'smooth',
    });
  };

  render() {
    return (
      <>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        <div> textInComponent </div>
        {/* This is the button that will go up */}
        <button onClick={() => this.scrollToTop()}> textInComponent </button>
      </>
    );
  }
}

Alternatively, if you're implementing a function component

import React, { Component } from 'react';

function ScrollToTopButton() {
  // this is function that will scroll to the top of the page when the button is clicked
  const scrollToTop = () => {
    window.scrollTo({
      top: 0,
      behavior: 'smooth',
    });
  };

  return (
    <>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      <div> textInComponent </div>
      {/* This is the button that will go up */}
      <button onClick={() => scrollToTop()}> textInComponent </button>
    </>
  );
}

export default ScrollToTopButton;

I'm not sure how to describe this code, but it works on my end; I hope it helps!

Upvotes: 2

Manuel Nelson
Manuel Nelson

Reputation: 335

I solved it! You can do this (not globally, but it works fine) with the npm package react-scroll Here is the link: https://www.npmjs.com/package/react-scroll

Upvotes: -1

Related Questions