Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/remix-run/remix/llms.txt

Use this file to discover all available pages before exploring further.

Typed utilities for parsing, manipulating, and serializing HTTP header values. headers provides focused classes for common HTTP headers.

Features

  • Header-Specific Classes - Purpose-built APIs for Accept, Cache-Control, Content-Type, and more
  • Round-Trip Safety - Parse from raw values and serialize back with .toString()
  • Typed Operations - Work with structured values instead of manual string parsing

Installation

npm i remix

Supported Headers

Each supported header has a class that represents the header value. Use the static from() method to parse header values. Each class has a toString() method that returns the header value as a string.

Accept

Parse, manipulate and stringify Accept headers. Implements Map<mediaType, quality>.
import { Accept } from 'remix/headers'

// Parse from headers
let accept = Accept.from(request.headers.get('accept'))

accept.mediaTypes // ['text/html', 'text/*']
accept.weights // [1, 0.9]
accept.accepts('text/html') // true
accept.accepts('text/plain') // true (matches text/*)
accept.accepts('image/jpeg') // false
accept.getWeight('text/plain') // 1 (matches text/*)
accept.getPreferred(['text/html', 'text/plain']) // 'text/html'

// Iterate
for (let [mediaType, quality] of accept) {
  // ...
}

// Modify and set header
accept.set('application/json', 0.8)
accept.delete('text/*')
headers.set('Accept', accept)

// Construct directly
new Accept('text/html, text/*;q=0.9')
new Accept({ 'text/html': 1, 'text/*': 0.9 })
new Accept(['text/html', ['text/*', 0.9]])

// Use class for type safety when setting Headers values
let headers = new Headers({
  Accept: new Accept({ 'text/html': 1, 'application/json': 0.8 }),
})

Accept-Encoding

Parse, manipulate and stringify Accept-Encoding headers. Implements Map<encoding, quality>.
import { AcceptEncoding } from 'remix/headers'

// Parse from headers
let acceptEncoding = AcceptEncoding.from(request.headers.get('accept-encoding'))

acceptEncoding.encodings // ['gzip', 'deflate']
acceptEncoding.weights // [1, 0.8]
acceptEncoding.accepts('gzip') // true
acceptEncoding.accepts('br') // false
acceptEncoding.getWeight('gzip') // 1
acceptEncoding.getPreferred(['gzip', 'deflate', 'br']) // 'gzip'

// Modify and set header
acceptEncoding.set('br', 1)
acceptEncoding.delete('deflate')
headers.set('Accept-Encoding', acceptEncoding)

// Construct directly
new AcceptEncoding('gzip, deflate;q=0.8')
new AcceptEncoding({ gzip: 1, deflate: 0.8 })

Accept-Language

Parse, manipulate and stringify Accept-Language headers. Implements Map<language, quality>.
import { AcceptLanguage } from 'remix/headers'

// Parse from headers
let acceptLanguage = AcceptLanguage.from(request.headers.get('accept-language'))

acceptLanguage.languages // ['en-us', 'en']
acceptLanguage.weights // [1, 0.9]
acceptLanguage.accepts('en-US') // true
acceptLanguage.accepts('en-GB') // true (matches en)
acceptLanguage.getWeight('en-GB') // 1 (matches en)
acceptLanguage.getPreferred(['en-US', 'en-GB', 'fr']) // 'en-US'

// Modify and set header
acceptLanguage.set('fr', 0.5)
acceptLanguage.delete('en')
headers.set('Accept-Language', acceptLanguage)

// Construct directly
new AcceptLanguage('en-US, en;q=0.9')
new AcceptLanguage({ 'en-US': 1, en: 0.9 })

Cache-Control

Parse, manipulate and stringify Cache-Control headers.
import { CacheControl } from 'remix/headers'

// Parse from headers
let cacheControl = CacheControl.from(response.headers.get('cache-control'))

cacheControl.public // true
cacheControl.maxAge // 3600
cacheControl.sMaxage // 7200
cacheControl.noCache // undefined
cacheControl.noStore // undefined
cacheControl.noTransform // undefined
cacheControl.mustRevalidate // undefined
cacheControl.immutable // undefined

