Next.js / Serverless Script Loading

Hi there - I’m looking for instructions on how to import the Shapediver API and library correctly in a NextJS environment.

I was looking at the react setup instructions in the documentation and noticed that there were errors: react setup - CodeSandbox.

Importing at the top of my React component like so:
import { api } from "@shapediver/viewer";

This results in a next.js error “document is not defined”.

This is probably because the script is attempting to load before the page has been rendered. My current fix for this is to load the script asynchronously like so:

useEffect(() => {
		loadShapeDiver(async () => {
			const SDV = window.SDV;

where loadShapeDiver looks like:

export const loadShapeDiver = (callback) => {
	const existingScript = document.getElementById("shapeDiver");

	if (!existingScript) {
		const script = document.createElement("script");
		script.src = ``; = "shapeDiver";
		script.onload = () => {
			if (callback) callback();
	if (existingScript && callback) callback();

I’d prefer to use the library over this method. What’s the right way to do so?

Thanks so much!

You’re right that the script is trying to reference document before the page is ready. Most likely it’s because some/all of this code is running during server-side rendering, where document isn’t ever going to be available. (This would also explain why the useEffect works)

I think the best way, if you want to use import { api } from "@shapediver/viewer";, is to wrap the component that uses it with nextjs dynamic. This has some tradeoffs, but it at least guarantees everything within that component only ever runs client-side (and has access to document). I have to do this a lot when I use threejs w/ next for the same reasons.

Great call @Chuck_Driesler, dynamic imports solved my problem. Appreciate your help!

For others that may get stuck with this, here’s my code (where ModelViewer is the component that imports the Shapediver library):

import dynamic from "next/dynamic";

const ModelViewer = dynamic(() =>
	import("./ModelViewer", {
		ssr: false,

function Model() {
	return (
			<ModelViewer />

export default Model;

It would be great to add this to the installation documentation!