A minimal but complete example that shows how to integrate the Threedy webvis viewer into a React + TypeScript app built with Vite. It demonstrates:
- Loading the webvis script once and exposing it via a React provider (
WebvisGlobalProvider) - Using the webvis web component
<webvis-viewer>inside React with proper TypeScript types - Working with convenient hooks to access the global
webvisobject, contexts, and viewers - Adding a model to a context and toggling viewer settings (e.g. topology mode)
- Navigating between routes while keeping the same named context/viewer
Note: This example targets instant3Dhub and webvis 3.11 (see devDependencies: @types/webvis ^3.11.0).
Make sure you have node 20.19+ or 22.12+ already installed.
- Install dependencies
npm install
- Configure the webvis script URL
- Set the environment variable
VITE_WEBVIS_URLto the URL of yourwebvis.jsserved by your instant3Dhub instance. You can use a.envfile at the project root:VITE_WEBVIS_URL=https://hubdemo.threedy.io/repo/webvis/webvis.js?next
- The exact URL depends on your deployment. The value must point to the
webvis.jsentry script provided by your hub. The example repository already contains an.envwith a demo URL you can replace.
- Run the app
npm run dev- Open the Vite dev server URL printed in the terminal (by default http://localhost:3000).
- Build for production
npm run build- To deploy under a sub-path, pass a base path at build time:
npm run build -- --base=/my/subpath/
- See Vite docs for details: https://vite.dev/guide/build.html#public-base-path
- Preview the production build locally:
npm run preview
This project uses the TypeScript type definitions published on DefinitelyTyped as @types/webvis (see devDependencies). These types expose the global webvis namespace so you can reference APIs and enums without importing anything.
- This package was installed initially with
npm i -D @types/webvis@^3.11.0and is automatically installed in this example. - Usage (global namespace, no import required):
- Interfaces:
webvis.ContextAPI,webvis.ViewerAPI - Enums/constants:
webvis.RenderMode,webvis.Property, etc.
- Interfaces:
- How this example uses them:
package.jsondeclares@types/webvisso the TypeScript compiler understands the webvis API used in hooks and components.- Code references the global types directly (e.g.,
const viewer: webvis.ViewerAPI | undefined).
Note about JSX element typings:
- The DefinitelyTyped package provides the core webvis API typings but does not declare React JSX intrinsic elements for web components.
- This repo therefore adds a small shim at
src/types/webcomponents.tsto type the<webvis-viewer>custom element for React/TSX. - If you need additional webvis elements (e.g.,
<webvis-full>), extend typings similarly as described in the “TypeScript support for webvis web components” section below.
-
Loading webvis
- The
WebvisGlobalProvidercomponent uses a small hook (useLoadWebvis) that injects a<script>tag for the configuredwebvis.jsand tracks loading state and errors. Once the script is loaded, the globalwindow.webvisis available and provided to children via theuseWebvis()hook. - Important: Once loaded into a window, webvis cannot be unloaded. The URL should stay constant for the app lifetime.
- The
-
Global provider and hooks
- The provider:
WebvisGlobalProviderloads thewebvis.jsscript once and exposes the globalwebvisobject. Children render only when webvis is ready (or a fallback is shown while loading). - Viewer element on Home: The Home page renders
<webvis-viewer context="myContext" viewer="myViewer">and attaches theviewerReffromuseWebvisViewer()to it. This ref requests the viewer instance from the element and exposes it asviewer(e.g.,viewer.getID()). The hook also offersremoveViewer()to remove the viewer from its context if needed. - AddModelButton: Uses
useWebvisContext()to obtain the named context (e.g.,"myContext") anduseWebvis()to access enums/constants. On click, it callscontext.add(...)with initial properties (e.g.,[webvis.Property.ENABLED] = true) to load the model once and disables itself when the model is already present. - TopologyToggle: Retrieves the same context via
useWebvisContext()and then waits for the specific viewer usinguseWebvisWaitViewer(context, "myViewer"). With the global constants fromuseWebvis(), it toggles the viewer’s render mode betweenwebvis.RenderMode.Facesandwebvis.RenderMode.FacesTopology. - Additionally available (not used directly on the Home page):
useWebvisCreateViewerlets you create a viewer on a<canvas>programmatically (without the web component).
- The provider:
-
Routes and lifecycle
- The Home page renders a
<webvis-viewer>withcontext="myContext"andviewer="myViewer"and usesuseWebvisViewerto access its instance. - The About page explains the moving parts. When navigating away and back, the same named context/viewer reattach without losing state.
- The Home page renders a
- Variable:
VITE_WEBVIS_URL- Points to the
webvis.jsserved by your instant3Dhub instance. - Example (public demo hub):
VITE_WEBVIS_URL=https://hubdemo.threedy.io/repo/webvis/webvis.js?next
- You can set it via
.env, environment, or CI/CD secrets. In code, the provider reads it usingimport.meta.env.VITE_WEBVIS_URL.
- Points to the
Wrap your app near the root, e.g.:
import { WebvisGlobalProvider } from "./src/components/WebvisGlobalProvider";
const url = import.meta.env.VITE_WEBVIS_URL;
<WebvisGlobalProvider url={url} fallback={<div>Loading webvis…</div>}>
<App />
</WebvisGlobalProvider>React/TS doesn’t know custom elements by default. This project adds a type definition for <webvis-viewer> in src/types/webcomponents.ts by extending the JSX.IntrinsicElements:
- Declares an interface
WebVisViewerElementextendsHTMLElement - Extends
JSX.IntrinsicElementswith'webvis-viewer'so you can write<webvis-viewer … />in TSX without errors - Updates
HTMLElementTagNameMapso element refs are correctly typed
Adding more webvis elements
- Follow the same pattern to add elements like
<webvis-full>. Create a small.d.tsor.tsfile that defines the element interface, extendsJSX.IntrinsicElementsandHTMLElementTagNameMap, and (depending on your JSX runtime) augments the appropriatereact/jsx-runtimeandreactmodules. See the existingsrc/types/webcomponents.tsfor a working example.
-
WebvisGlobalProvider- Props
url: string(required) — Thewebvis.jsURL to loadfallback?: React.ReactNode— Rendered while the script is loading
- Behavior
- Loads the script once; provides webvis to descendants
- Props
-
useWebvis- Signature:
() => typeof window.webvis - Returns the global
webvisobject from context (throws if used outside the provider)
- Signature:
-
useWebvisContext- Signature:
(from?: string) => webvis.ContextAPI | undefined - Gets or requests a named context; returns
undefineduntil ready (it resolves asynchronously in current webvis versions)
- Signature:
-
useWebvisViewer- Signature:
() => { viewer: webvis.ViewerAPI | undefined; viewerRef: (el: HTMLElement | null) => void; removeViewer: () => void } - Attach
viewerRefto a<webvis-viewer>. OnceREADY,vieweris set.removeViewerremoves it from its context.
- Signature:
-
useWebvisWaitViewer- Signature:
(context: webvis.ContextAPI | undefined, id?: string) => webvis.ViewerAPI | undefined - Waits until the viewer with the given id in the context reaches
READY.
- Signature:
-
useWebvisCreateViewer(additional option)- Signature:
(context: webvis.ContextAPI | undefined, viewerId?: string, settings?: Record<webvis.ViewerSettingStrings, unknown>) => { viewer: webvis.ViewerAPI | undefined; viewerRef: (el: HTMLCanvasElement | null) => void } - Lets you create a viewer on a canvas element without using the
<webvis-viewer>custom element.
- Signature:
This example is provided under the terms of the MIT License. See the LICENSE file.
For any report please contact us.