// Modify and set header
cacheControl.maxAge = 7200
cacheControl.immutable = true
headers.set('Cache-Control', cacheControl)

// Construct directly
new CacheControl('public, max-age=3600')
new CacheControl({ public: true, maxAge: 3600 })

Content-Disposition

Parse, manipulate and stringify Content-Disposition headers.
import { ContentDisposition } from 'remix/headers'

// Parse from headers
let contentDisposition = ContentDisposition.from(
  response.headers.get('content-disposition')
)

contentDisposition.type // 'attachment'
contentDisposition.filename // 'example.pdf'
contentDisposition.filenameSplat // "UTF-8''%E4%BE%8B%E5%AD%90.pdf"
contentDisposition.preferredFilename // '例子.pdf' (decoded from filename*)

// Modify and set header
contentDisposition.filename = 'download.pdf'
headers.set('Content-Disposition', contentDisposition)

// Construct directly
new ContentDisposition('attachment; filename="example.pdf"')
new ContentDisposition({ type: 'attachment', filename: 'example.pdf' })

Content-Range

Parse, manipulate and stringify Content-Range headers.
import { ContentRange } from 'remix/headers'

// Parse from headers
let contentRange = ContentRange.from(response.headers.get('content-range'))

contentRange.unit // "bytes"
contentRange.start // 200
contentRange.end // 1000
contentRange.size // 67589

// Unsatisfied range
let unsatisfied = ContentRange.from('bytes */67589')
unsatisfied.start // null
unsatisfied.end // null
unsatisfied.size // 67589

// Construct directly
new ContentRange({ unit: 'bytes', start: 0, end: 499, size: 1000 })

Content-Type

Parse, manipulate and stringify Content-Type headers.
import { ContentType } from 'remix/headers'

// Parse from headers
let contentType = ContentType.from(request.headers.get('content-type'))

contentType.mediaType // "text/html"
contentType.charset // "utf-8"
contentType.boundary // undefined (or boundary string for multipart)

// Modify and set header
contentType.charset = 'iso-8859-1'
headers.set('Content-Type', contentType)

// Construct directly
new ContentType('text/html; charset=utf-8')
new ContentType({ mediaType: 'text/html', charset: 'utf-8' })
Parse, manipulate and stringify Cookie headers. Implements Map<name, value>.
import { Cookie } from 'remix/headers'

// Parse from headers
let cookie = Cookie.from(request.headers.get('cookie'))

cookie.get('session_id') // 'abc123'
cookie.get('theme') // 'dark'
cookie.has('session_id') // true
cookie.size // 2

// Iterate
for (let [name, value] of cookie) {
  // ...
}

// Modify and set header
cookie.set('theme', 'light')
cookie.delete('session_id')
headers.set('Cookie', cookie)

// Construct directly
new Cookie('session_id=abc123; theme=dark')
new Cookie({ session_id: 'abc123', theme: 'dark' })
new Cookie([
  ['session_id', 'abc123'],
  ['theme', 'dark'],
])

If-Match

Parse, manipulate and stringify If-Match headers. Implements Set<etag>.
import { IfMatch } from 'remix/headers'

// Parse from headers
let ifMatch = IfMatch.from(request.headers.get('if-match'))

ifMatch.tags // ['"67ab43"', '"54ed21"']
ifMatch.has('"67ab43"') // true
ifMatch.matches('"67ab43"') // true (checks precondition)
ifMatch.matches('"abc123"') // false

// Note: Uses strong comparison only (weak ETags never match)
let weak = IfMatch.from('W/"67ab43"')
weak.matches('W/"67ab43"') // false

// Modify and set header
ifMatch.add('"newetag"')
ifMatch.delete('"67ab43"')
headers.set('If-Match', ifMatch)

// Construct directly
new IfMatch(['abc123', 'def456'])

If-None-Match

Parse, manipulate and stringify If-None-Match headers. Implements Set<etag>.
import { IfNoneMatch } from 'remix/headers'

