'NextJS scroll into view of specific component upon button OnClick that exists on a different js file

I'm using NextJS and I have my components on different js files that come together under one js file on my "pages" folder (index.js). What I want to be able to do is click on a button on my header/ navbar and scroll into the view of a component into another js file. (I'm really new to NextJS, React hooks etc.) Tried using an href={myRef} into the component I want to scroll into but the file that contains it gives a reference error that "myRef is not defined". Also, tried document.getElementById etc but of course it's not working as NextJS is server-rendered.

My components are wrapped into functions, so functional components.

<Button
              variant="outlined"
              size="small"
              onClick={doSomethingAndScrollThere}
              sx={{
                background: "rgba(196, 196, 196, 0.3)",
                border: "none",
                display: { xs: "none", sm: "block" },
                "&:hover": {
                  border: "1px solid black",
                  background: "rgba(196, 196, 196, 0.3)",
                },
              }}
            >
              <Typography
                variant="subtitle2"
                sx={{ color: "black", textTransform: "Capitalize" }}
              >
                Contact me
              </Typography>
            </Button>

I'm also using Material UI. This is the button nested inside my navbar. And here is the component that I want to scroll into. (Noting that that box component is also nested inside a grid component)

import * as React from "react";
   import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";

const ContactBox = () => {
  return (
    <React.Fragment>
      <Box
        id="contactBox"
        sx={{
          background: "rgba(196, 196, 196, 0.3)",
          border: "none",
          borderRadius: "40px",
          justifyContent: "center",
          padding: "20px",
        }}
      >
        <Typography variant="h5">Contact info</Typography>
        <Typography variant="subtitle1">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </Typography>
      </Box>
    </React.Fragment>
  );
};
export default ContactBox;

Up is the box_component's file.js and down is the grid component's file.js containing the box_component:

<Grid item xs={8}>
      <ContactBox />
    </Grid>

There was no point in adding the whole code in the last part. Thank you for your answers, in advance and go easy on me, as I said, I'm still learning.



Solution 1:[1]

@dharman answer helped me like that: added onClick={() => router.push("/#contactBox")} inside my button, this piece of code inside my functional component (in this case it was my topbar/navbar) const router = useRouter() and this import { useRouter } from "next/router"; at the top!

It would be great if I could scroll smoothly on it too! Thanks for your help!

Solution 2:[2]

You could achieve smooth scroll by leveraging DOM built-in utils through react like so;

say you create the definition for component like so

type IScrollableAnchorProps = {
  id: string;
};

const ScrollableAnchor: React.FC<IScrollableAnchorProps> = ({
  id,
  children,
}) => {
  const router = useRouter();
  const hashMatchRegex = useRef(new RegExp(/(?<=#)\w*/));
  const scrollTargetElementRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    const match = router.asPath.match(hashMatchRegex.current);
    if (match && match[0] === id) {
      scrollTargetElementRef.current?.scrollIntoView({ behavior: 'smooth' });
    }
  }, [id, router.asPath]);

  return (
    <div id={id} ref={scrollTargetElementRef}>
      {children}
    </div>
  );
};

You'd use said definition like so;

<a href="/#ContactMe">Contact Me</a>

<ScrollableAnchor id="ContactMe">
<>Your content here</>
</ScrollableAnchor>

This also works with page transitions

Solution 3:[3]

I am a little unclear exactly what you are trying to achieve but it sounds like what you are trying to do can be done with hash anchor links and the html id attribute.

The first thing I would mention is that you should be using the official next.js Link component.

https://nextjs.org/learn/basics/navigate-between-pages/link-component

So likely you will wrap your "Button" component in the navbar with "Link" like so...

import Link from 'next/Link';
// ...removed for brevity

export default function MyNavBar() {
  return (
    <Link href="/#ContactMe" passHref>
      <Button>
        <Typography>
        ...your button
        </Typography>
      </Button>
    </Link>
  );
}

Example, on my /pages/index.html I have some different components like...

export default function Home() {
  return (
    <GridWrapper>
      <div id="top">
        The top of my page
      </div>
      <div id="middle">
        The middle of my page
      </div>
      <div id="ContactMe">
        The contact me part of your page
      </div>
    </GridWrapper>
  );
}

If the page is not your home page, like say it is to 127.0.0.1:3000/about and there is a ContactMe component you want to scroll directly to then in your about page place the id attribute where you want it and in the "Link" component make the href="/about#ContactMe".

Solution 4:[4]

For smooth scrolling to a section on the page, you can implement the following:- (Note: exact same solution is implemented on my website https://oculiv.com)

// your navbar component with menu/button (maybe header.js)

// A scroller function that takes element id and smooth scrolls to it.
const scroll2El = elID => {
    window.scrollTo({
      top: document.getElementById(elID).offsetTop - 60,
      behavior: 'smooth',
    });
  };

const onBtnClick = (e) => {
    e.preventDefault();
    const goto = e.target.getAttribute('goto');
    setTimeout(() => {
      scroll2El(goto);
    }, 100);
  }

...
// goto props should get the same value as the id of the element it scrolls to
<button goto="contacts" onClick={onBtnClick}>Contact</button >
...
// your Contact Component file
// Contact.js

<section id="contacts" />

This solution would work with almost any front-end JS framework/library

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Zoi K.
Solution 2 Eyo Okon Eyo
Solution 3 Dharman
Solution 4 Gaurav