LISN.js

Edit on StackBlitz

"use client";
import { useRef, useEffect } from "react";

import {
  useScrollbar,
  PagerComponent,
  PagerPageComponent,
  PagerSwitchComponent,
  PagerComponentRef,
} from "@lisn.js/react";
import "lisn.js/pager.css";
import "lisn.js/scrollbar.css";

import Image from "next/image";

import styles from "./demo.module.css";

const images = Array.from(
  { length: 8 },
  (e, i) => `https://picsum.photos/600/300?p=${i}`,
);

export default function Page() {
  useScrollbar({ useHandle: true });

  const mainPagerRef = useRef<PagerComponentRef>(null);
  const thumbPagerRef = useRef<PagerComponentRef>(null);

  useEffect(() => {
    const mainPager = mainPagerRef.current?.getWidget();
    const thumbPager = thumbPagerRef.current?.getWidget();
    if (mainPager && thumbPager) {
      thumbPager.onTransition(() =>
        mainPager.goToPage(thumbPager.getCurrentPageNum()),
      );
    }
  }, []);

  return (
    <>
      <div className={styles.wrapper}>
        <PagerComponent
          widgetRef={mainPagerRef}
          className={styles.preview}
          config={{ style: "tabs", useGestures: false }}
        >
          {
            // the array is constant so we can use idx as the key
            images.map((url, idx) => {
              return (
                <PagerPageComponent key={idx}>
                  <Image src={url} alt="" />
                </PagerPageComponent>
              );
            })
          }
        </PagerComponent>

        <PagerComponent
          className={styles.slider}
          widgetRef={thumbPagerRef}
          config={{
            style: "carousel",
            peek: true,
            pageSize: 150,
            horizontal: true,
            parallax: true,
            useGestures: "wheel,touch",
          }}
        >
          {
            // the array is constant so we can use idx as the key
            images.map((url, idx) => {
              return (
                <PagerPageComponent key={idx}>
                  <PagerSwitchComponent>
                    <Image src={url} alt="" />
                  </PagerSwitchComponent>
                </PagerPageComponent>
              );
            })
          }
        </PagerComponent>
      </div>
    </>
  );
}
/* Container */
.wrapper {
  --animate-duration: 0.4s;
  width: 600px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100dvh;
}

/* Main preview pager */
.preview {
  height: 300px;
}

.slider {
  --lisn-pager--gap: 10px;
}

.preview img,
.slider img {
  width: 100%;
}

.slider img {
  cursor: pointer;
}

Edit on CodePen

document.addEventListener("DOMContentLoaded", () => {
  const main = document.getElementById("demo");
  const mainPagerEl = demo.querySelector(".preview");
  const thumbPagerEl = demo.querySelector(".slider");

  const mainPager = new LISN.widgets.Pager(mainPagerEl, {
    pages: mainPagerEl.querySelectorAll(".page"),
    style: "tabs",
    useGestures: false,
  });

  const thumbnailEls = thumbPagerEl.querySelectorAll(".page");
  const thumbPager = new LISN.widgets.Pager(thumbPagerEl, {
    pages: thumbnailEls,
    switches: thumbnailEls,
    style: "carousel",
    peek: true,
    pageSize: 150,
    horizontal: true,
    parallax: true,
    useGestures: "wheel,touch",
  });

  thumbPager.onTransition(() =>
    mainPager.goToPage(thumbPager.getCurrentPageNum()),
  );
});
<div id="demo">
  <div class="preview">
    <div class="page">
      <img src="https://picsum.photos/600/300?p=1" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=2" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=3" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=4" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=5" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=6" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=7" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=8" />
    </div>
  </div>

  <div class="slider">
    <div class="page">
      <img src="https://picsum.photos/600/300?p=1" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=2" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=3" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=4" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=5" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=6" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=7" />
    </div>

    <div class="page">
      <img src="https://picsum.photos/600/300?p=8" />
    </div>
  </div>
</div>
#demo {
  width: 600px;
  margin: 0 auto;
}

#demo .preview {
  height: 300px;
}

#demo .slider {
  --lisn-pager--gap: 10px;
}

#demo .preview img,
#demo .slider img {
  width: 100%;
}

#demo .slider img {
  cursor: pointer;
}