@sushindustries/react-product-viewer

0.1.0

A React Three Fiber viewer for a single GLB product: orbit, contact shadow, runtime material variants, geometric zone tinting, and a PNG snapshot.

pnpm add @sushindustries/react-product-viewer
2 min read
View as Markdown

The React adapter for @sushindustries/product-viewer@sushindustries/product-viewerRuntime material swapping and geometric zone tinting for a single GLB product. Framework-free.package. The core package holds everything that is just three.js; this holds the component and the hooks.

Install

pnpm add @sushindustries/react-product-viewer three @react-three/fiber @react-three/drei three-stdlib
shell

Use

import { lazy, Suspense } from "react";

// Lazy, always. three and R3F are ~600 kB and cannot run on a server.
const ProductViewer = lazy(
	() => import("@sushindustries/react-product-viewer"),
);

export function Page() {
	return (
		<Suspense fallback={null}>
			<ProductViewer
				model={{ url: "/models/chair.glb", realLength: 0.9 }}
				variants={["walnut"]}
				groundBound
			/>
		</Suspense>
	);
}
tsx

ProductViewer is exported both by name and as the default. The default is not a style choice — React.lazy requires one.

Two TanStack integrations, both optional

Separate entry points, because both their peers are optional too:

// TanStack Query owns the asset, so a route loader can preload it —
// on hover, under defaultPreload: "intent" — and eviction frees the GPU.
import { productModelOptions } from "@sushindustries/react-product-viewer/query";

// TanStack Router owns the selection, so a configured product is a URL
// somebody can send.
import { useVariantSearch } from "@sushindustries/react-product-viewer/router";
ts

The split matters. If Query owns the GLB, the model is already in memory before the route renders, and the cache eviction you already configured is what frees video memory. If Router owns the selection, "the chair in walnut" is a link.

Styles

import "@sushindustries/react-product-viewer/styles.css";
ts

Class names are prefixed pv-. Pass className to add your own.

Components

ExportDoes
ProductViewerthe canvas, the lighting, the orbit controls
ProductModeljust the model, for when you own the canvas
ProductHero / HeroInteractivea hero that stays still until interacted with
ViewIn3D / useViewIn3Dprogressive enhancement from an image to the viewer
VariantButton / useVariantSwatchesswatches driven by the model's own variants
ProductModelJsonLd3DModel structured data

ViewIn3D is the one worth reading twice: it renders an image, and only pulls the 3D bundle when someone asks for it. Most visitors never do.