DocumentViewer
v1.0.13Updated 20 Oct 2026Modal viewer for ONE stored document (fb_781415ea): fetches it WITH credentials, renders a PDF in an iframe, an image inline, and an explicit "cannot be shown here" state + Download for office types. Footer: print through the frame (so the DOCUMENT prints), download under the stored filename, open-in-new-tab, close. Also accepts app-composed markup (srcDoc) for client-built print sheets. DocumentViewerSurface is the dialog-less inner surface for embedding / SSR.
Preview
Live · 7 stories▸Why this is recommendeddocumentpdfpreviewfile+7
The default, out-of-the-box DocumentViewer.
- ✓Opening a stored attachment / document from a record while the reader stays mid-task (the owner rule: popup dialogs, never new tabs)
- ✓Previewing uploaded PDFs and images from a credentialed API
- ✓Client-composed print sheets (srcDoc) with a real print path
- ✓Showing a file whose name came from whoever uploaded it, where a disguised name must not be able to save something else to the reader disk
- Fetch-then-render with credentials: include — an iframe pointed at the URL would render a login page or JSON envelope as if it were the document; fetching first turns a refusal (403 / business rule) into the app's own inline error WITH the server's message and a Retry
- States: skeleton while fetching (never a spinner with text), inline error with the server's message + HTTP status + Retry, and an explicit unsupported-type state with Download — never an empty frame
- Type resolution: mimeType prop > response Content-Type > filename extension; office types (doc/docx/xls/xlsx/ppt/pptx) resolve to the unsupported state by design
- Print through frame.contentWindow so the printed artifact is the document, not the page hosting it
- Download saves the blob under the stored fileName; open-in-new-tab opens the blob URL
- fileName is treated as UNTRUSTED (it is whatever an uploader typed): control characters, bidi overrides and zero-width characters are stripped before it is shown as the headline, the frame title or the image alt text, so a name carrying U+202E can no longer read as scanexe.pdf while being scan<U+202E>fdp.exe. The download attribute gets a second, filesystem-level cleaning: path separators and the characters Windows refuses become underscores (a/b.pdf saves as a_b.pdf, ../../x cannot leave the downloads folder), a leading dot cannot write a hidden file, an over-long name is cut but keeps its extension, and a name that cleans away entirely falls back to the unnamedFile label instead of letting the browser invent one. The extension is never rewritten, so what the reader sees is what lands on disk
- The blob URL carries the type the component RESOLVED, never the type the response asserted: an HTML body behind mimeType="application/pdf" would otherwise become a text/html blob in the deliberately unsandboxed PDF frame and run as script in the page origin; an unresolvable type is pinned to application/octet-stream. Side benefit — a real PDF served as octet-stream is now framed instead of offered as a save
- srcDoc accepts app-composed markup (client-built print sheets) — takes precedence over src; print-only (no blob, so no download / open-in-tab)
- Object-URL hygiene: blob URLs are revoked on src change, close and unmount; the in-flight fetch is aborted
- i18n labels prop (DocumentViewerLabels — every built-in string overridable, including the accessible name of the loading skeleton and the unnamedFile fallback; unsupportedDescription and errorStatus are {token} templates)
- ✕Browsing many files (use FileBrowser)
- ✕Video playback (use VideoPlayer)
- ✕Editing rich text or markup (this is a viewer, not an editor)
Install
Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.
Usage
Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.
Props
openDialog visibility (controlled)
yesonOpenChangeOpen-state change callback (✕, footer Close, Escape, outside click)
yessrcStored-document URL; fetched lazily (only while open) with credentials and rendered from a blob
—srcDocApp-composed markup rendered verbatim in the frame (client-built print sheets). Takes precedence over src; print-only mode — no download / open-in-tab
—fileNameStored filename: the headline, the frame title, the download name, and the extension fallback for type resolution. Treated as untrusted host data — invisible and direction-reordering characters are stripped before it is shown, and the saved name additionally has path separators and filesystem-reserved characters neutralised. The extension is never rewritten
yesmimeTypeMIME type hint; wins over the response Content-Type and the extension. An office mimeType shows the unsupported state immediately (no skeleton flash)
—titleDialog headline; defaults to fileName
—fetchInitMerged over { credentials: 'include' } — credentialed fetch is the default and the point
—labelsOverride any built-in UI string (print/download/openInNewTab/close/errorTitle/errorStatus/retry/unsupportedTitle/unsupportedDescription/documentAria/loading/unnamedFile) for i18n. unnamedFile is the headline and the saved name for a document whose fileName carries nothing readable
—classNameApplied to the DialogContent (the surface variant applies it to its root)
—