
Security News
vlt 1.5.0 Adds Agent Skills Management
vlt 1.5.0 links skills from JavaScript dependencies to coding agents, with lockfile versioning and Socket data to filter out known malicious packages.
@reportwright/viewer
Advanced tools
The ReportWright report viewer for any web app: parameters, paging, search, drill-down, exports. React is bundled inside.
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:
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:
worker: { format: 'es' } in vite.config.js. Without it the build fails with
Invalid value "iife" for option "worker.format".new Worker(new URL(...)): copy the worker files next to your bundle:
cp node_modules/@reportwright/viewer/dist/engine.worker*.js dist/. engine.worker.js has no imports (its libraries are
inside it); engine.worker.bwip.js (barcodes) loads only when a report
needs it. Without it those reports render on the main thread, with the warning below.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.
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.
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.
| Feature | Standalone (definition, data, fontsUrl) | With server |
|---|---|---|
| View, page, search, zoom, parameters, sort, drill-down, document map | yes | yes |
| Export PDF, PDF/A, Word, Excel, PowerPoint, HTML, CSV (in the browser) | yes | yes |
| REST / JSON / XML / CSV data sources the browser can reach (CORS) | yes | yes |
Designer: edit, preview, onSave / onOpen to your app | yes | yes |
Load a saved report by id (report: 'id'), drill-through to another saved report | pass reports: { id: definition } | yes |
Designer Save without onSave (writes to the server), version history | no (Save is hidden) | yes |
| SQL data sources (the server holds the connections) | no | yes |
| Server-side PDF for very large reports, schedules and e-mail, uploaded fonts, AI drafts, users and roles | no | yes |
In Node, @reportwright/engine renders and exports with no server and no browser.
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.
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 })).
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.
toolbar.hidetoolbar: { 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.
| Key | Hides |
|---|---|
export | the Export menu: every format and the options gear (Print stays) |
pdf, excel, word, powerpoint, html, csv, json | that one export format |
print | the Print button |
search | the search box and its match controls |
zoom | zoom out, zoom value, zoom in, whole page, fit width |
navigation | first page, previous, page number, next, last page |
sidebar | the page thumbnails and the document map buttons |
parameters | the Parameters button |
run, viewMode, fullScreen, stats | those single items (use their id) |
exportOptions | the options gear (the formats stay) |
Any other key is taken as an item id. Unknown keys hide nothing.
ViewerHandlemountViewer returns a handle (the React, Vue, Svelte and Angular wrappers give you the same one through their ready callback or event):
| Method | What 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
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.
FAQs
The ReportWright report viewer for any web app: parameters, paging, search, drill-down, exports. React is bundled inside.
The npm package @reportwright/viewer receives a total of 40 weekly downloads. As such, @reportwright/viewer popularity was classified as not popular.
We found that @reportwright/viewer demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
vlt 1.5.0 links skills from JavaScript dependencies to coding agents, with lockfile versioning and Socket data to filter out known malicious packages.

Security News
Rust 1.100 will add opt-in dependency cooldowns to Cargo, giving security scanners and maintainers time to catch malicious crate releases.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.