Open File Viewer: A Container-First File Preview SDK for React, Vue, Svelte and Vanilla JS
A framework-agnostic embedded file viewer for vanilla JavaScript, React, Vue and Svelte. Put PDF, Office, images, media, archives, email, drawings, 3D, GIS and engineering files inside one stable container.
At a glance
- What is it?
- Open File Viewer wraps PDF, Office, image, media, archive, email, CAD, 3D and GIS previews in one DOM container you own. It is a good fit for business apps that need attachment preview; it is not a desktop file manager.
- Who is it for?
- Adopt Open File Viewer if you are embedding previews inside a React, Vue, Svelte or vanilla JS business application and want format handling isolated in plugins you can remove. Do not adopt it if you are looking for a desktop program that opens files on Windows, or if you cannot host pdfjs-dist assets yourself.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 17, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The attachment preview problem Open File Viewer targets
Business systems accumulate attachments. A contract is a PDF, a budget is an xlsx, a drawing is a dxf, an email thread is an eml, a scan is a jpeg. Each of those formats has its own JavaScript library, its own loading model, and its own failure mode. The usual outcome is a codebase where a PDF viewer, a spreadsheet renderer and an image lightbox coexist with no shared lifecycle, no shared error state, and no shared way to say "this file cannot be previewed, offer a download instead".
Open File Viewer addresses that by making the container the unit of integration. The README states that all content renders inside the DOM container you provide, and that the library does not open a new window or interrupt the host business page. The audience is therefore front-end engineers building admin panels, CRM screens, ticketing tools and document workflows, not end users looking for a file manager.
The package is written in TypeScript and published under the MIT licence, with separate entry points for vanilla JavaScript, React, Vue and Svelte. The workspace version at the time of the last push was 0.1.47, and the release history shows 0.1.45, 0.1.46 and 0.1.47 within a week of each other. That cadence matters when you evaluate it: the API surface is still moving.
How the plugin pipeline decides what renders
The architecture is a flat plugin array plus a factory. You build a list of plugin instances, hand it to createViewer() along with a container selector and the file, and the viewer walks that list. Each plugin exposes a match() function, and the first match wins. That is the whole dispatch mechanism, and it is deliberately boring, which is the right call for a preview layer.
The README is explicit that plugin order matters. fallbackPlugin() is terminal, so placing it before native plugins means nothing else ever gets a chance to render. The same ordering rule applies to the support check: isPreviewSupported() normalizes the source exactly like createViewer(), evaluates plugin.match() in order, does not mount DOM, does not call plugin.render(), and does not count fallbackPlugin() as native preview support. Keeping one plugin array and reusing it for both the pre-flight check and the mount is the intended pattern.
Format handling is progressive. Formats browsers can already display are rendered locally first, and the README describes complex formats as candidates for gradual integration of WASM, dedicated parsers or server-side conversion. The PDF path is the concrete example: pdfPlugin() uses pdfjs-dist, and PDF.js 5 and later pull JPEG2000 and JBIG2 decoders from pdfjs-dist/wasm/. Those files are not bundled for you. You serve the directory and pass its public URL, including the trailing slash, as wasmUrl. PDF.js 4 ignores that option, so the same configuration is forward-compatible but inert on the older major.
Installing @open-file-viewer/core and mounting a first viewer
The README uses pnpm throughout. Add the core package, then the framework binding if you are not writing vanilla JavaScript. React needs both core and the react package, and Vue and Svelte follow the same two-package pattern.
pnpm add @open-file-viewer/core
pnpm add @open-file-viewer/core @open-file-viewer/reactPDF preview is the one format with an external peer. The README says pdfjs-dist is required when you use pdfPlugin().
pnpm add pdfjs-distImport the shared stylesheet once, at application level, not per component. The README shows a single import of the core style file.
import "@open-file-viewer/core/style.css";Now build the plugin list. This is the vanilla JavaScript quick start from the README, trimmed to the plugins that need no extra configuration. Note that pdfPlugin() takes a workerSrc, which the README sources from pdfjs-dist/build/pdf.worker.mjs with Vite's ?url suffix.
import {
createViewer,
imagePlugin,
textPlugin,
pdfPlugin,
officePlugin,
archivePlugin,
fallbackPlugin
} from "@open-file-viewer/core";
import "@open-file-viewer/core/style.css";
import pdfWorkerSrc from "pdfjs-dist/build/pdf.worker.mjs?url";
const plugins = [
imagePlugin(),
textPlugin(),
pdfPlugin({ workerSrc: pdfWorkerSrc }),
officePlugin(),
archivePlugin(),
fallbackPlugin()
];Mount it against a selector. The README's example passes width, height, fit, toolbar and theme, and keeps a handle so the host can react to layout changes.
const viewer = createViewer({
container: "#viewer",
file: fileOrUrl,
fileName: "contract.pdf",
width: "100%",
height: "70vh",
fit: "contain",
toolbar: true,
theme: "auto",
plugins
});
viewer.resize();
viewer.destroy();What you should see is the file rendered inside the element matched by #viewer, with no new tab and no navigation away from the host page. The README documents CSS sizes including px, %, vh, vw, rem and calc() for the width and height options, and states that the viewer responds to container changes. viewer.resize() is the manual trigger; viewer.destroy() tears the instance down, which is what you call on route change or component unmount.
If you want to know whether a file will render before you mount anything, call the support check with the same array.
import { isPreviewSupported } from "@open-file-viewer/core";
const supported = await isPreviewSupported(fileOrUrl, plugins, {
fileName: "contract.pdf",
mimeType: "application/pdf"
});This resolves without touching the DOM, so it is usable to decide between rendering a preview and showing a download link.
Where Open File Viewer breaks: sandboxes, WASM paths and remote PDFs
The README documents a failure mode that will look familiar to anyone who has shipped a micro-frontend. Inside qiankun or micro-app, the sandbox tears down window message listeners when a sub-app unmounts. That breaks the setImmediate polyfill jszip depends on, JSZip.loadAsync never resolves, and every zip-based preview (docx, xlsx, pptx, epub, ofd) hangs on "Loading". This is not a bug in the viewer so much as a collision between two sandboxing models, but it lands on you, and the symptom is a spinner rather than an error.
The remote PDF fallback has a security-shaped trade-off. When PDF.js cannot parse or load a remote PDF URL, the plugin falls back to the browser's embedded PDF viewer in a sandboxed iframe. Because Chromium-based viewers differ in what they need, webFallbackScripts defaults to "auto": cross-origin URLs get allow-scripts while staying sandboxed, and same-origin URLs keep scripts blocked. Setting it to "never" makes the sandbox strict, and "always" trusts same-origin PDF endpoints. The README notes this setting only affects the remote iframe fallback and does not change the normal PDF.js rendering path or force a referrerpolicy value. If you serve PDFs from the same origin as the host app, the default is the conservative one, and switching to "always" is a decision you should make deliberately.
The wasmUrl requirement is an operational constraint rather than a bug. PDF.js 5 and later fetch JPEG2000 and JBIG2 decoders from a directory you host, and the README warns that the hosted files must stay on the same version as the installed pdfjs-dist. A CDN cache holding an older wasm build against a newer pdfjs-dist is a version skew you have to manage yourself.
Finally, this is the wrong tool when the requirement is a desktop program. The related searches around Windows 7, Windows 10 and Android file viewers describe a different category of software. Open File Viewer is a browser SDK with no desktop build in the README, and no offline story beyond whatever your web app already provides.
Open File Viewer against a single-format PDF.js integration
The honest alternative is not another multi-format SDK. It is wiring pdfjs-dist directly, which is what many teams do first, and it is a reasonable choice when PDF is the only format you will ever preview.
The difference is where the abstraction sits. A direct pdfjs-dist integration gives you a canvas, a page loop and a text layer, and you own pagination, zoom, error handling and the toolbar. Open File Viewer puts those behind createViewer() and a plugin array, and adds a shared lifecycle across formats: loading, error, unsupported and download fallback states are described in the README as application-ready, and a multi-file queue is part of the same surface. You also get the support check, which a hand-rolled PDF viewer does not have because it has nothing to check against.
The cost is a dependency graph. Open File Viewer still depends on pdfjs-dist for PDF, so you are not removing that package by adopting it, you are adding a layer on top. If your only format is PDF and your UI is simple, that layer is overhead. If you are already juggling five formats and want to delete the spreadsheet renderer later without touching the PDF path, the plugin boundary is the reason to take it. The README frames the project as a file preview foundation rather than a PDF-only demo, and the plugin-per-format design is what makes that claim checkable: you can read the plugin list and see exactly what you are shipping.
Licence, release cadence and what upgrading costs
The repository is MIT licensed, declared in both the LICENSE file and the workspace package.json. MIT is permissive: you can use it commercially, modify it, and redistribute it, provided the copyright notice and permission notice travel with it. That is a summary of the licence text, not legal advice, and the obligations that matter to you depend on how you distribute your application.
The upgrade picture is the part to watch. The workspace sat at 0.1.47, with 0.1.45, 0.1.46 and 0.1.47 released on 2026-09-10, 2026-09-15 and 2026-09-16 respectively, and the last push to main was on 2026-09-17. A 0.x line moving that fast means patch releases can carry behaviour changes, and the README already documents one: webFallbackScripts defaults to "auto", which is a sandbox policy decision, not a cosmetic default. Pin the version in your lockfile and read the release notes before bumping.
The maintenance surface is small but real. You are responsible for hosting pdfjs-dist assets, keeping the wasm directory version-matched, and importing @open-file-viewer/core/style.css exactly once. Those are three things that break quietly rather than loudly, which is the upgrade cost in practice.
Editorial conclusion
Adopt Open File Viewer if you are embedding previews inside a React, Vue, Svelte or vanilla JS business application and want format handling isolated in plugins you can remove. Do not adopt it if you are looking for a desktop program that opens files on Windows, or if you cannot host pdfjs-dist assets yourself. Before committing, verify the version of @open-file-viewer/core you install, whether you need pdfjs-dist and the matching wasmUrl directory, and whether the plugin list you plan to ship covers every format your users actually upload.
Frequently asked questions
Is Open File Viewer free to use?
Yes. The repository is MIT licensed, and the licence is declared in the LICENSE file and in the workspace package.json. That permits commercial use and modification as long as the copyright and permission notice are preserved.
What does "open file" mean in Open File Viewer?
In this project it means rendering a file inside a DOM container you provide, not launching a separate application. The README states that all content renders inside that container and that the library does not open a new window or interrupt the host business page.
Which file viewer is best?
There is no single answer, and the choice depends on whether you need a browser SDK or a desktop program. Open File Viewer is a browser-side preview SDK for vanilla JavaScript, React, Vue and Svelte, with each format handled by a separate plugin you can include or leave out.
Community notes