New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@reportwright/viewer

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@reportwright/viewer

The ReportWright report viewer for any web app: parameters, paging, search, drill-down, exports. React is bundled inside.

Source
npmnpm
Version
0.13.0
Version published
Weekly downloads
40
-98.31%
Maintainers
1
Weekly downloads
 
Created
Source

@reportwright/viewer

npm license Socket

The ReportWright report viewer in any web app (React is bundled inside, in a shadow root, so your styles do not leak in). A React app imports @reportwright/viewer/react-peer (and /react-peer/designer) instead, the same viewer using the app's own React, so the page loads React once; @reportwright/react does this for you:

Bundler setup (read this first)

The viewer renders in a Web Worker (engine.worker.js, one self-contained file next to the viewer). Bundlers that understand new Worker(new URL(...)) pick it up; two settings matter:

  • Vite 7: set worker: { format: 'es' } in vite.config.js. Without it the build fails with Invalid value "iife" for option "worker.format".
  • esbuild (plain bundling) does not follow new Worker(new URL(...)): copy node_modules/@reportwright/viewer/dist/engine.worker.js next to your bundle (it is one file; its only imports are the package's own dependencies).

If the worker cannot load, the viewer still renders, on the main thread (slow and janky at 100k rows), and logs once [reportwright] worker did not load (<reason>); rendering on the main thread. handle.usesWorker tells you which one you got.

Import it through your bundler (Vite, webpack, esbuild, Rollup, Next…). Its third-party libraries (pdf-lib, @pdf-lib/fontkit, bwip-js, fflate, bidi-js) are ordinary dependencies, pinned to the versions the tests ran against, so the package itself holds only ReportWright's own readable code.

Without a server (library only)

npm i @reportwright/viewer @reportwright/fonts
# serve the fonts as static files, e.g. from your app's public folder
cp -r node_modules/@reportwright/fonts/public public/pw     # fonts/, harfbuzz-subset.wasm, icc/
import { mountViewer } from '@reportwright/viewer';
mountViewer('#report', {
  definition,                         // the report JSON (see @reportwright/engine/report)
  // data: JSON for each data source, by name. Match the data set's path: { orders: { rows: [...] } } when it reads "$.rows"
  // (docs/QUICKSTART.md), or { orders: [...] } when it has no path (the array is the rows). A mismatch gives empty tables.
  data: { orders: { rows: [...] } },
  // REST sources in the definition are fetched as usual
  fontsUrl: '/pw/fonts/',             // the copied fonts folder; PDF subsetting and PDF/A use ../harfbuzz-subset.wasm and ../icc/
});

Nothing calls /api; each font is downloaded once, and only when the layout first needs it (a report in Inter Regular and Bold fetches those two files, not all seven); the toolbar uses the system font.

If a font cannot be loaded (wrong fontsUrl, the folder not served), the report does not draw: the viewer shows "The report did not run. Font "Inter-Regular" did not load (HTTP 404)" with a Retry button, and onReady(error) is called with that error. Fix the path and retry; there is no fallback font. onReady(error) is called when the first run ends: null when it went through, or the error that stopped it. Bundled apps: serve the fonts folder at the URL you pass as fontsUrl; fontsUrl is not inferred from the package. Copying only fonts/ works too (PDFs then embed whole fonts, and PDF/A export needs icc/).

Serve fontsUrl with Cache-Control: public, max-age=31536000, immutable: the files do not change within a package version, so a returning visitor downloads none of them.

What works standalone, what needs the server

The npm packages are a library: nothing here needs a ReportWright server. The server is a separate, self-hosted app (not on npm): github.com/MrArun005/reportwright, run it with Docker (instructions). Options that need it are optional and hidden without it.

FeatureStandalone (definition, data, fontsUrl)With server
View, page, search, zoom, parameters, sort, drill-down, document mapyesyes
Export PDF, PDF/A, Word, Excel, PowerPoint, HTML, CSV (in the browser)yesyes
REST / JSON / XML / CSV data sources the browser can reach (CORS)yesyes
Designer: edit, preview, onSave / onOpen to your appyesyes
Load a saved report by id (report: 'id'), drill-through to another saved reportpass reports: { id: definition }yes
Designer Save without onSave (writes to the server), version historyno (Save is hidden)yes
SQL data sources (the server holds the connections)noyes
Server-side PDF for very large reports, schedules and e-mail, uploaded fonts, AI drafts, users and rolesnoyes

In Node, @reportwright/engine renders and exports with no server and no browser.

Data sources and private addresses

A report definition says which URLs its data comes from, and the viewer's browser requests them. So that a definition cannot point a viewer at your network, a data source on another host may reach public http(s) addresses only: literal private, loopback, link-local and CGNAT addresses (192.168.1.1, 127.0.0.1, [::1], 169.254.169.254, 100.64.0.1, in any spelling), localhost / .local / .internal names and other schemes are refused, redirects are checked hop by hop, and a parameter may fill a URL's path and query but not its host.

mountViewer(el, { definition, allowHosts: ['reports-api.corp.example'] }); // private hosts that may be reached
mountViewer(el, { definition, unsafeFetch: true });                          // no check (trusted definitions only)
mountViewer(el, { definition, fetch: myFetch });                             // every data request goes through yours

A browser cannot resolve DNS, so a public name that resolves to a private address is not caught. When definitions come from people you do not trust (a multi-tenant app), render on the server (its guard checks DNS), or pass a fetch that allows only the hosts you know. The same options apply to mountDesigner.

Content Security Policy

The viewer needs no 'unsafe-inline': its styles are constructable stylesheets, and the style attributes of the drawing are applied through CSSOM. The policy for a page with the viewer (standalone, fonts on the same origin):

default-src 'self';
script-src 'self';
style-src 'self';
font-src 'self';
img-src 'self' data: blob:;
connect-src 'self'

This is the policy the viewer's standalone page is tested under (PDF export included). Keep the viewer's script in a file (<script type="module" src="…">), not inline. The viewer adds a <style> element only in browsers without constructable stylesheets, and gives it the nonce you pass (mountViewer(el, { nonce })).

With the self-hosted ReportWright server

mountViewer('#report', { server: 'https://reports.example.com', report: 'loan-statement', params: { accountId: 'LN-1' } });

The ReportWright server must allow your site's origin (PW_EMBED_ORIGINS). PDF export and barcodes load only when used. MIT.

The designer is a second entry, loaded only by pages that use it:

import { mountDesigner } from '@reportwright/viewer/designer';
const designer = mountDesigner('#designer', {
  server: 'https://reports.example.com',
  definition,                                   // or report: 'id' to load it from the server
  onSave: (def) => saveToMyApp(def),            // without onSave, Save writes to the ReportWright server
  onOpen: () => pickAReport(),                  // shows an Open… button; return a definition
  readOnly: false,
  toolbox: { hide: ['map', 'chart'] },
  panels: { data: true, inspector: true },
  dataSourceTemplates: [{ label: 'Orders API', source: { name: 'orders', type: 'rest', url: 'https://api.example.com/orders' } }],
});
designer.getDefinition();

Types are included (ViewerOptions, DesignerOptions, …). Framework wrappers: @reportwright/react, @reportwright/vue, @reportwright/svelte, @reportwright/angular.

Viewer toolbar: toolbar.hide

toolbar: { hide: [...], add: [...] } hides built-in items. A group key hides the whole group; an item id hides that one item. Hiding every export format also hides the Export menu and its options gear.

KeyHides
exportthe Export menu: every format and the options gear (Print stays)
pdf, excel, word, powerpoint, html, csv, jsonthat one export format
printthe Print button
searchthe search box and its match controls
zoomzoom out, zoom value, zoom in, whole page, fit width
navigationfirst page, previous, page number, next, last page
sidebarthe page thumbnails and the document map buttons
parametersthe Parameters button
run, viewMode, fullScreen, statsthose single items (use their id)
exportOptionsthe options gear (the formats stay)

Any other key is taken as an item id. Unknown keys hide nothing.

Controlling a mounted viewer: ViewerHandle

mountViewer returns a handle (the React, Vue, Svelte and Angular wrappers give you the same one through their ready callback or event):

MethodWhat it does
setParameters(params)merges params into the current parameters and runs the report again; the host element is not remounted
setDefinition(def)shows another report definition in the same element
goToPage(n)scrolls to page n (1-based)
getPageCount()the page count of the last run (0 before the first)
print()opens the print dialog for the PDF
export(format)Promise<Blob>: format is pdf, xlsx, docx, pptx, html, csv or json (or the item ids excel, word, powerpoint)
on(event, fn)listens for ready (a run finished, detail: page count), error (detail: message), page (detail: current page), parameters (detail: the parameters); returns an unsubscribe function
unmount()removes the viewer
const viewer = mountViewer('#report', { server, report: 'loan-statement' });
viewer.setParameters({ accountId: 'LN-2' });      // re-runs; 'ready' fires when it is done
const pdf = await viewer.export('pdf');           // a Blob

Size

What a page downloads when the viewer mounts: 847 KB gzipped (0.7.3: 877 KB), readable unminified code with source maps. Loaded only when used: other interface languages (about 20 KB gzipped each), PDF export (pdf-lib), barcodes (bwip-js), Excel/HTML/CSV export, the designer. Most of the first load is the font engine (fontkit) the layout measures text with, and React. The package is larger unpacked (two builds: React inside, and React from your app at @reportwright/viewer/react-peer, plus source maps); only one build reaches your page. PDF export, fontkit, barcodes, zip and bidi come from the dependencies above, so your bundler can share them with the rest of your app.

Keywords

reportwright

FAQs

Package last updated on 08 Oct 2026

Related posts