javascriptreactjsnext.jsreact-hooksgetelementsbyclassname

error ReferenceError: document is not defined and cannot read properties of undefined (reading 'style') (NextJS Custom Carousel bug)


I created a custom carousel component that displays text testimonies. This custom carousel is made from scratch which uses a custom script functionality (carouselFunction.js) for its arrow navigations to traverse to each text testimony displayed. The carousel looks exactly like this:

Custom Carousel

I have an issue in regards to my custom carousel in NextJS. I get a ReferenceError: document is not defined within my carouselFunction.js file (a JS file for the carousel functionality), and I am not sure why it raises that error since I'm only getting the class name "textimony" of the div of my custom carousel component (Carousel.tsx), and assign that class name to slides variable, which is going to be used within carouselFunction.js.

source code of carouselFunction.js:

let slideIndex = 0;
showSlides(slideIndex);

export function changeSlides(n) {
  showSlides((slideIndex += n));
}

export function showSlides(n) {
  let i;
  let slides = document.getElementsByClassName("textimony");

  n > slides.length - 1
    ? (slideIndex = 1)
    : n < 0
    ? (slideIndex = slides.length - 1)
    : null;

  for (i = 0; i < slides.length; i++) {
    slides[i].style.display = "none";
  }
  slides[slideIndex].style.display = "flex";
}

source code of Carousel.tsx:

"use client";
import React from "react";
import Image from "next/image";
import "/app/globals.css";
import Script from "next/script";
import { changeSlides } from "../Constants/Carousel_Function/carouselFunction.js";
import leftArrow from "../resources/Shape Elements/left-arrow.webp";
import rightArrow from "../resources/Shape Elements/right-arrow.webp";

const SaliyabCarousel = () => {
  return (
    <div>
      <div className="textimony">
        <a className="left-arrow" onClick={() => changeSlides(-1)}>
          <Image src="" width="107" height="104" alt="left arrow pic" />
        </a>
        <q>
          Saliyab&apos;s workshop was a game-changer for me. The insights and
          practical advice I gained from the experts in the field helped me to
          take my skills to the next level and make a real impact in my work. I
          highly recommend it!
        </q>
        <a className="right-arrow" onClick={() => changeSlides(1)}>
          <Image
            className="right-arrow"
            src={rightArrow}
            width="106"
            height="104"
            alt="right arrow pic"
          />
        </a>
      </div>

      <div className="textimony">
        <a className="left-arrow" onClick={() => changeSlides(-1)}>
          <Image
            src={leftArrow}
            width="107"
            height="104"
            alt="left arrow pic"
          />
        </a>
        <q>
          I&apos;ve attended several workshops in the past, but Saliyab&apos;s
          was by far the most informative and engaging. The speakers were
          knowledgeable and approachable, and I left feeling motivated and
          inspired to pursue new opportunities in the tech industry.
        </q>
        <a className="right-arrow" onClick={() => changeSlides(1)}>
          <Image
            className="right-arrow"
            src={rightArrow}
            width="106"
            height="104"
            alt="right arrow pic"
          />
        </a>
      </div>

      <div className="textimony">
        <a className="left-arrow" onClick={() => changeSlides(-1)}>
          <Image
            src={leftArrow}
            width="107"
            height="104"
            alt="left arrow pic"
          />
        </a>
        <q>
          The Saliyab workshop provided me with a wealth of new information and
          resources that I could immediately apply to my work. I appreciated the
          hands-on approach and the chance to network with other professionals
          in the field.
        </q>
        <a className="right-arrow" onClick={() => changeSlides(1)}>
          <Image
            className="right-arrow"
            src={rightArrow}
            width="106"
            height="104"
            alt="right arrow pic"
          />
        </a>
      </div>

      <div className="textimony">
        <a className="left-arrow" onClick={() => changeSlides(-1)}>
          <Image
            src={leftArrow}
            width="107"
            height="104"
            alt="left arrow pic"
          />
        </a>
        <q>
          As a beginner in the tech industry, I was a bit intimidated to attend
          the Saliyab workshop. But I&apos;m so glad I did! The workshop was
          tailored to all skill levels and I left with a much deeper
          understanding of the field and its possibilities.
        </q>
        <a className="right-arrow" onClick={() => changeSlides(1)}>
          <Image
            className="right-arrow"
            src={rightArrow}
            width="106"
            height="104"
            alt="right arrow pic"
          />
        </a>
      </div>
      <Script src="../Constants/Carousel_Function/carouselFunction.js" />
    </div>
  );
};

export default SaliyabCarousel;

ReferenceError Pic: ReferenceErrorpic

Not only that, but I also got a TypeError: Cannot read properties of undefined (reading 'style'), which is I think due to the ReferenceError above.

TypeError pic: TypeErrorPic

I already called the Script of the carouselFunction.js on my layout page but the error still persists.

source code of layout.tsx:

import { Inter } from "next/font/google";
import Script from "next/script";

const inter = Inter({ subsets: ["latin"] });

export const metadata = {
  title: "Saliyab Website",
  description: "Saliyab Website using NextJS",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <meta charSet="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <link rel="preconnect" href="https://fonts.googleapis.com" />
      <link
        rel="preconnect"
        href="https://fonts.gstatic.com"
        crossOrigin="anonymous"
      />
      <link
        href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap"
        rel="stylesheet"
      />
      <Script src="../Constants/Carousel_Function/carouselFunction.js" />

      <body className={inter.className}>{children}</body>
    </html>
  );
}

I am still learning the ropes of NextJS and it would be of great help to acquire guides and feedbacks in creating my custom carousel in NextJS. I am aware of the existing carousels from component libraries such as Material UI, etc. But I just really wanted to try creating a customized version of mine.

Your responses would indeed help me a lot in finishing this task of mine. Thank you!


Solution

  • Client components pre-render on the server, so they don't have access to the document. There are several ways to resolve this error:

    1. Access the document inside useEffect once it is loaded
    2. Check if window is undefined before accessing the document.

    For the first method, you could convert carouselFunction.js to a custom hook using useEffect or for the second method, perform the check in carouselFunction.js before accessing the document.

    For more details, see these posts, which contain extensive examples of both methods:

    ReferenceError: document is not defined inside Next.js client component

    Next.js: document is not defined