Modern replacement of file-saver.js.
npm install tinysaveryarn add tinysaverpnpm add tinysaverimport{saveAs,saveAsAsync,saveText,saveJSON,saveCanvas}from'tinysaver'import{saveStream}from'tinysaver/stream'// Save a BlobsaveAs(newBlob(['hello world'],{type: 'text/plain'}),'hello-world.txt')// Save text contentsaveText('Hello World','greeting.txt')// Save JSON datasaveJSON({name: 'John',age: 30},'data.json',{space: 2})// Save canvas as imageconstcanvas=document.querySelector('canvas')saveCanvas(canvas,'image.png',{quality: 0.95})// Promise-based saveawaitsaveAsAsync(newBlob(['hello async']),'async.txt')// Save from stream/responseconstresponse=awaitfetch('/api/export')awaitsaveStream(response,'export.bin')Stream support is published from a separate entry so applications that only use Blob, URL, text, JSON, or canvas downloads do not bundle the stream implementation.
import{saveStream,StreamDownloader,typeDownloadStreamSource,typeSaveStreamOptions,}from'tinysaver/stream'awaitsaveStream(awaitfetch('/api/export'),'export.bin')constdownloader=newStreamDownloader()awaitdownloader.saveStream(source,'export.bin',options)saveStream is not exported from tinysaver, and FileDownloader no longer
has a saveStream method. Migrate root imports and class usage as follows:
// Beforeimport{FileDownloader,saveStream}from'tinysaver'// Afterimport{saveStream,StreamDownloader}from'tinysaver/stream'import{saveAs}from'tinysaver'saveAs(newBlob(['hello world'],{type: 'text/plain'}),'hello-world.txt',{autoBom: true,// Add UTF-8 BOM for text filesclickDelay: 100,// Delay before triggering downloadopenInNewTab: false,// Open in new tab instead of downloadingdisableClick: false,// Disable automatic click simulationonStart(){console.log('Download started')},onComplete(){console.log('Download completed')},onError(err){console.error('Download failed',err)},onProgress(loaded,total){console.log(`${loaded}/${total}`)// total is 0 when size is unknown},timeout: 10_000,// Abort automatically after 10ssignal: abortController.signal,// Manual cancellationfetchOptions: {credentials: 'include'},// For CORS probing requestpreferFileSystemAccess: true,// Use showSaveFilePicker for Blob/stream sourcesonPhaseChange(phase){console.log(phase)// probing/downloading/saving/completed/error/aborted},})All download methods support lifecycle callbacks:
saveText('content','file.txt',{onStart(){// Called when download process starts},onProgress(loaded,total){// Called during download progressconsole.log(`Downloaded ${loaded}/${total} bytes`)},onComplete(){// Called when download completes},onError(error){// Called when download failsconsole.error(error)},})tinysaver targets the following browser versions and newer:
- Chrome 87
- Edge 88
- Firefox 78
- Safari 14
Internet Explorer is not supported.
Save any Blob or URL as a file. Compatible with FileSaver.js saveAs API.
Promise-based version of saveAs, suitable for async workflows and explicit error handling.
Parameters:
blob- Blob object or URL stringfilename- Name of the file to save (optional)options- Download options (optional)
Save text content as a file.
Parameters:
text- Text content to savefilename- Name of the text fileoptions- Download options with optionalmimeTypeproperty
Save JSON data as a file.
Parameters:
data- JavaScript object or value to savefilename- Name of the JSON fileoptions- Download options with optionalspaceproperty for formatting
Save HTML canvas as an image file.
Parameters:
canvas- HTMLCanvasElement to savefilename- Name of the image fileoptions- Download options with optionaltypeandqualityproperties
Save stream data (ReadableStream, Response, AsyncIterable) as a file.
Import it from tinysaver/stream.
When preferFileSystemAccess is enabled and supported, chunks are written
directly to the selected file. Other browsers materialize the stream as a
browser-managed Blob before starting the download. Abort signals and timeouts
cancel the active stream and propagate cancellation to its source.
Blob fallback buffering is limited to 256 MiB by default to protect the browser
from unbounded memory growth. Set maxBufferBytes to a positive byte limit when
another bound is appropriate for the application.
URL sources always use the browser download transport. File pickers require a transient user gesture and therefore cannot be opened after an asynchronous URL probe or transfer.
The library includes comprehensive unit tests covering:
- 📥 FileDownloader core functionality and error handling
- 🏷️ BOM (Byte Order Mark) insertion for text files
- 📝 Default filename handling
- 💾 saveText, saveJSON, and saveCanvas implementations
⚠️ Canvas conversion error handling- 🔔 Callback invocation during download lifecycle
- ⏱️ Timeout/abort behavior and phase callbacks
- 🌊 Stream-based save flow
- 📂 File System Access API preferred path
- Unit tests run in
jsdomfor deterministic behavior. - Browser tests run in Chromium, Firefox, and WebKit through Playwright.
- Production builds target Chrome 87, Edge 88, Firefox 78, and Safari 14.
- Downloads use the standard anchor
downloadattribute andURLAPI. - IE-only and pre-modern browser fallbacks are intentionally excluded.
Run tests with:
pnpm test
pnpm test:browserThe Vue playground contains interactive examples for saving text, JSON, canvas artwork, and generated streams directly in the browser.
pnpm playgroundRun pnpm playground:build to verify its production bundle.