Skip to content

lazy-file

The lazy-file package provides LazyFile and LazyBlob, which are drop-in replacements for the standard File and Blob classes that defer reading content until it is actually accessed. This makes them ideal for serving files from disk or object storage without loading entire files into memory upfront.

Installation

The lazy file package is included with Remix. No additional installation is needed.

Import

ts
import { LazyFile, LazyBlob } from 'remix/lazy-file'

API

LazyFile

A File-compatible object that defers reading its content. It implements the full File interface (name, type, size, lastModified, arrayBuffer(), text(), stream(), slice()).

ts
class LazyFile extends LazyBlob implements File {
  constructor(
    read: () => ReadableStream<Uint8Array> | Promise<ReadableStream<Uint8Array>>,
    name: string,
    options: LazyFileOptions,
  )

  readonly name: string
  readonly lastModified: number
}
OptionTypeDescription
typestringThe MIME type of the file.
sizenumberThe byte length of the file.
lastModifiednumberTimestamp in milliseconds. Defaults to Date.now().

The read function is called only when the file's content is actually accessed via arrayBuffer(), text(), stream(), or slice().

ts
let file = new LazyFile(
  () => fetch('https://example.com/photo.jpg').then(r => r.body!),
  'photo.jpg',
  { type: 'image/jpeg', size: 1048576 },
)

// No network request has been made yet
console.log(file.name) // 'photo.jpg'
console.log(file.size) // 1048576

// The read function is called now
let stream = file.stream()

LazyBlob

A Blob-compatible object with deferred content reading.

ts
class LazyBlob implements Blob {
  constructor(
    read: () => ReadableStream<Uint8Array> | Promise<ReadableStream<Uint8Array>>,
    options: LazyBlobOptions,
  )

  readonly type: string
  readonly size: number

  arrayBuffer(): Promise<ArrayBuffer>
  text(): Promise<string>
  stream(): ReadableStream<Uint8Array>
  slice(start?: number, end?: number, type?: string): Blob
}
OptionTypeDescription
typestringThe MIME type of the blob.
sizenumberThe byte length of the blob.

getByteLength()

Both LazyFile and LazyBlob expose their size through the standard size property without loading content into memory. This is useful for setting Content-Length headers or checking file sizes before streaming.

ts
let file = new LazyFile(readFn, 'video.mp4', {
  type: 'video/mp4',
  size: 104857600, // 100 MB
})

// No content loaded -- size is known immediately
console.log(file.size) // 104857600

Byte Range Support

slice() creates a new blob representing a sub-range of the content. Combined with lazy reading, this enables efficient byte-range serving.

ts
let file = new LazyFile(readFn, 'video.mp4', {
  type: 'video/mp4',
  size: 104857600,
})

// Serve bytes 0-1023
let chunk = file.slice(0, 1024)
let stream = chunk.stream()

Examples

Efficient File Serving

Serve a file from a URL without buffering it in memory:

ts
import { LazyFile } from 'remix/lazy-file'
import { createFileResponse } from 'remix/response/file'

router.map(fileRoute, async ({ request, params }) => {
  let metadata = await getFileMetadata(params.id)

  let file = new LazyFile(
    () => fetch(metadata.url).then(r => r.body!),
    metadata.name,
    {
      type: metadata.mimeType,
      size: metadata.size,
    },
  )

  return createFileResponse(file, request)
})

Lazy File from Storage

ts
import { LazyFile } from 'remix/lazy-file'

let file = new LazyFile(
  () => storage.getStream('avatars/user-123.jpg'),
  'avatar.jpg',
  { type: 'image/jpeg', size: 50000 },
)

// Pass to a response -- content is streamed on demand
return new Response(file.stream(), {
  headers: {
    'Content-Type': file.type,
    'Content-Length': String(file.size),
  },
})

Lazy Blob for API Responses

ts
import { LazyBlob } from 'remix/lazy-file'

let blob = new LazyBlob(
  () => generateReport(),
  { type: 'application/pdf', size: reportSize },
)

return new Response(blob.stream(), {
  headers: { 'Content-Type': blob.type },
})
  • fs --- Create LazyFile instances from disk with openLazyFile.
  • response --- createFileResponse for serving files with ETag and range support.
  • file-storage --- File storage abstraction.

Released under the MIT License.