Using three.js based library in vite/Rollup - "Multiple instances of three.js being imported"

Hi,

I’m trying to use Vite to build a test project making use of a three.js based library (the library is my own).

My problem is that either I get the warning “Multiple instances of three.js being imported” or “THREE is not defined”, depending on where I import three.

This is more to do with Vite than three.js, but just raising it here in case anyone has had the same problem. I can see similar previous topics, but they don’t seem to be exactly the same as my case:

Here is what I have tried:

  • if I do an import * as THREE from 'three' in both my library and my test app, I get the “multiple instances of Three.js” warning.
  • if I leave out the imports from EITHER the library OR the test app, I get a THREE is not defined error.

Here is my test app:

import * as THREE from 'three';
import * as MyLib from  '../../mylib/lib/main.js';


const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.001, 100);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

window.addEventListener("resize", e => {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
});
const box = new THREE.BoxGeometry(2,2,2);
const cube = new THREE.Mesh(box, new THREE.MeshBasicMaterial({ color: 0xff0000 }));

scene.add(cube);

const mylib = new MyLib(scene, camera);


renderer.setAnimationLoop(animate);

function animate() {
    mylib.update();
    renderer.render(scene, camera);
}

I use a standard Vite config (no changes). package.json of the app is very simple, just specifies three as a dependency and vite as a dev dependency.

The same behaviour occurs when I build my lib as a Rollup bundle through Vite, excluding three.js from the bundle.

Any ideas? It seems to be difficult to only import three.js once; either it’s imported twice or not at all.

EDIT: vite in dev mode does not have the same problem. vite in dev mode uses esbuild, not Rollup - suggesting it’s a Rollup issue specifically.

Sorry if it’s more to do with build tools than three itself, but just asking in case anyone has encountered the same problem.

Thanks.

Depends on how your library imports three.js - since it’s not a separate package, all you’d really need to do is just import * as THREE from 'three'; - no import maps, no scripts, as simple as that.

If your own custom library falls outside of vite’s src - be sure to package it before importing (or just move it inside vite’s src, kinda way simpler.)

If your library would be a separate package, define three as a peerDependency in package.json instead of devDependency / dependency in your library package.

1 Like

Thanks. The library simply imports three using import * as THREE from 'three' as you say. The library is placed in a lib directory which seems to be standard. The eventual intention is to make it available as an npm package but for now I’m just testing it out locally.

Let me try making three a peer dependency and see if that works.

In that case, unless you still need to package the library separately - using peerDependencies in a project with more than a single directory will not do much.

If you plan on having that library as a separate bundle, to test locally you have to put it in a separate directory, with a separate package.json, npm pack it, and then npm install it in the main project. Otherwise you’re doubling dependencies.

2 Likes

I think this should — in general — all be fine. There are some exceptions if you’re using three/webgpu or three/tsl that are still being worked out. Or if you have other dependencies that might pull in a second version of three.js. I don’t see any of that in your sample code but perhaps something in your other source files?

if that’s your lib, not threejs, it should to be within /src, it should not move outside of that directory. as for threejs, it has to be npm installed. this is either a version conflict, some dependency has three inside dependencies in package.json, which is not correct, or maybe it’s a typo somewhere like from "Three", which would pull a separate threejs.

@mjurczyk many thanks. npm pack to create a local package worked, as in, the test app works and does not have the “multiple instances of three” warning.

The “multiple instances of three” warning was due, I believe, to both the test app and the library including import * as THREE from 'three' statements, even though only one version of three was actually installed and the bundle from the library did not contain a duplicate copy of three. For whatever reason (and I have to admit I don’t understand why) Vite in dev mode (using esbuild) could handle this but building for production (using rollup) could not.

1 Like