Blog SaaS 7 min read

File Upload Preview Fallbacks Before Upload Completes

Show file upload preview placeholder images while files are uploading or when file types cannot be previewed, using URL-based fallbacks that match file type context.

file upload preview placeholderupload preview fallbackfile type placeholderimage fallbackSaaS UX
File Upload Preview Fallbacks Before Upload Completes

File upload UIs show preview thumbnails to confirm what the user selected. For image files, this preview is usually a local object URL. For non-image files—PDFs, spreadsheets, video—or while an upload is in progress, the preview slot needs a placeholder that communicates file type rather than showing a broken image icon.

The fallback.pics API generates correctly sized, labeled placeholder images from a URL. Map each file type to a colored placeholder that communicates context, and your upload preview UI handles all file types consistently.

Why it matters

Upload preview failure modes that leave users confused

There are four common failure modes in file upload preview UIs. First, the user selects a non-image file type (PDF, DOCX, XLSX) and the template tries to render it as an img src, producing a broken icon. Second, the upload is in progress and there is no preview URL yet—the placeholder renders nothing until the upload completes.

Third, the file is an image but the local FileReader API fails (common on iOS for certain HEIC/HEIF formats) and the preview errors. Fourth, after upload the server returns a CDN URL for the file, but the CDN propagation delay means the URL returns a 404 for the first few seconds.

All four of these produce the same result: a broken image icon in the preview slot. A typed placeholder—different for PDF, spreadsheet, video, image—prevents all four failure modes from surfacing as broken UI.

File type placeholders

Map file types to colored placeholder URLs

Assign a color by file type so the placeholder communicates context: red for documents, green for spreadsheets, blue for PDFs, orange for video, gray for unknown types. The type label in the placeholder text (PDF, XLS, MP4) gives additional context at a glance.

These placeholders serve as the default src for all non-image file types and as the onerror fallback for image uploads that fail to generate a local preview.

Implementation tsx
const FILE_TYPE_PLACEHOLDERS: Record<string, string> = {
  pdf:   'https://fallback.pics/api/v1/200x200/EF4444/FFFFFF?text=PDF',
  doc:   'https://fallback.pics/api/v1/200x200/3B82F6/FFFFFF?text=DOC',
  docx:  'https://fallback.pics/api/v1/200x200/3B82F6/FFFFFF?text=DOCX',
  xls:   'https://fallback.pics/api/v1/200x200/10B981/FFFFFF?text=XLS',
  xlsx:  'https://fallback.pics/api/v1/200x200/10B981/FFFFFF?text=XLSX',
  csv:   'https://fallback.pics/api/v1/200x200/10B981/FFFFFF?text=CSV',
  mp4:   'https://fallback.pics/api/v1/200x200/F97316/FFFFFF?text=MP4',
  mov:   'https://fallback.pics/api/v1/200x200/F97316/FFFFFF?text=MOV',
  zip:   'https://fallback.pics/api/v1/200x200/8B5CF6/FFFFFF?text=ZIP',
  default: 'https://fallback.pics/api/v1/200x200/71717A/FFFFFF?text=FILE',
};

function filePlaceholder(filename: string): string {
  const ext = filename.split('.').pop()?.toLowerCase() ?? 'default';
  return FILE_TYPE_PLACEHOLDERS[ext] ?? FILE_TYPE_PLACEHOLDERS.default;
}

In-progress state

Animated skeleton placeholder while upload is in progress

While a file is uploading, neither the local preview nor the CDN URL is ready. Show an animated skeleton placeholder during the upload. Once the upload completes, swap in the local preview URL (for images) or the file type placeholder (for non-images).

The animated skeleton communicates active progress rather than an empty or broken state. Pair it with a progress indicator for large files.

Implementation text
<!-- Animated skeleton during upload -->
<img
  src="https://fallback.pics/api/v1/animated/skeleton/200x200"
  width="200"
  height="200"
  alt="Uploading file"
/>

React component

FilePreview component with upload state and type fallbacks

A single FilePreview component can handle all four failure modes. It tracks upload state, reads the file extension for type detection, and generates the appropriate placeholder URL for each state.

Implementation tsx
interface FilePreviewProps {
  file: File;
  uploadState: 'pending' | 'uploading' | 'complete' | 'error';
  uploadedUrl?: string;
}

export function FilePreview({ file, uploadState, uploadedUrl }: FilePreviewProps) {
  const isImage = file.type.startsWith('image/');
  const [localSrc, setLocalSrc] = useState<string | null>(null);
  const typePlaceholder = filePlaceholder(file.name);
  const skeletonSrc = 'https://fallback.pics/api/v1/animated/skeleton/200x200';

  useEffect(() => {
    if (!isImage) return;
    const url = URL.createObjectURL(file);
    setLocalSrc(url);
    return () => URL.revokeObjectURL(url);
  }, [file, isImage]);

  if (uploadState === 'uploading') {
    return <img src={skeletonSrc} width={200} height={200} alt="Uploading" />;
  }

  const src = uploadState === 'complete' && uploadedUrl
    ? uploadedUrl
    : (isImage && localSrc) ? localSrc : typePlaceholder;

  return (
    <img
      src={src}
      width={200}
      height={200}
      alt={file.name}
      onError={(e) => {
        e.currentTarget.onerror = null;
        e.currentTarget.src = typePlaceholder;
      }}
    />
  );
}

CDN propagation delay

Handle CDN propagation delay after upload completes

After a file upload completes and the server returns a CDN URL, the file may not yet be available at that URL if CDN propagation is still in progress. An img element pointing at the CDN URL immediately after upload may produce a brief 404.

Show the local preview or file type placeholder for a brief window after upload completion and only swap to the CDN URL after a short delay—or after confirming the CDN URL is reachable with a HEAD request.

Implementation text
async function waitForCdnUrl(url: string, retries = 5): Promise<boolean> {
  for (let i = 0; i < retries; i++) {
    const res = await fetch(url, { method: 'HEAD' });
    if (res.ok) return true;
    await new Promise((r) => setTimeout(r, 500 * (i + 1)));
  }
  return false;
}

Key takeaways

What to standardize before shipping

  • Four failure modes affect upload previews: non-image file type, upload in progress, FileReader failure on mobile, and CDN propagation delay—cover all four.
  • Map file extensions to colored type placeholders (red for docs, green for sheets, blue for PDF) so non-image files have recognizable icons.
  • Use the animated skeleton placeholder during active upload progress rather than a static placeholder.
  • Revoke object URLs created with URL.createObjectURL to prevent memory leaks in React upload components.
  • Confirm CDN URL availability with a HEAD request before swapping from local preview to CDN URL to avoid 404 flashes.

Production fallback layer

Use fallback.pics anywhere an image URL is accepted.

Start with one deterministic URL and standardize fallback behavior across your design system.

Read the docs