File Download Reference
Use useDownload to save generated data, uploaded files, AI outputs, and client-side media processing results to the user's device.
useDownload
Hook Options
onError((error: Error) => void): Optional custom error handler. Providing it suppresses the default error toast.
Returns
download(function): Trigger a file download.isDownloading(boolean): Whether a download is in progress.error(Error | null): Current download error.clearError(() => void): Clear the current error.
download Arguments
data(Blob,string,ArrayBuffer, or{ url: string }, required): Data or URL to download.filename(string): Optional filename. When omitted for aFile, the file's own name is used.mediaType(string): Optional media type override. Otherwise it is inferred from the filename.
Supported Data Types
Blob: Binary images, audio, video, or generated files.string: Text content or a data URL.ArrayBuffer: Raw binary data.{ url: string }: Hosted file URL.
Examples
Export JSON:
const { download } = useDownload()
const data = JSON.stringify(tasks, null, 2)
download(data, 'tasks-export.json')
Download a hosted image:
download({ url: imageUrl }, 'generated-image.png')
Download a File returned by useVideoStitch or useVideoAudioOverlay:
download(result)
Omitting the filename uses the File object's existing name and .mp4 extension.
Batch Downloads
For multiple files in one archive, combine JSZip with useDownload:
import JSZip from 'jszip'
import { useDownload } from '@/hooks/use-download'
type Panel = { id: string; imageUrl: string; name: string }
export default function App({ panels }: { panels: Panel[] }) {
const { download } = useDownload()
const [isZipping, setIsZipping] = React.useState(false)
const handleDownloadAll = async () => {
setIsZipping(true)
try {
const zip = new JSZip()
for (let index = 0; index < panels.length; index++) {
const panel = panels[index]
const response = await fetch(panel.imageUrl)
if (!response.ok) {
throw new Error(`Failed to fetch ${panel.imageUrl}`)
}
const blob = await response.blob()
const filename = `${String(index + 1).padStart(2, '0')}-${panel.name}.png`
zip.file(filename, blob)
}
const zipBlob = await zip.generateAsync({ type: 'blob' })
download(zipBlob, 'my-export.zip')
} catch (error) {
console.error('Failed to create ZIP archive:', error)
} finally {
setIsZipping(false)
}
}
return (
<button
onClick={handleDownloadAll}
disabled={isZipping || panels.length === 0}
>
{isZipping ? (
<span className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
) : null}
Download all
</button>
)
}
Useful JSZip operations:
const zip = new JSZip()
zip.file('readme.txt', 'Hello World')
zip.file('data.json', JSON.stringify(data, null, 2))
zip.folder('images')?.file('photo.png', imageBlob)
const blob = await zip.generateAsync({ type: 'blob' })
Error Handling
const { download } = useDownload({
onError: (err) => {
console.error('Download failed:', err.message)
},
})
Best Practices
- Let the hook infer media types from filenames when possible.
- Omit the filename for
Fileresults that already include the correct extension. - Use
{ url }for hosted files and generated media URLs. - Show a loading state when building ZIP files or fetching many remote files.
- Use zero-padded filenames in ZIP exports so files sort correctly.
- Organize ZIP contents with folders and include a manifest when exports need context.
- Handle remote fetch failures when batching files from URLs.