packages/response/README.md
Response helper utilities for the web Fetch API. response provides focused helpers for common HTTP responses with correct headers and caching semantics.
Response API, works in any JavaScript runtime (Node.js, Bun, Deno, Cloudflare Workers)npm i remix
This package provides no default export. Instead, import the specific helper you need:
import { createFileResponse } from 'remix/response/file'
import { createHtmlResponse } from 'remix/response/html'
import { createRedirectResponse } from 'remix/response/redirect'
import { compressResponse } from 'remix/response/compress'
The createFileResponse helper creates a response for serving files with full HTTP semantics. It works with both native File objects and LazyFile from @remix-run/lazy-file:
import { createFileResponse } from 'remix/response/file'
import { openLazyFile } from 'remix/fs'
let lazyFile = openLazyFile('./public/image.jpg')
let response = await createFileResponse(lazyFile, request, {
cacheControl: 'public, max-age=3600',
})
If-None-Match, If-Modified-Since, If-Match, If-Unmodified-Since)206 Partial Content)await createFileResponse(file, request, {
// Cache-Control header value.
// Defaults to `undefined` (no Cache-Control header).
cacheControl: 'public, max-age=3600',
// ETag generation strategy:
// - 'weak': Generates weak ETags based on file size and mtime (default)
// - 'strong': Generates strong ETags by hashing file content
// - false: Disables ETag generation
etag: 'weak',
// Hash algorithm for strong ETags (Web Crypto API algorithm names).
// Only used when etag: 'strong'.
// Defaults to 'SHA-256'.
digest: 'SHA-256',
// Whether to generate Last-Modified headers.
// Defaults to `true`.
lastModified: true,
// Whether to support HTTP Range requests for partial content.
// Defaults to `true`.
acceptRanges: true,
})
For assets that require strong validation (e.g., to support If-Match preconditions or If-Range with Range requests), configure strong ETag generation:
return createFileResponse(file, request, {
etag: 'strong',
})
By default, strong ETags are generated using the Web Crypto API with the 'SHA-256' algorithm. You can customize this:
return createFileResponse(file, request, {
etag: 'strong',
// Specify a different hash algorithm
digest: 'SHA-512',
})
For large files or custom hashing requirements, provide a custom digest function:
await createFileResponse(file, request, {
etag: 'strong',
async digest(file) {
// Custom streaming hash for large files
let { createHash } = await import('node:crypto')
let hash = createHash('sha256')
for await (let chunk of file.stream()) {
hash.update(chunk)
}
return hash.digest('hex')
},
})
The createHtmlResponse helper creates HTML responses with proper Content-Type and DOCTYPE handling:
import { createHtmlResponse } from 'remix/response/html'
let response = createHtmlResponse('<h1>Hello, World!</h1>')
// Content-Type: text/html; charset=UTF-8
// Body: <!DOCTYPE html><h1>Hello, World!</h1>
The helper automatically prepends <!DOCTYPE html> if not already present. It works with strings, SafeHtml from @remix-run/html-template, Blobs/Files, ArrayBuffers, and ReadableStreams.
import { html } from 'remix/html-template'
import { createHtmlResponse } from 'remix/response/html'
let name = '<script>alert(1)</script>'
let response = createHtmlResponse(html`<h1>Hello, ${name}!</h1>`)
// Safely escaped HTML
The createRedirectResponse helper creates redirect responses. The main improvements over the native Response.redirect API are:
location instead of a full URL. This isn't technically spec-compliant, but it's so widespread that many applications use relative redirects regularly without issues.ResponseInit object as the second argument, allowing you to set additional headers and status code.import { createRedirectResponse } from 'remix/response/redirect'
// Default 302 redirect
let response = createRedirectResponse('/login')
// Custom status code
let response = createRedirectResponse('/new-page', 301)
// With additional headers
let response = createRedirectResponse('/dashboard', {
status: 303,
headers: { 'X-Redirect-Reason': 'authentication' },
})
The compressResponse helper compresses a Response based on the client's Accept-Encoding header:
import { compressResponse } from 'remix/response/compress'
let response = new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' },
})
let compressed = await compressResponse(response, request)
Compression is automatically skipped for:
Accept-Encoding headerContent-Encoding)Cache-Control: no-transformContent-Length below threshold (default: 1024 bytes)Accept-Ranges: bytes)The compressResponse helper accepts options to customize compression behavior:
await compressResponse(response, request, {
// Minimum size in bytes to compress (only enforced if Content-Length is present).
// Default: 1024
threshold: 1024,
// Which encodings the server supports for negotiation.
// Defaults to ['br', 'gzip', 'deflate']
encodings: ['br', 'gzip', 'deflate'],
// node:zlib options for gzip/deflate compression.
// For SSE responses (text/event-stream), flush: Z_SYNC_FLUSH
// is automatically applied unless you explicitly set a flush value.
// See: https://nodejs.org/api/zlib.html#class-options
zlib: {
level: 6,
},
// node:zlib options for Brotli compression.
// For SSE responses (text/event-stream), flush: BROTLI_OPERATION_FLUSH
// is automatically applied unless you explicitly set a flush value.
// See: https://nodejs.org/api/zlib.html#class-brotlioptions
brotli: {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: 4,
},
},
})
Range requests and compression are mutually exclusive. When Accept-Ranges: bytes is present in the response headers, compressResponse will not compress the response. This is why the createFileResponse helper enables ranges only for non-compressible MIME types by default - to allow text-based assets to be compressed while still supporting resumable downloads for media files.
@remix-run/headers - Type-safe HTTP header manipulation@remix-run/html-template - Safe HTML templating with automatic escaping@remix-run/fs - File system utilities including openFile@remix-run/fetch-router - Build HTTP routers using the web fetch API@remix-run/mime - MIME type utilitiesSee LICENSE