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 Blob
saveAs(new Blob(['hello world'], { type: 'text/plain' }), 'hello-world.txt')
// Save text content
saveText('Hello World', 'greeting.txt')
// Save JSON data
saveJSON({ name: 'John', age: 30 }, 'data.json', { space: 2 })
// Save canvas as image
const canvas = document.querySelector('canvas')
saveCanvas(canvas, 'image.png', { quality: 0.95 })
// Promise-based save
await saveAsAsync(new Blob(['hello async']), 'async.txt')
// Save from stream/response
const response = await fetch('/api/export')
await saveStream(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,
type DownloadStreamSource,
type SaveStreamOptions,
} from 'tinysaver/stream'
await saveStream(await fetch('/api/export'), 'export.bin')
const downloader = new StreamDownloader()
await downloader.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:
// Before
import { FileDownloader, saveStream } from 'tinysaver'
// After
import { saveStream, StreamDownloader } from 'tinysaver/stream'import { saveAs } from 'tinysaver'
saveAs(new Blob(['hello world'], { type: 'text/plain' }), 'hello-world.txt', {
autoBom: true, // Add UTF-8 BOM for text files
clickDelay: 100, // Delay before triggering download
openInNewTab: false, // Open in new tab instead of downloading
disableClick: false, // Disable automatic click simulation
onStart() {
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 10s
signal: abortController.signal, // Manual cancellation
fetchOptions: { credentials: 'include' }, // For CORS probing request
preferFileSystemAccess: true, // Use showSaveFilePicker for Blob/stream sources
onPhaseChange(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 progress
console.log(`Downloaded ${loaded}/${total} bytes`)
},
onComplete() {
// Called when download completes
},
onError(error) {
// Called when download fails
console.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.