// Parse from headers
let ifNoneMatch = IfNoneMatch.from(request.headers.get('if-none-match'))

ifNoneMatch.tags // ['"67ab43"', '"54ed21"']
ifNoneMatch.has('"67ab43"') // true
ifNoneMatch.matches('"67ab43"') // true

// Supports weak comparison (unlike If-Match)
let weak = IfNoneMatch.from('W/"67ab43"')
weak.matches('W/"67ab43"') // true

// Modify and set header
ifNoneMatch.add('"newetag"')
ifNoneMatch.delete('"67ab43"')
headers.set('If-None-Match', ifNoneMatch)

// Construct directly
new IfNoneMatch(['abc123'])

If-Range

Parse, manipulate and stringify If-Range headers.
import { IfRange } from 'remix/headers'

// Parse from headers
let ifRange = IfRange.from(request.headers.get('if-range'))

// With HTTP date
ifRange.matches({ lastModified: 1609459200000 }) // true
ifRange.matches({ lastModified: new Date('2021-01-01') }) // true

// With ETag
let etagHeader = IfRange.from('"67ab43"')
etagHeader.matches({ etag: '"67ab43"' }) // true

// Empty/null returns empty instance (range proceeds unconditionally)
let empty = IfRange.from(null)
empty.matches({ etag: '"any"' }) // true

// Construct directly
new IfRange('"abc123"')

Range

Parse, manipulate and stringify Range headers.
import { Range } from 'remix/headers'

// Parse from headers
let range = Range.from(request.headers.get('range'))

range.unit // "bytes"
range.ranges // [{ start: 200, end: 1000 }]
range.canSatisfy(2000) // true
range.canSatisfy(500) // false
range.normalize(2000) // [{ start: 200, end: 1000 }]

// Multiple ranges
let multi = Range.from('bytes=0-499, 1000-1499')
multi.ranges.length // 2

// Suffix range (last N bytes)
let suffix = Range.from('bytes=-500')
suffix.normalize(2000) // [{ start: 1500, end: 1999 }]

// Construct directly
new Range({ unit: 'bytes', ranges: [{ start: 0, end: 999 }] })
Parse, manipulate and stringify Set-Cookie headers.
import { SetCookie } from 'remix/headers'

// Parse from headers
let setCookie = SetCookie.from(response.headers.get('set-cookie'))

setCookie.name // "session_id"
setCookie.value // "abc"
setCookie.path // "/"
setCookie.httpOnly // true
setCookie.secure // true
setCookie.domain // undefined
setCookie.maxAge // undefined
setCookie.expires // undefined
setCookie.sameSite // undefined

// Modify and set header
setCookie.maxAge = 3600
setCookie.sameSite = 'Strict'
headers.set('Set-Cookie', setCookie)

// Construct directly
new SetCookie('session_id=abc; Path=/; HttpOnly; Secure')
new SetCookie({
  name: 'session_id',
  value: 'abc',
  path: '/',
  httpOnly: true,
  secure: true,
})

Vary

Parse, manipulate and stringify Vary headers. Implements Set<headerName>.
import { Vary } from 'remix/headers'

// Parse from headers
let vary = Vary.from(response.headers.get('vary'))

vary.headerNames // ['accept-encoding', 'accept-language']
vary.has('Accept-Encoding') // true (case-insensitive)
vary.size // 2

// Modify and set header
vary.add('User-Agent')
vary.delete('Accept-Language')
headers.set('Vary', vary)

// Construct directly
new Vary('Accept-Encoding, Accept-Language')
new Vary(['Accept-Encoding', 'Accept-Language'])
new Vary({ headerNames: ['Accept-Encoding', 'Accept-Language'] })

Raw Headers

Parse and stringify raw HTTP header strings.
import { parse, stringify } from 'remix/headers'

let headers = parse('Content-Type: text/html\r\nCache-Control: no-cache')
headers.get('content-type') // 'text/html'
headers.get('cache-control') // 'no-cache'

stringify(headers)
// 'Content-Type: text/html\r\nCache-Control: no-cache'