shadcn.io is not affiliated with official shadcn/ui
File Preview Dialog Block for React
A shared drive's document list with a full-screen viewer shown open: three drawn pages that turn with buttons or arrow keys, a Fit, 125% and 150% zoom, a details list, activity, a Copy link button and a Download that saves a text file.
Installation
Overview
Let people read a file without leaving the page it lives on, with this document viewer for React with Base UI. It opens from the Documents page of Shelfdrive, a shared drive: the page is a drawn list of six files, the opened one shaded, under a soft scrim inside a hairline frame with a plus mark on each corner, and its header holds an outline Open Q3 planning Button. The viewer fills the screen. A title bar names "Q3 planning.pdf" with its size and who it is shared with; a toolbar holds previous and next page buttons around "1 / 3", a Toggle Group for Fit, 125% and 150%, a Copy link Copy Button and Download text, the only Shine Button. The pages are drawn: a heading, paragraphs and a table, then a bar chart, then a timeline. Beside them from md is a list of the file's details and two lines of activity; on a phone they fall under the page. Download saves a real file, "Q3 planning (text).txt", with the written text of the three pages, named for what it is. The page you stopped on is kept, and the page behind says where you were. For preview the viewer is drawn open in place when the page loads, inset so the list shows around it; the header button opens the real full-screen Dialog, with the same page and the same zoom. For drives, document tools, inboxes with attachments and any app that previews files.
FAQ
Why is the viewer already open, and what do I keep?
So you can see it without a click. On load the viewer is a copy drawn in place over the page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Open Q3 planning button opens the real full-screen Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same PreviewBody and PreviewBar and share the block's state, so the page you turned to in the copy is the page in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
Are these real pages?
No. The three pages are drawn from shapes, hidden from screen readers, and each one is described in words for assistive technology: "Page 2 of 3: a bar chart of weekly active folders, rising over twelve weeks". The block makes no claim that a PDF was rendered. In your app render the real file with your PDF viewer or a page image, keep the page buttons and the zoom, and keep a text description or the document's own text for screen readers.
What does Download save?
A plain-text file, "Q3 planning (text).txt", that holds the written text of the three pages with a "Page 1 of 3" line above each. It is real, the browser saves it, and the button says "Download text" so nobody expects the PDF. The dialog and the polite status message say the PDF itself is not in this demo. In your app point the Download Trigger at the file's URL or a function that returns its Blob.
How do the arrow keys and the zoom work?
With focus on the page area, the arrow keys turn the page: the end-side arrow goes forward in either direction, so in a right-to-left page the left arrow is next. The buttons do the same and go quiet at the first and last page, saying so when pressed. Zoom is one group of three choices, never empty: Fit sizes the page to the viewer's height, and 125% and 150% make it wider than the area so the area scrolls both ways.
What stays local?
The page number, the zoom and the shaded row are local state; nothing is fetched, saved or sent. Copy link puts the typed link shelfdrive.example/d/q3-planning on your clipboard through the browser, and says so, or says it couldn't. In your app load the file by its id, and build the link and the details from your API.
Is it accessible from the keyboard?
Yes. The real dialog is named by its title and described by its sentence, focus stays inside until it closes, and Escape and the close button return focus to Open Q3 planning. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. The page area is a named region you can focus and scroll, the zoom is a labelled group, and the copy shown on load moves no focus; Escape inside it and its close button close it and put focus on Open Q3 planning.
Related
- Pro
A site-survey plans tab with its full-screen inspector shown open: three drawn sheets in a viewport you pan by dragging, zoom with Fit, 100%, 200% and 400% or a slider, a minimap that jumps, and a real SVG download.
- Pro
Lightbox Gallery
Stage and thumbnailsNine pictures of a photography studio's year on shared hairlines, a featured frame and a caption under each, where every frame opens one viewer dialog with the whole picture, its credit, Previous and Next, a counter, arrow keys and a swipe.
- Pro
Version History Dialog
Side sheetsA docs tool's version history sheet shown open: six versions with authors and times, the diff of the chosen one against the current text marked with underline, strike-through, glyphs and words, and a two-step restore.
- Pro
Share Receipt Dialog
Share and inviteA payments app's Payment sent page with a dialog shown open that keeps the receipt three honest ways: a paper receipt drawn in mono type, Copy as text, Save .txt and Print, each doing what the browser really does.
- Pro
A usage-report tool's Exports page with its export dialog shown open: a CSV or JSON format, a range with row counts, six fields behind a mixed Select all box and a size estimate, and a Download button that saves a real file.
- Pro
Media Upload Dialog
Media and widgetsA video library with its upload dialog shown open: a drop zone for images, video and audio that names the files it turns away and why, tiles for the ones that pass with a check each, bulk removal, a total and an Add button that puts the clips in the library.