Blog Web & Frontend
WEB DEVELOPMENT

In-Browser Document Preview, Done Right

Rendering PDFs, Office files, and images inline in the browser without forcing a download first — the approach we use for a smoother in-app document preview.

Dec 21, 2023

Users hate downloading a file just to see if it's the right one. Whether it's a client portal showing signed contracts or an HR system showing payslips, forcing a save-then-open round trip loses people, especially on mobile. Getting inline preview right means treating PDFs and Office documents as two separate problems, because the browser only natively understands one of them.

PDFs: Native Rendering vs PDF.js

Every modern browser ships a built-in PDF renderer, so an embed or iframe pointed at the file gets you inline viewing with zero JavaScript. That's the right call for a simple "here's the document" page. The moment you need custom controls — page thumbnails, in-document search, watermark overlays, restricting print/download, or a consistent UI across browsers that render the native viewer differently — you drop to PDF.js and render into a canvas yourself.

javascript
pdfjsLib.GlobalWorkerOptions.workerSrc = '/scripts/pdf.worker.min.js';

pdfjsLib.getDocument('/documents/sample.pdf').promise
  .then(function (pdf) {
    return pdf.getPage(1);
  })
  .then(function (page) {
    const scale = 1.5;
    const viewport = page.getViewport({ scale });
    const canvas = document.getElementById('pdf-canvas');
    const context = canvas.getContext('2d');

    canvas.width = viewport.width;
    canvas.height = viewport.height;

    return page.render({ canvasContext: context, viewport: viewport }).promise;
  })
  .catch(function (error) {
    console.error('Failed to render PDF', error);
  });

Office Documents Are a Different Problem

No browser renders .docx or .xlsx natively, so you're choosing between two fundamentally different approaches. Microsoft's Office Online viewer (an iframe pointed at view.officeapps.live.com) is free and looks great, but it requires the source file to be reachable over public HTTPS by Microsoft's servers — which rules it out for anything behind auth, a VPN, or a signed-URL-with-short-expiry scheme. The alternative is server-side conversion: run the document through LibreOffice headless or a commercial library, produce a PDF, and hand that to the PDF pipeline you already built.

  • Office Online viewer: zero infrastructure, but the file must be publicly fetchable and you lose control over the rendering pipeline
  • Server-side conversion (LibreOffice headless, Aspose, GroupDocs): works behind auth and gives consistent output, but you own the conversion queue and its failure modes
  • Conversion output can be cached — convert once on upload, not on every preview request
  • Very large spreadsheets convert slowly and sometimes ugly; set a size threshold where you fall back to a plain download link

Wiring Up a Preview Endpoint

The endpoint's job is simple: figure out the content type, convert if needed, and stream the result back with a Content-Disposition of inline rather than attachment. That header is what decides whether the browser opens the file in place or pops a save dialog, and it's the single most common bug in a preview feature that otherwise works.

csharp
[HttpGet("api/documents/{id}/preview")]
public async Task<IActionResult> PreviewDocument(int id)
{
    var document = await _documentRepository.GetByIdAsync(id);
    if (document == null)
        return NotFound();

    byte[] pdfBytes = document.ContentType == "application/pdf"
        ? await _storage.ReadAllBytesAsync(document.StoragePath)
        : await _conversionService.ConvertToPdfAsync(document.StoragePath);

    Response.Headers.Add("Content-Disposition", $"inline; filename={document.FileName}.pdf");
    return File(pdfBytes, "application/pdf");
}

X-Frame-Options and Content-Security-Policy frame-ancestors block iframes by default on most sites. If your preview page won't load Office Online or your own PDF.js iframe, check those headers before you assume the viewer itself is broken.

Wrapping Up

Preview is one of those features users never praise and always notice when it's missing. Normalize everything to PDF where you can, cache the conversion, and get the inline Content-Disposition header right — that combination covers the vast majority of what a document portal actually needs.

Web Development File Handling UX

More from the blog

View all articles

Read something that fits your problem?

Tell us what you're building. We'll come back with an honest, scoped recommendation — not a sales pitch.

Start a Project