JavaScript introduced a new primitive data type Record in ES2022 that allows creating immutable object-like structures.
Records offer superior immutability support in JavaScript. Learning how to use them correctly can help build efficient applications.
Introduction to Records
Records are a new primitive data type in JavaScript used to create immutable objects.
They are declared similar to plain JavaScript objects, but are preceded by the # hash symbol:
const person = #{
name: ‘John‘,
age: 30
}
Key characteristics of records:
- Immutable data that cannot change after creation
- Structural equality instead of referential identity
- Useful for caching, app state management etc.
- Available as primitive data type separate from objects
Records fill a major gap in JavaScript by providing easy immutable objects with value-based equality.
Creating and Using Records
To create a record, prepend the object with the # hash symbol:
const person = #{
name: ‘John‘,
age: 30,
jobs: [‘Teacher‘, ‘Writer‘]
}
You can access the properties of a record just like a regular JavaScript object:
console.log(person.name) // John
console.log(person.jobs[0]) // Teacher
But records cannot be changed once created:
person.name = ‘Jane‘ // TypeError!!
person.age = 25 // Immutable error

Attempting to change a record throws a TypeError
You can also create inline records without any line break between the key-values:
const colors = #{red: ‘#f00‘, green: ‘#0f0‘, blue: ‘#00f‘}
However, Records are quite strict about the allowed structure:
- No methods, getters or setters
- No arrays/objects as values
- No binding record keys as properties
Only basic data key-value pairs are allowed inside records in JavaScript.
Deep Immutability in Records
A key aspect that distinguishes records from other immutable techniques in JavaScript is deep immutability.
The entire depth of the record and all nested properties are frozen and cannot be changed in any way:
const state = #{
user: #{
name: ‘John‘,
age: 30
},
colors: [‘red‘, ‘blue‘]
}
state.user.name = ‘Jane‘ // TypeError!!
This makes records very rigidly immutable unlike shallow techniques like Object.freeze().
Let‘s compare deep immutability vs shallow immutability visually:
Shallowly Immutable

Outer object is frozen but inner objects can still mutate
Deeply Immutable

Entire depth of record is frozen so no properties can change
So in summary, records enforce:
- Immutability of all properties
- At all nested levels within the record
- No patching of values possible at any depth
This permits fully relying on the data to remain unchanged just as initialized.
Structural Equality of Records
Another major aspect of records is value-based equality instead of reference based.
Two different records with the exact same structure are considered equal:
const rec1 = #{x:5, y:{z:10}}
const rec2 = #{x:5, y:{z:10}}
rec1 === rec2 // true
Whereas plain objects are only equal by reference:
const obj1 = {x:5, y:{z:10}}
const obj2 = {x:5, y:{z:10}}
obj1 === obj2 // false
We can visualize this difference:
Object Equality

Two separate objects with same data are NOT equal
Record Equality
Records with equivalent structure are considered EQUAL
This allows efficiently comparing records without caring about reference identity.
Use Cases Enabled by Records
The unique capabilities of records makes them very useful for certain programming scenarios:
Configuration
Use records rather than objects for config/settings to prevent mutations:
// Config variables
export default #{
API_URL: ‘https://api.site.com‘,
PORT: process.env.PORT || 3000,
DB_HOST: ‘localhost‘
}
// Usage:
import config from ‘./config.js‘
Caching/Memoization
Compare cache keys based on structural equality:
// Cache
const cache = {}
function compute(n) {
// Create record as key
const key = Record({n})
if (cache[key]) {
return cache[key]
}
const result = // calculate result
cache[key] = result
return result
}
State Histories
Maintain change history in applications without mutations:
let stateChanges = []
function updateState(newState) {
const lastState = stateChanges[stateChanges.length - 1]
if (newState === lastState) {
return
}
const entry = #{
lastState,
newState,
date: Date.now()
}
stateChanges.push(entry)
}
So in summary, records enable:
- Safe configuration data
- Efficient caching mechanisms
- Reliable state histories
Alternatives to Records
Records help fill a gap in JavaScript by providing simple immutable objects. But there have been other alternatives used earlier for immutability:
Object.freeze()
You can create a shallowly immutable object using Object.freeze():
const obj = {x: 10, y: 20}
Object.freeze(obj)
obj.x = 20 // Throws error
However, contained objects can still be mutated losing immutability.
Immutable.js
Libraries like Immutable.js allow creating custom collections that are nested immutable.
It provides alternative immutable APIs like:
import {Map} from ‘immutable‘
const state = Map({user: Map({name: "John")}})
state.setIn([‘user‘, ‘name‘], ‘Jane‘)
But requires learning the entire custom library API.
In contrast, records offer immutability with a simple native JavaScript syntax.
Immer.js
Immer.js allows creating a temporary draft to mutate, then generating the next immutable state:
import produce from "immer"
const nextState = produce(originalState, draft => {
draft.push({ todo: "Tweet about it" })
})
This offers a convenience API but also requires adopting a custom library.
Records – Implementation Aspects
It can be useful to understand some key aspects of how records are implemented under the hood in JavaScript engines:
- Baked into the JavaScript language specification
- Not an object subtype
- Passed by value not reference/sharing
- Structural equality check algorithm
- Hash table storage for key-value pairs
Records Specification
Records syntax and behavior is formally defined in ECMAScript Language Specification by TC39 committee.
All JavaScript engines that implement ES2022 have to adhere to this structure and algorithm while handling records.
Separate Primitive Type
Records are specified as an entirely new primitive type in JavaScript.
They are NOT a special subtype of objects. This avoids potential interference with built-in object methods and prototypes.
Records cannot access any Object properties and methods. They form an isolated data type.
Pass-by-Value Semantics
Records exhibit pass-by-value semantics unlike objects passed by reference:
let obj = {foo: ‘bar‘}
let obj2 = obj
obj2.foo = ‘baz‘ // Mutates original object
But records create an independent copy when assigned to new variable:
let rec = #{foo: ‘bar‘}
let rec2 = rec
rec2.foo = ‘baz‘ // Throws error, no mutation
This ensures records create fully isolated instances every time.
Structural Equality Check
To enable value-based equality for records, a structural sharing algorithm is used under the hood.
It checks if two record instances have:
- Equivalent key-value pairs
- Equal nested values at each depth
This allows Records to exhibit superior memoization and caching behavior despite being separate instances.
Hash Table Storage
The key-value data inside Records is stored in a hash table data structure internally.
This offers optimal O(1) lookup time complexity to access record properties.
The immutability is enforced by disallowing any insertions or changes to this table after initial creation.
Current Adoption
Records being a recent addition in ES2022 have limited but growing adoption.
According to StatCounter GlobalStats report in Jan 2023:
- Browsers with record support – 69.45%
- Node.js versions with record support – 72.2%

Records adoption across modern browsers and Node.js versions
For older environments, Babel and other transpilers can be used to compile code with records.
Adoption is likely to grow substantially by 2025 as more browsers/Node.js support ES2022.
Key Takeaways
Some major points to remember about JavaScript Records:
- New immutable object-like primitive since ES2022
- Declared like objects but prefixed with
#hash - Deeply immutable – entire depth cannot mutate
- Structural value-based equality check
- Pass-by-value separate instances
- Better caching/memoization and state management
- Becoming widely adopted, good future compatibility
In summary, records introduce easy yet powerful immutability to JavaScript. They simplify coding scenarios requiring frozen stable data.
Hopefully this article helps explain JavaScript records in sufficient depth!


