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
Returns
download(function): Start a browser download. Resolvestruewhen preparation finishes and the download starts, orundefinedon failure.truedoes not mean the user saved the file.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 handleExport = async () => {
const data = JSON.stringify(tasks, null, 2)
const started = await download(data, 'tasks-export.json')
if (!started) return
}
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
download resolves true when the browser download starts and undefined when preparation fails. Failure also sets error and shows the platform toast. true does not mean the user saved the file.
const started = await download(data, 'tasks-export.json')
if (!started) return
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.