◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/App Dev
◇ App Dev/2026-06-30Intermediate

Previewing Files In-App in Rork — calling Quick Look safely from Expo

How to preview PDFs, images, and Office documents in place without sending users out of your app, using Quick Look (QLPreviewController) from Rork (Expo). Covers pre-downloading remote files, the local-URL requirement, the Android FileProvider alternative, and handling the share button.

Rork585Expo219Quick Lookfile previewindie developer43

✦ Premium Article

A user taps an invoice PDF they received inside the app, gets bounced out to Safari, and can't find their way back — building apps as an indie developer, I've felt the pain of this "left and never returned" experience. Sending someone away just to view a file breaks the context they were in.

iOS has a mechanism built for exactly this: Quick Look (QLPreviewController). It opens PDFs, images, text, CSV, and Office documents like Word and Excel as a full-screen preview inside your app. A standard Rork app is generated as React Native (Expo), so you call this native Quick Look through a thin bridge module. The crux of the design was knowing precisely what you can and can't hand it.

What Quick Look can and can't open

First, the premise. Quick Look is a local file preview mechanism. Getting this wrong is how the first implementation stumbles.

  • It opens local file URLs (paths starting with file://)
  • Passing an https:// remote URL directly won't work. You have to download it locally first
  • Supported formats are broad: PDF, images (JPEG/PNG/HEIC), text, CSV, RTF, iWork, Microsoft Office, USDZ (3D), and more
  • A correct extension is the hint for type detection. With an extensionless name like document.tmp, even genuine PDF content sometimes won't open

What I tripped on first was handing it a signed URL fetched from the server as-is. Quick Look just shows a blank, with no error. Locking in the rule "always cache remote files before opening" from the start is the shortcut.

Cache the remote file first

In Expo, download with expo-file-system and hand the saved local URI to Quick Look. If it's already downloaded, reusing it instead of re-fetching keeps the wait short even for a large PDF.

// downloadForPreview.ts
import * as FileSystem from 'expo-file-system';
 
// Cache a remote URL and return the file:// local path
export async function ensureLocalCopy(remoteUrl: string, filename: string) {
  const target = FileSystem.cacheDirectory + filename;
 
  const info = await FileSystem.getInfoAsync(target);
  if (info.exists) return target; // don't re-download if present
 
  const { uri, status } = await FileSystem.downloadAsync(remoteUrl, target);
  if (status !== 200) {
    throw new Error(`download failed: ${status}`);
  }
  return uri;
}

Always include the correct extension in the filename. If the server doesn't return one, deriving .pdf or .xlsx from the Content-Type yourself keeps Quick Look's type detection stable.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦You'll be able to let users view PDFs, images, and Office docs inside the app instead of handing them to an external app
✦You'll see, with working code, why passing a remote URL directly fails and the correct flow of caching first, then opening the local URL
✦You'll learn the design that folds iOS Quick Look and Android FileProvider+ACTION_VIEW into one call site, plus when to suppress the share button
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◇ App Dev2026-09-18
The three lines I check when prebuild stops on the standard SDK 57 Swift AppDelegate error
Turning on ios.enableSceneSupport for iOS 27 can stop prebuild cold. The cause was not my SDK version but the shape of AppDelegate.swift. Here are the three lines to check, and why copying the SDK 58 migration steps by hand will break your build.
◇ App Dev2026-09-16
The Token I Deleted Came Back After a Restart — Detecting Failed SecureStore Deletes
On Android, getItemAsync can report null right after sign-out while the value is still sitting on disk. Here is how I rewrote a sign-out path that was verifying deletion by reading instead of by the delete result.
◇ App Dev2026-09-10
Your First EAS Workflow in a Rork Repo, and the Alert That Goes Missing Exactly When You Need It
Putting two files into .eas/workflows in a repo exported from Rork, and why a notification wired with needs stays silent on exactly the nights it fails — with the output of a small local checker I actually ran.