React File Download: A Complete Guide 📥

A clean, reusable solution for downloading files from URLs in React applications

The Core Function

Here’s a robust file download utility that handles most common scenarios:

export default async function downloadFileFromUrl(url, defaultName, headers, onFinish) {
  const response = await fetch(url, {
    method: 'GET',
    headers: headers || {}
  });

  const contentDisposition = response.headers.get('Content-Disposition');
  const filename = contentDisposition
    ? contentDisposition.split('filename=')[1]
    : defaultName;

  if (response.ok) {
    const blob = await response.blob();

    // Create blob link to download
    const blobUrl = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = blobUrl;
    link.download = filename;

    // Force download
    link.click();
  } else {
    const message = `Error Message: ${response.statusText}`;
    handleError(message);
  }

  if (onFinish) {
    onFinish();
  }
}

How It Works

1. Fetch the File

  • Makes a GET request to the provided URL
  • Supports custom headers for authentication or other requirements

2. Smart Filename Detection

  • Attempts to extract filename from Content-Disposition header
  • Falls back to provided defaultName if header is missing

3. Blob Download

  • Converts response to blob for binary data handling
  • Creates a temporary object URL
  • Programmatically triggers download via hidden link

4. Cleanup & Callback

  • Handles errors gracefully
  • Executes optional completion callback

Usage Examples

Basic Download

await downloadFileFromUrl(
  'https://api.example.com/files/report.pdf',
  'quarterly-report.pdf'
);

With Authentication

await downloadFileFromUrl(
  'https://api.example.com/files/private-doc.pdf',
  'document.pdf',
  { 'Authorization': 'Bearer your-token-here' }
);

With Loading State

const [downloading, setDownloading] = useState(false);

const handleDownload = async () => {
  setDownloading(true);
  await downloadFileFromUrl(
    fileUrl,
    'my-file.pdf',
    null,
    () => setDownloading(false) // onFinish callback
  );
};

Possible Extensions

1. Enhanced Error Handling

// Add more specific error types
if (!response.ok) {
  switch (response.status) {
    case 404:
      throw new Error('File not found');
    case 403:
      throw new Error('Access denied');
    case 500:
      throw new Error('Server error');
    default:
      throw new Error(`Download failed: ${response.statusText}`);
  }
}

2. Progress Tracking

// Add download progress callback
export default async function downloadFileFromUrl(
  url,
  defaultName,
  headers,
  onFinish,
  onProgress // New parameter
) {
  const response = await fetch(url, { method: 'GET', headers: headers || {} });

  if (!response.ok) throw new Error('Download failed');

  const contentLength = response.headers.get('Content-Length');
  const total = parseInt(contentLength, 10);
  let loaded = 0;

  const reader = response.body.getReader();
  const chunks = [];

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    chunks.push(value);
    loaded += value.length;

    if (onProgress && total) {
      onProgress({ loaded, total, percentage: (loaded / total) * 100 });
    }
  }

  const blob = new Blob(chunks);
  // Continue with download logic...
}

3. File Type Validation

// Add MIME type checking
const validateFileType = (response, allowedTypes = []) => {
  const contentType = response.headers.get('Content-Type');
  if (allowedTypes.length && !allowedTypes.includes(contentType)) {
    throw new Error(`Invalid file type: ${contentType}`);
  }
};

4. Memory Management

// Clean up blob URL to prevent memory leaks
const blobUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = filename;
link.click();

// Clean up
setTimeout(() => {
  window.URL.revokeObjectURL(blobUrl);
}, 1000);

5. Custom Hook Version

// useFileDownload hook
const useFileDownload = () => {
  const [downloading, setDownloading] = useState(false);
  const [error, setError] = useState(null);

  const download = useCallback(async (url, defaultName, headers) => {
    try {
      setDownloading(true);
      setError(null);
      await downloadFileFromUrl(url, defaultName, headers);
    } catch (err) {
      setError(err.message);
    } finally {
      setDownloading(false);
    }
  }, []);

  return { download, downloading, error };
};

Common Use Cases

  • API File Downloads: PDFs, Excel reports, images from secured endpoints
  • Document Management Systems: Download user files with proper authentication
  • Export Features: Download generated reports or data exports
  • Media Downloads: Images, videos, audio files

Browser Compatibility

This approach works in all modern browsers that support:

  • Fetch API
  • Blob API
  • Object URLs
  • HTML5 download attribute

For older browsers, consider adding polyfills or fallback methods.

Best Practices

  1. Always handle errors - Network requests can fail
  2. Show loading states - File downloads can take time
  3. Validate file types - Ensure security and user expectations
  4. Clean up resources - Revoke blob URLs to prevent memory leaks
  5. Consider file size limits - Large files might need streaming approaches

This utility provides a solid foundation for file downloads in React apps. Extend it based on your specific requirements!