Reveal 3D Viewer
Add a Cognite Reveal 3D viewer to a Flows app using the published @cognite/reveal-widget npm package. Renders CAD models, point clouds, 360° image collections, and CDF scenes from CDF, with model browsing or direct model/revision IDs.
DM instance to visualize: $ARGUMENTS
Use This When
The user wants to embed an interactive Cognite Reveal viewer for CDF 3D content in a Flows app.
Do not use this skill for static diagrams, graph visualizations, or unrelated custom Three.js scenes.
Do not use the deprecated app-local "copy the bundle" approach — that pattern (a src/features/reveal-3d/ folder of copied provider/hook source) is replaced by installing @cognite/reveal-widget directly. If an app still has a copied bundle from a prior integration, migrate it to this package rather than extending it.
Prerequisites
- The app uses React + TypeScript and is wrapped in
@cognite/app-sdk'sCogniteSdkProvider(Flows auth), which supplies theCogniteClient(sdk) viauseCogniteSdk()from@cognite/app-sdk/react.@cognite/duneis the CLI used to scaffold/deploy the app, not the runtime auth library — apps created withnpx @cognite/dune apps createdepend on@cognite/app-sdkfor this, not@cognite/duneitself (theuseDune()/@cognite/dune/authhook only exists for legacy--classicscaffolds). - The CDF project has 3D models, or the user has supplied direct model/revision IDs or a CDM (
externalId/space) model reference. - For DM-linked 3D, the instance/model must be identifiable via a CDM
externalId/spacepair or a classicmodelId/revisionId; instance highlighting works once a model is loaded and the instance is contextualized (mapped) to it. - Determine whether the project's 3D content lives in the Core Data Model or the classic 3D API before setting
viewerOptions.useCoreDm— don't default it totrue. There's no single flag for this; csp-and-fixes.md gives the exact SDK calls to check directly.
Integration Workflow
Follow these steps in order. Adapt paths to the target app's conventions instead of inventing new ones.
- Inspect the target app. Read
package.json,vite.config.ts,src/main.tsx, and the app's folder/alias conventions. - Install the package and peers with the app's package manager. See Dependencies. Reuse existing pinned React and SDK versions where they satisfy the peer ranges.
- Configure Vite. Read vite-config.md and add the
three/@cognite/revealdedupe entry. No process/util/assert polyfills are needed — the package ships browser-ready. - Configure
manifest.json's CSP allowances for whatever the scene/model actually contains (scene ground-plane/skybox textures, 360° image collections). Read csp-and-fixes.md — it also covers the app-side fix point clouds need (manifest.jsoncan't grant it directly) and a StrictMode gotcha, so read it even if the app has no scenes/360 content yet. - Add a controller class that wraps
RevealWidgetControllerand drives it imperatively (load resources, style/highlight instances, control the camera) from your own event handlers — not fromuseEffectreacting to prop changes. See implementation.md. - Mount
RevealWidgetwithviewerOptions={{ sdk, useCoreDm }}(setuseCoreDmper the project, not hardcoded — see csp-and-fixes.md) andsetControllerRefinside a container with an explicit height.RevealWidgetmanages its own internal Reveal context — do not wrap it in another provider from this package. - Choose the resource pattern. Use the model-browser pattern (
sdk.models3D.list()+ classicmodelId/revisionId) as the default unless the user has already supplied a CDMexternalId/spacemodel reference. Full examples in implementation.md. - Clean up. Call
.remove()on anyReveal3DResourceHandlereturned byaddResourcewhen it's no longer needed (selection change, unmount). - Run typecheck and build (
tsc --noEmit,pnpm build, etc.) and fix any dependency/peer-version issues.
Minimal Example
import { useRef } from 'react';
import type { CogniteClient } from '@cognite/sdk';
import {
RevealWidget,
type Reveal3DResourceHandle,
type RevealWidgetController,
type ThreeDResourceIdentifier,
} from '@cognite/reveal-widget';
class ThreeDViewerController {
private model: Reveal3DResourceHandle | undefined;
constructor(private readonly widgetController: RevealWidgetController) {}
async loadModel(resource: ThreeDResourceIdentifier): Promise<void> {
this.model = await this.widgetController.addResource(resource);
this.widgetController.cameraController.focusModel(this.model);
}
dispose(): void {
this.model?.remove();
}
}
export function ViewerPage({
sdk,
resource,
}: {
sdk: CogniteClient;
resource: ThreeDResourceIdentifier;
}) {
const viewerRef = useRef<ThreeDViewerController>();
function handleWidgetController(widgetController: RevealWidgetController | undefined) {
viewerRef.current?.dispose();
if (widgetController === undefined) {
viewerRef.current = undefined;
return;
}
const viewer = new ThreeDViewerController(widgetController);
void viewer.loadModel(resource);
viewerRef.current = viewer;
}
return (
<div style={{ width: '100%', height: '70vh', position: 'relative' }}>
<RevealWidget
viewerOptions={{ sdk, useCoreDm }} // set per the project — see csp-and-fixes.md
setControllerRef={handleWidgetController}
/>
</div>
);
}
Dependencies
Suggested versions are starting points. If the target app already pins compatible versions, defer to the app.
| Package | Suggested version | Purpose |
|---|---|---|
@cognite/reveal-widget |
^0.1.0 |
The RevealWidget component and its types |
react / react-dom |
^18.3.1 (peer) |
UI framework — peer dependency, must match the app |
@cognite/reveal |
4.35.3 (peer) |
Reveal viewer runtime — peer dependency, exact match required |
@cognite/sdk |
^10.13.0 (peer) |
CDF API client — peer dependency |
Everything else (three, @tanstack/react-query, @base-ui/react, @floating-ui/react, @tabler/icons-react, dayjs, lodash-es, ml-matrix, random-seed, @cognite/aura) is a transitive dependency of the package and installs automatically — do not add it manually unless the app needs to pin a version, or unless app code imports from it directly. The model-browser pattern in implementation.md does exactly that with @tanstack/react-query (useInfiniteQuery/useQuery) — add it as a direct dependency in that case, since importing from an undeclared transitive dependency breaks under strict package managers like pnpm.
Example install (pnpm; adapt to the app's package manager):
pnpm add @cognite/reveal-widget @cognite/reveal @cognite/sdk react react-dom
After install, check that the app's @cognite/reveal and react/react-dom versions satisfy the package's peer ranges (@cognite/reveal requires an exact 4.35.3 match).
Do not copy any source bundle into the app and do not install process, util, assert, ajv, or vite-plugin-node-polyfills for this package — none of that is needed.
Critical Rules
- Drive the widget imperatively through
RevealWidgetController, obtained viasetControllerRef. Don't try to reconstruct Reveal's old declarative provider tree (CacheProvider/RevealProvider/RevealCanvas/Reveal3DResources) — that API belongs to the old copied-bundle approach and is not what this package exposes. RevealWidgetwraps its own Reveal context internally — never nest it inside another provider from this package.- Dispose the previous controller class instance (
.dispose()calling.remove()on tracked handles) insidesetControllerRefbefore constructing a new one, and again whenwidgetControllerbecomesundefined(unmount). - Resources passed to
addResourcemust match the exact identifier shape for theirtype/sourceTypecombination (see implementation.md) — mixing classic and CDM fields is a type error. - Instance highlighting only affects instances that are already contextualized (mapped) to a loaded model; load the model first, then call
styleByInstance/focusInstances. RevealWidget's container must have an explicit height — it fills its parent.- Lazy-load canvas-heavy viewer content with
React.lazy+Suspensewhen adding a route/page. useCoreDmmust match the project, not default totrue— wrong 401s and silent 360-collection failures otherwise. Don't wrap the app inReact.StrictMode— it tears downRevealWidget's viewer mid-load in dev and produces errors that don't occur in production. Point clouds need an app-side same-origin fix, sincemanifest.jsoncan't grant thedata:CSP allowance they'd otherwise need. All three: see csp-and-fixes.md.
Advanced Reference
For the full resource-identifier catalog (CAD, point cloud, 360 images, scenes), instance highlighting, and camera control, read implementation.md.
For Vite/dedupe configuration, read vite-config.md.
For CSP/manifest.json allowances, the useCoreDm/StrictMode gotchas, the point-cloud app-side fix, and 360-collection troubleshooting, read csp-and-fixes.md.
Verification Checklist
-
@cognite/reveal-widgetis installed alongside its peers (react,react-dom,@cognite/reveal,@cognite/sdk) at compatible versions. - No source bundle was copied into the app; all imports come from
@cognite/reveal-widget. -
vite.config.tsincludesresolve.dedupe: ['three', '@cognite/reveal'](plus the app's existing dedupe entries). - No
process/util/assertpolyfills orvite-plugin-node-polyfillswere added for this package. -
RevealWidgetis mounted once, is not nested in another Reveal provider, and its container has an explicit height. -
viewerOptions.useCoreDmmatches whether the target project is actually Core-Data-Model-based. - The app does not wrap itself in
React.StrictMode. -
manifest.jsongrantsimg-srcforhttps://*.cognitedata.comif the app loads scenes with ground planes/skybox, andconnect-srcfor the actual signed-URL host observed from a CSP violation if it loads 360° image collections. If the app needs point cloud support, the same-originBlob-patch fix has been applied and verified. - A controller class wraps
RevealWidgetController, obtained viasetControllerRef, and drivesaddResource/styleByInstance/focusInstances/cameraControllerimperatively. - The controller class is disposed (and tracked resource handles
.remove()d) both when a new controller is set and on unmount (widgetController === undefined). - Resource identifiers use the correct
type/sourceTypeshape for the model being loaded. - Typecheck and build pass.