MUST be used whenever integrating CogniteFileViewer into a Flows app to preview CDF files (PDFs, images, text). Do NOT manually wire up react-pdf or file…
Integrate CogniteFileViewer
Add CogniteFileViewer to this Flows app to preview CDF files (PDF, image, text).
Dependencies
The file-viewer library files (copied in Step 2) require this npm package:
Package
Version
react-pdf
^9.1.1
pdfjs-dist ships as a dependency of react-pdf at the correct version — do not install it separately.
react and @cognite/sdk are assumed to already be present in Flows apps.
Your job
Complete these steps in order. Read each file before modifying it.
Step 1 — Understand the app
Read these files before touching anything:
package.json — detect package manager (packageManager field or lock file) and existing deps
vite.config.ts — understand current Vite setup
The component where the viewer should be added
Step 2 — Copy the file-viewer source files
The file-viewer library lives in the code/ directory next to this skill file. Read and copy
all files from there into src/cognite-file-viewer/ inside the app:
code/types.ts
code/mimeTypes.ts
code/fileResolution.ts
code/useViewport.ts
code/useFileResolver.ts
code/useDocumentAnnotations.ts
code/DocumentAnnotationOverlay.tsx
code/CogniteFileViewer.tsx
code/index.ts
The PDF.js worker is configured inside CogniteFileViewer.tsx — no separate consumer setup is needed.
Step 3 — Install dependencies
Install react-pdf (see Dependencies above) using the app's package manager:
pnpm → pnpm add react-pdf@^9.1.1
npm → npm install react-pdf@^9.1.1
yarn → yarn add react-pdf@^9.1.1
pnpm users: pnpm's strict linking may prevent the browser from resolving pdfjs-dist. Either add pdfjs-dist as a direct dependency (pnpm add pdfjs-dist), or add public-hoist-pattern[]=pdfjs-dist to .npmrc.
Step 4 — Configure Vite
Add optimizeDeps.exclude: ['pdfjs-dist'] to vite.config.ts to prevent Vite from pre-bundling pdfjs-dist (which breaks the worker):
export default defineConfig({
// ... existing config ...
optimizeDeps: {
exclude: ['pdfjs-dist'],
},
});
Step 5 — Use the component
Import and render CogniteFileViewer from the locally copied files:
import { CogniteFileViewer } from './cognite-file-viewer';
Get the sdk from the useDune() hook (already available in every Flows app):
import { useDune } from '@cognite/dune';
const { sdk } = useDune();
Supported file types
Type
Formats
PDF
.pdf — page navigation, zoom, pan, diagram annotation overlay
Office documents
Word, PowerPoint, Excel, ODS, ODP, ODT, RTF, TSV — converted to PDF via the CDF Document Preview API, then rendered identically to PDF
Image
JPEG, PNG, WebP, SVG, TIFF — zoom, pan, rotation
Text
.txt, .csv, .json — rendered as preformatted text
Other
Falls back to renderUnsupported
Minimal usage
This is all you need — zoom, pan, and touch gestures are handled internally:
<CogniteFileViewer
source={{ type: 'internalId', id: file.id }}
client={sdk}
style={{ width: '100%', height: '600px' }}
/>
The component needs a defined height. If the parent has no explicit height, the viewer will collapse to zero. Always set a height via style, className, or the parent container.
File source
Pass any of three source types:
// By instance ID (data-modelled file — enables annotations)
<CogniteFileViewer
source={{ type: 'instanceId', space: 'my-space', externalId: 'my-file' }}
client={sdk}
/>
// By CDF internal ID
<CogniteFileViewer
source={{ type: 'internalId', id: 12345 }}
client={sdk}
/>
// By direct URL
<CogniteFileViewer
source={{ type: 'url', url: 'https://...', mimeType: 'application/pdf' }}
/>
Prefer instanceId when available — it's the only source type that enables the diagram annotation overlay. When listing files via sdk.files.list(), check file.instanceId first:
source={
file.instanceId
? { type: 'instanceId', space: file.instanceId.space, externalId: file.instanceId.externalId }
: { type: 'internalId', id: file.id }
}
Full props reference
<CogniteFileViewer
// Required
source={source}
client={sdk} // required for instanceId and internalId sources
// PDF pagination
page={page} // controlled current page (1-indexed)
onPageChange={setPage}
onDocumentLoad={({ numPages }) => setNumPages(numPages)}
// Zoom & pan (works on PDF and images)
zoom={zoom} // 1 = 100%; Ctrl/Cmd+wheel, pinch-to-zoom, and middle-click drag built in
onZoomChange={setZoom}
minZoom={0.25} // default
maxZoom={5} // default
panOffset={pan} // controlled pan offset; resets on page change
onPanChange={setPan}
// Fit mode
fitMode="width" // 'width' fits to container width; 'page' fits entire page in container
// Rotation (PDFs and images)
rotation={rotation} // 0 | 90 | 180 | 270
// Diagram annotations (instanceId sources only)
showAnnotations={true} // default
onAnnotationClick={(annotation) => { /* annotation.linkedResource has space + externalId */ }}
onAnnotationHover={(annotation) => {}}
// Custom annotation tooltip (replaces native <title> tooltip)
renderAnnotationTooltip={(annotation, rect) => (
<div style={{
position: 'absolute',
left: rect.x + rect.width,
top: rect.y,
zIndex: 11,
}}>
{annotation.text}
</div>
)}
// Custom overlay (SVG paths, highlights, drawings — works on PDF and images)
renderOverlay={({ width, height, originalWidth, originalHeight, pageNumber, rotation }) => (
<svg
width={width}
height={height}
viewBox={`0 0 ${originalWidth} ${originalHeight}`}
preserveAspectRatio="none"
style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'all' }}
>
<path d="..." stroke="cyan" fill="none" />
</svg>
)}
// Custom renderers (all optional)
renderLoading={() => <MySpinner />}
renderError={(error) => <MyError message={error.message} />}
renderUnsupported={(mimeType) => <div>Cannot preview {mimeType}</div>}
// Layout
className="..."
style={{ width: '100%', height: '100%' }}
/>
Tips & tricks
Reset page, zoom and rotation when the source changes.
The component does not reset these automatically when you switch files — do it yourself:
const navigateToFile = (file: FileInfo) => {
setSelectedFile(file);
setPage(1);
setZoom(1);
setRotation(0);
};
Gate pagination UI on numPages > 0.
onDocumentLoad only fires for PDFs. Don't render pagination controls until you know there are pages to paginate:
{numPages > 0 && (
<>
<button disabled={page <= 1} onClick={() => setPage(p => p - 1)}>‹</button>
<span>{page} / {numPages}</span>
<button disabled={page >= numPages} onClick={() => setPage(p => p + 1)}>›</button>
</>
)}
Annotation click → navigate to linked file.
annotation.linkedResource contains the space and externalId of the linked CDF instance. Match it against file.instanceId to navigate:
onAnnotationClick={(annotation) => {
if (!annotation.linkedResource) return;
const { space, externalId } = annotation.linkedResource;
const linked = files.find(
f => f.instanceId?.space === space && f.instanceId?.externalId === externalId
);
if (linked) navigateToFile(linked);
}}
Touch support is built in. Two-finger pinch-to-zoom and two-finger drag-to-pan work on touch devices automatically. No configuration needed.
Pan is middle-click drag (when zoomed in) on desktop. Left-click remains free for annotation clicks and text selection.
Ctrl/Cmd + wheel zooms toward the cursor — also built in. Wire zoom/onZoomChange if you want programmatic zoom buttons or to persist zoom state; otherwise it works fully uncontrolled.
renderOverlay receives original page dimensions (originalWidth, originalHeight) so you can set up an SVG viewBox in the original coordinate space. Paths drawn in PDF-point or image-pixel coordinates will map correctly to the rendered page at any zoom level.
Common pitfalls
Problem
Cause
Fix
Failed to resolve module specifier 'pdf.worker.mjs'
pdfjs-dist not hoisted (pnpm)
Add public-hoist-pattern[]=pdfjs-dist to .npmrc, or pnpm add pdfjs-dist directly
API version does not match Worker version
pdfjs-dist version mismatch between app and react-pdf
Do not install pdfjs-dist separately — let react-pdf provide it. If already installed, remove it
Annotations never show
instanceId is undefined — annotation overlay is disabled without it
Use instanceId source, or fall back and accept no annotations for classic files
Annotations show but are empty
File has no CogniteDiagramAnnotation edges in CDF
Expected — only P&ID/diagram files synced to the data model have annotations
Viewer collapses to zero height
Parent has no explicit height
Set height via style, className, or parent CSSdon't have the plugin yet? install it then click "run inline in claude" again.
added explicit decision points for pnpm hoisting and file source selection, documented all inputs including package manager detection and sdk hook requirement, defined output contract with file type support matrix and gesture handling, clarified outcome signals and common pitfalls with edge case handling.
add CogniteFileViewer to your Flows app to render CDF files (PDF, image, text, Office documents) with zoom, pan, rotation, touch gestures, and diagram annotation overlays. use this skill whenever you need in-app file preview without manually wiring react-pdf or managing pdfjs-dist workers. the component handles all rendering logic and gesture detection internally.
npm package
sdk client
useDune() hook from @cognite/dune to access the sdk instance for file resolutionfile source (pick one)
vite configuration
library files to copy
read the app structure: open package.json (detect package manager via packageManager field or lock file presence), vite.config.ts (understand current Vite setup), and the component file where the viewer will live. do not modify anything yet.
copy library files: read all 9 files from the skill's code/ directory, then copy them into src/cognite-file-viewer/ inside the app. the PDF.js worker is already configured inside CogniteFileViewer.tsx; no separate setup needed.
install react-pdf: run the install command for your detected package manager.
pnpm add react-pdf@^9.1.1npm install react-pdf@^9.1.1yarn add react-pdf@^9.1.1configure vite: open vite.config.ts and add optimizeDeps: { exclude: ['pdfjs-dist'] } to the defineConfig export. this prevents Vite from pre-bundling pdfjs-dist, which breaks the worker thread.
import and render: in the component file, import CogniteFileViewer from './cognite-file-viewer', get the sdk from useDune(), and render the component with required props (source, client, style with explicit height). start with minimal props: source, client, and style.
pnpm strict hoisting issue: if using pnpm and the browser throws "failed to resolve module specifier 'pdf.worker.mjs'", do one of these:
public-hoist-pattern[]=pdfjs-dist to .npmrc, orpnpm add pdfjs-dist directly to hoist it as an explicit dependencyfile source choice: when passing source prop, prefer instanceId over internalId if available, because only instanceId enables the diagram annotation overlay. check file.instanceId first before falling back to file.id:
source={
file.instanceId
? { type: 'instanceId', space: file.instanceId.space, externalId: file.instanceId.externalId }
: { type: 'internalId', id: file.id }
}
annotations expected but never show: if instanceId is provided but no annotations appear, the file may not have CogniteDiagramAnnotation edges in the data model. this is expected for non-diagram files. confirm the file is a P&ID or diagram synced to the data model.
height collapse: if the viewer renders but collapses to zero height, the parent container lacks an explicit height. set height via inline style, className, or parent CSS; the component does not auto-fit container height.
success state: the component renders inside the designated container, displaying the file at its first page (for PDFs) or at 100% zoom (for images/text). zoom, pan, and rotation controls are active. if source is instanceId, the diagram annotation overlay renders (if annotations exist in CDF).
file type support:
gesture support built-in:
tips: reset page, zoom, rotation manually when source changes; the component does not do it automatically. gate pagination UI on numPages > 0 since onDocumentLoad only fires for PDFs. use renderOverlay to draw custom SVG paths in original PDF-point or image-pixel coordinates, and they will scale correctly at any zoom level. touch support (pinch-zoom, two-finger drag) is always enabled; no config needed.
credits: skill authored by cognitedata. enriched for implexa quality standards with explicit decision points, edge case handling (pnpm hoisting, height collapse, annotation gating), and input/output contracts.