response
The response package provides helper functions for creating common HTTP responses. Each helper handles the correct headers, status codes, and body encoding so you do not have to set them manually.
Installation
The response package is included with Remix. No additional installation is needed.
Import
The main export bundles all helpers:
import {
createHtmlResponse,
createFileResponse,
createRedirectResponse,
compressResponse,
} from 'remix/response'Each helper is also available from its own sub-export for tree-shaking:
import { createHtmlResponse } from 'remix/response/html'
import { createFileResponse } from 'remix/response/file'
import { createRedirectResponse } from 'remix/response/redirect'
import { compressResponse } from 'remix/response/compress'API
createHtmlResponse(body, init?)
Creates a Response with Content-Type: text/html; charset=utf-8 and a <!DOCTYPE html> prefix.
function createHtmlResponse(
body: string,
init?: ResponseInit,
): Responselet response = createHtmlResponse('<html><body>Hello</body></html>')
// Status is 200, Content-Type is text/html; charset=utf-8
// Body is prefixed with <!DOCTYPE html>Pass init to set a custom status or additional headers:
let response = createHtmlResponse('<html><body>Not Found</body></html>', {
status: 404,
headers: {
'Cache-Control': 'no-store',
},
})createFileResponse(file, request, options?)
Creates a Response that streams a File or LazyFile to the client with proper headers for ETag-based caching, conditional requests, and byte-range requests.
function createFileResponse(
file: File,
request: Request,
options?: FileResponseOptions,
): Responseimport { openLazyFile } from 'remix/fs'
let file = await openLazyFile('./uploads/photo.jpg')
let response = createFileResponse(file, request)The function automatically handles:
Content-Type-- Set from the file'stypeproperty.Content-Length-- Set from the file'ssizeproperty.ETag-- Generated from the file's size and last modified time.Last-Modified-- Set from the file'slastModifiedproperty.If-None-Match/If-Modified-Since-- Returns304 Not Modifiedwhen appropriate.Range-- Returns206 Partial Contentfor byte-range requests.Cache-Control-- Set fromoptions.cacheControlif provided.
Options
| Option | Type | Default | Description |
|---|---|---|---|
cacheControl | string | -- | The Cache-Control header value. |
etag | string | Auto-generated | A custom ETag value. |
createRedirectResponse(url, init?)
Creates a redirect Response.
function createRedirectResponse(
url: string,
init?: number | ResponseInit,
): ResponseThe second argument can be a status code or a full ResponseInit:
// 302 Found (default)
createRedirectResponse('/dashboard')
// 301 Moved Permanently
createRedirectResponse('/new-url', 301)
// Custom headers
createRedirectResponse('/dashboard', {
status: 303,
headers: {
'Set-Cookie': 'session=abc; Path=/',
},
})compressResponse(response, encoding)
Compresses a response body using the specified encoding.
function compressResponse(
response: Response,
encoding: 'gzip' | 'br' | 'deflate',
): Responselet response = new Response('Hello, world!')
let compressed = compressResponse(response, 'gzip')
compressed.headers.get('Content-Encoding') // 'gzip'The function:
- Compresses the response body using the specified algorithm.
- Sets the
Content-Encodingheader. - Removes the
Content-Lengthheader (since the compressed size is unknown until streaming completes). - Returns the original response unchanged if the body is empty.
Examples
HTML Page
import { createHtmlResponse } from 'remix/response/html'
router.map(homeRoute, () => {
return createHtmlResponse(`
<html>
<head><title>Home</title></head>
<body><h1>Welcome</h1></body>
</html>
`)
})File Download
import { openLazyFile } from 'remix/fs'
import { createFileResponse } from 'remix/response/file'
router.map(downloadRoute, async ({ request, params }) => {
let file = await openLazyFile(`./files/${params.name}`)
return createFileResponse(file, request, {
cacheControl: 'public, max-age=31536000, immutable',
})
})Post/Redirect/Get Pattern
import { createRedirectResponse } from 'remix/response/redirect'
router.map(createPostRoute, async ({ request }) => {
let formData = await request.formData()
let title = formData.get('title') as string
await createPost(title)
// Redirect to the new post after creation
return createRedirectResponse('/posts', 303)
})Compressed API Response
import { compressResponse } from 'remix/response/compress'
import { AcceptEncoding } from 'remix/headers'
router.map(apiRoute, async ({ request }) => {
let data = await getLargeDataset()
let response = Response.json(data)
let ae = AcceptEncoding.parse(
request.headers.get('Accept-Encoding') ?? '',
)
let encoding = ae.negotiate(['br', 'gzip'])
if (encoding) {
return compressResponse(response, encoding)
}
return response
})Related
- headers --- Typed header parsing and serialization.
- fs --- Read files from disk for serving.
- lazy-file --- Deferred file reading for efficient serving.
- compression-middleware --- Automatic response compression as middleware.