Using Vysmo with Next.js

Vysmo's libraries are framework-free vanilla TS with optional React wrappers. They drop straight into a Next.js app — no Next- specific adapter package needed at v1. This guide covers the common production patterns: image optimization, lazy loading, SSR, and priority hints.

Install

pnpm add @vysmo/slideshow-react @vysmo/transitions @vysmo/animations

@vysmo/slideshow-react is the React component; @vysmo/transitions ships the 60 transition shaders; @vysmo/animations is a transitive peer dep. For a page-flip experience, also add @vysmo/flipbook-react.

Minimal example

A drop-in gallery with WebGL transitions, lazy-loading, and a 1-slide preload window:

// app/gallery/page.tsx
"use client";

import { Slideshow } from "@vysmo/slideshow-react";
import { paintBleed } from "@vysmo/transitions";

const photos = [
  "/photos/01.jpg",
  "/photos/02.jpg",
  "/photos/03.jpg",
];

export default function GalleryPage() {
  return (
    <Slideshow
      slides={photos}
      transition={paintBleed}
      lazy
      preloadWindow={1}
      style={{ width: "100%", aspectRatio: "16 / 9" }}
    />
  );
}

lazy tells the slideshow to only decode the current slide + immediate neighbours, with the runner's GPU cache evicting URLs that scroll out of the window via LRU. preloadWindow=1 is the default; raise it to 2 or 3 for galleries where users rapid-fire navigation is common.

Image optimization via the Next image loader

Next.js's built-in image optimizer accepts any URL and returns responsively-sized AVIF/WebP versions through /_next/image?url=…&w=…&q=…. Vysmo accepts those URLs the same as any other string — pass them straight to the slides array:

// app/gallery/page.tsx
"use client";

import { Slideshow } from "@vysmo/slideshow-react";
import { paintBleed } from "@vysmo/transitions";

const RAW_URLS = [
  "/photos/01.jpg",
  "/photos/02.jpg",
  "/photos/03.jpg",
];

// Build Next.js image-optimizer URLs at the size the slideshow renders.
// Vysmo accepts these URL strings the same as any other.
function optimized(url: string, width = 1600): string {
  return `/_next/image?url=${encodeURIComponent(url)}&w=${width}&q=80`;
}

export default function GalleryPage() {
  const slides = RAW_URLS.map((u) => optimized(u, 1600));
  return (
    <Slideshow
      slides={slides}
      transition={paintBleed}
      lazy
      preloadWindow={1}
      style={{ width: "100%", aspectRatio: "16 / 9" }}
    />
  );
}

For multiple breakpoints, generate URLs at the size your slideshow actually renders at (e.g. 1600px for a hero, 800px for a sidebar gallery). Next's optimizer caches each variant, so regenerating per-render is free after the first hit.

SSR + dynamic import

Vysmo's React components are SSR-safe at module load (no DOM access during import), but the WebGL canvas itself is browser-only. In a Next.js App Router page using server components, wrap with next/dynamic and ssr: false to skip the wasted server render and clean up the hydration payload:

// components/ClientSlideshow.tsx
"use client";

import dynamic from "next/dynamic";

// Render only on the client — WebGL is browser-only. Importing
// @vysmo/slideshow-react itself is SSR-safe (no DOM at module load),
// but mounting it during SSR has no effect anyway. `ssr: false` skips
// the wasted server render entirely.
export const ClientSlideshow = dynamic(
  () => import("@vysmo/slideshow-react").then((m) => m.Slideshow),
  { ssr: false },
);

If your page is already a "use client" component, you can import Slideshow directly — no dynamic import needed.

CORS notes

Images loaded over HTTP fetch+createImageBitmap need correct CORS headers (Access-Control-Allow-Origin) to upload as WebGL textures without tainting the canvas. The Next.js image optimizer handles this for URLs it serves (/_next/image?…). For third-party images you fetch directly, make sure the origin sets the right headers — or proxy through your own Next API route.

What's not in scope at v1

  • A dedicated @vysmo/slideshow-next package — not built yet. The core slideshow + URL inputs covers the common cases. We'll consider a deeper integration package (with next/image as a prop, server-side prefetch, blur-placeholder pipeline) if there's specific demand. Let us know what you need.
  • Blur placeholders during decode. Currently slides render as soon as their texture is uploaded; for the brief gap, the previous slide is visible. A built-in blur placeholder pipeline would need to integrate with next/image's blur data — that's the kind of thing a -next package would do.

See also