Building a Reusable File Preview Platform in Vue 3
Most admin panels, content systems, and collaboration tools eventually need file preview. Without a layered architecture, each new format or storage backend forces changes across every page that shows an attachment. This design keeps business code ignorant of rendering details, so swapping file services or adding formats never touches the UI.
File preview starts simple but quickly accumulates complexity: PDFs, Word docs, spreadsheets, images, logs, and archives each demand different browser capabilities, while Blob URLs, CORS, authentication, and memory management create hidden failure modes. A five-layer architecture separates business pages from rendering logic, routing every file through a unified state model that accepts both URLs and Blobs.
Format identification uses a priority chain—caller-specified type, server MIME, then file extension—rather than trusting extensions alone. Each format gets its own renderer component, loaded asynchronously so that viewing an image never pulls in Office dependencies. Text and archive fetches use AbortController to cancel stale requests when users switch files rapidly.
Cross-origin access moves from Vite dev proxies to production gateways that enforce domain whitelists and prevent SSRF. Third-party Office preview becomes an optional fallback, not a default, with sensitive files routed through server-side PDF conversion instead. The state machine treats loading, error, unsupported, and retry as first-class states, making analytics straightforward and user experience consistent across every format.
Most file preview implementations start as a quick iframe and grow into a tangle of special cases; the five-layer split here is a deliberate inversion that makes the preview platform the stable center and business pages thin clients.
Delaying Blob URL revocation until after a close animation completes is a small detail that prevents a visible flash, but it also introduces a timer that must be cancelled on the next open() call—a race condition easy to miss in testing.
Treating error and unsupported as first-class states rather than edge cases means analytics can distinguish network failures from format gaps from permission denials, which is far more useful than a generic 'preview failed' log.
The insistence that hideDownload is not security is worth underlining: too many teams ship a hidden download button and believe they've implemented access control, when the file URL is still valid and inspectable in dev tools.