Render two isolated instances of threejs application side by side issue

Hello all!

I ran into a problem that I’ve been struggling to solve. Essentially, I have a Three.js application wrapped in a web component and exported as an NPM package. The goal is to use multiple instances of the NPM package in another application. In my app, I’m using Three Fiber for rendering and Zustand for state management.

My issue is that when I update the mesh, for example by applying a new material, and I use invalidate from useThree, the invalidate only triggers a re-render in one instance—the one that rendered first. The second instance isn’t affected.

I’ve tried several alternatives to invalidate, such as using gl.render(scene, camera) or advance(), but nothing changes. Only the first instance updates. From debugging, I can see that the useThree store instances are different, the GL reference is not the same, and the canvases, cameras, and scenes are separate. I also checked the Zustand store, and the instances are different there as well.

Example of what i want to achieve:

Web component:
import React from ‘react’
import * as ReactDOMClient from ‘react-dom/client’
import reactToWebComponent from ‘react-to-webcomponent’
import { ExtendedR2WCOptions } from ‘extended-custom-types/extended-web-component-options’

import App from ‘./App’

export const createWeb3DEngine = (tagName: string = ‘web-3d-engine’) => {
if (!customElements.get(tagName)) {
customElements.define(
tagName,
reactToWebComponent(App, React, ReactDOMClient, {
props: {
…some props
},
} as ExtendedR2WCOptions),
)
}
}

the usage of the app in hosted app:

I wondered may be the issue is with the web component, so I tried to run the two instances uder the same root:

const MultiAppContainer = () => {
return (



<App {…testPropsFromAppLayer} />


<App {…{ …testPropsFromAppLayer }} />


)
}

ReactDOM.createRoot(document.getElementById(‘root’)!).render()

but with no luck.

I created a sandbox app where I reproduced the issue:
https://codesandbox.io/p/sandbox/multiple-views-with-uniform-controls-forked-pd3q8d

Has anyone encountered this issue and can provide some insight into what I can do?

The core issue is that each <Canvas> in React Three Fiber has its own internal store/context. When you call invalidate() from useThree(), it triggers a re-render only in that specific context. If both instances share state somehow or if the “wrong” invalidate function is being called, the second instance won’t update.

Typical fixes:

  1. Ensure Unique Canvas/Context: Each <Canvas> should wrap only its own 3D scene. Then, calls to useThree() within a <Canvas> child will reference that same Canvas’s store.
  2. Check Frameloop Mode: If you’re using frameloop="demand", you must call invalidate() on the correct Canvas store.
  3. Separate State: If you use a global Zustand store for both scenes, ensure the R3F store references remain distinct.
  4. Per-Instance Invalidate: In each scene component, do:
const invalidate = useThree((state) => state.invalidate)
...
invalidate()

so it triggers the correct Canvas’s render loop.

If you place both scenes under one parent <Canvas>, they share the same store, but you must ensure each “view” triggers re-renders correctly.

Thanks for the answer.
But all the steps that you mentioned I already did and verifyed, and still I have the same problem.
In the sandbox link that I attached you can see the example implementation.