React & Next.js

Embed the Meshless 360 viewer in React and Next.js with the @meshless/react wrapper — install, SSR-safe usage, and every prop.

@meshless/react is a thin React wrapper around the Meshless embed. You pass a projectId and get an interactive, cookieless 360° viewer that streams pre-rendered frames from the Meshless CDN — no Three.js, no WebGL, no client 3D code.

1. Install

npm install @meshless/react
# or: pnpm add @meshless/react
# or: yarn add @meshless/react

The package ships ESM + CJS builds and its own TypeScript types. Its only peer dependency is react (18 or 19).

2. Render the viewer

import { MeshlessViewer } from "@meshless/react";

export function ProductViewer() {
  return (
    <MeshlessViewer
      projectId="YOUR_PROJECT_ID"
      autoplay
      showHotspots
      style={{ width: "100%", aspectRatio: "1" }}
    />
  );
}

Under the hood the component renders a <div class="meshless-viewer" data-project-id="…"> and injects the embed script from https://cdn.meshless.io/embed/v1/embed.js once per page. The script then hydrates every viewer on the page.

3. Next.js (App Router)

MeshlessViewer is SSR-safe: on the server it renders the plain wrapper div and defers script injection to a client useEffect. Because it uses a hook, render it inside a Client Component — add "use client" at the top of the file that uses it:

"use client";

import { MeshlessViewer } from "@meshless/react";

export function HeroViewer({ projectId }: { projectId: string }) {
  return (
    <MeshlessViewer
      projectId={projectId}
      autoplay
      autorotateSpeed={8}
      style={{ width: "100%", aspectRatio: "4 / 3" }}
    />
  );
}

You can then use <HeroViewer /> from any Server Component. No next/dynamic and no ssr: false workaround is needed — the viewer simply mounts on the client.

4. Next.js (Pages Router) & other setups

In the Pages Router (or Vite, CRA, Remix, etc.) just import and render the component — it works anywhere React runs. There is nothing server-specific to configure.

Props

Only projectId is required. Every omitted prop falls back to the viewer defaults you saved for the project in the dashboard.

PropTypeDescription
projectIdstringRequired. The Meshless project id.
bgColorstring | nullAny CSS colour for the stage background.
hideControlsbooleanHide the on-screen rotation controls.
showHotspotsbooleanShow the project's hotspots (default true).
fullscreenbooleanShow a fullscreen toggle.
autoplaybooleanStart auto-rotating on load.
autorotateSpeednumberFrames per second while auto-rotating (default 10).
autorotateDirection'cw' | 'ccw'Rotation direction (default 'cw').
autorotatePauseOnHoverbooleanPause while hovered (default true).
autorotateResumeDelaynumberMs after interaction before resuming.
sensitivitynumberHorizontal drag: pixels per frame step.
verticalSensitivitynumberVertical drag: pixels per row step.
invertX / invertYbooleanInvert drag directions.
momentumbooleanVelocity-based deceleration after release.
momentumFrictionnumber0–1 deceleration per animation frame.
wheelbooleanMouse-wheel rotation (default true).
keyboardbooleanArrow-key navigation (default true).
initialFrame / initialRownumberStarting frame / row indices.
className / styleReact standardApplied to the wrapper div.

Requirements

  • The project must be Ready (frames encoded and uploaded).
  • If your workspace uses a domain allowlist, the page's origin must be allowed (Dashboard → Domains). Otherwise the viewer will refuse to load.

Need full control?

If you host your own frame sequences (for example, exported with the Meshless converter) and want a fully controlled component with hotspot editing, colorways, and lazy loading, use @meshless/viewer instead of this wrapper.

What's next?

  • Embed Options — every data-* attribute the props map to
  • JavaScript SDK — control the viewer at runtime and listen for events
  • Variants — publish alternate renders for colors and finishes