JavaScriptImmutabilityRecords & TuplesPerformanceES2026

JavaScript Records & Tuples: Immutable Data is Finally Native

C
Core Platform Engineer
Featured Guide 20 min read

{} === {} is finally True.

For 30 years, JavaScript developers have struggled with object identity. Infinite loops in useEffect, unnecessary React re-renders, and complex memoization checks.

Enter Records & Tuples: Native, immutable data structures that compare by content, not reference.

02. The Hash Syntax

Simply put a hash # before your object or array literal. That's it. It is now deeply immutable and compares by value.

Old Object (Reference)

const a = { x: 1 };
const b = { x: 1 };

console.log(a === b); 
// ❌ False (Different memory address)
                    

New Record (Value)

const a = #{ x: 1 };
const b = #{ x: 1 };

console.log(a === b); 
// ✅ True (Same content)
                    

03. The React Revolution

This kills useMemo for 90% of use cases. If you pass a Record to a child component, React's shallow comparison just works. No more prevProps.obj === nextProps.obj failures.

// No memo needed!
<Chart config={#{ theme: 'dark', data: #[1, 2, 3] }} />

04. The Killer Feature: Composite Map Keys

Previously, using an object as a Map key relied on its reference. You couldn't create a "fresh" object and look up a value. With Records, Value Object keys are finally possible.

Before (Broken)

const cache = new Map();
cache.set({x:1, y:2}, "Hit!");

// Returns undefined because this object 
// is a DIFFERENT reference
cache.get({x:1, y:2}); // ❌ undefined
                     

After (Works)

const cache = new Map();
cache.set(#{x:1, y:2}, "Hit!");

// Works because the Record is compared 
// by value, not reference
cache.get(#{x:1, y:2}); // ✅ "Hit!"
                     

05. How it Works: Structural Sharing

You might think identifying two deep objects as "equal" is slow (O(N) recursion). But engine implementers use Structural Sharing.

When you modify a Record: const newRec = #{ ...oldRec, b: 2 }
The engine doesn't copy the entire tree. It points newRec to the same memory locations as oldRec for all unchanged properties. Comparison is often as fast as O(1) hashing or pointer checking for shared sub-trees.

06. The Senior Engineer's Take

Implementation Details Matter

Under the hood, engines like V8 use "structural sharing" (similar to Immutable.js tries) to keep memory usage low.

Warning: Records & Tuples are strictly immutable. You cannot mutate them. If you need mutation, you must create a new version (spread operator works: #{ ...old, newProp: 1 }).

Interactive Playground

import React, { useState } from 'react';

// ⚖️ Equality Visualizer

export default function EqualityDemo() {
    const [mode, setMode] = useState('record'); // object | record
    
    // Simulating objects created in memory
    // In React state, these would be new references on every render if defined inline
    // We simulate "checking equality" between two identical looking items
    
    const objA = { id: 1, config: { color: 'red' } };
    const objB = { id: 1, config: { color: 'red' } };
    
    // Visual representation of Memory Address
    const addrA = "0x001F"; // Fake
    const addrB = "0x00A4"; // Fake for Object, same for Record simulation logic
    
    // For Records, the engine effectively treats them as the same value
    const isRecord = mode === 'record';
    const areEqual = isRecord; 

    return (
        <div className="bg-slate-50 dark:bg-slate-950 p-8 rounded-3xl border border-slate-200 dark:border-slate-800 shadow-xl">
             <div className="flex justify-between items-center mb-10">
                <h3 className="text-2xl font-black text-gray-900 dark:text-white flex items-center gap-3">
                    <span className="text-purple-500">⚖️</span> Equality Check
                </h3>
                <div className="flex bg-slate-200 dark:bg-slate-900 p-1 rounded-xl">
                    <button 
                        onClick={() => setMode('object')}
                        className={`px-6 py-2 rounded-lg font-bold text-sm transition-all ${mode === 'object' ? 'bg-white dark:bg-slate-800 shadow text-gray-600' : 'text-slate-500'}`}
                    >
                        Regular Object {}
                    </button>
                    <button 
                        onClick={() => setMode('record')}
                        className={`px-6 py-2 rounded-lg font-bold text-sm transition-all ${mode === 'record' ? 'bg-white dark:bg-slate-800 shadow text-purple-500' : 'text-slate-500'}`}
                    >
                        Record #{}
                    </button>
                </div>
            </div>

            <div className="flex flex-col md:flex-row gap-8 items-center justify-center">
            
                {/* Item A */}
                <div className="relative p-6 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 shadow-lg w-64 text-center">
                    <div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-gray-200 dark:bg-slate-700 px-2 py-1 rounded text-[10px] font-mono font-bold text-gray-500">
                        {isRecord ? 'Value: #{...}' : `Ref: ${addrA}`}
                    </div>
                    <div className="mx-auto mb-4 text-4xl">
                        {isRecord ? '📦' : '📦'} 
                    </div>
                    <pre className="text-left text-xs bg-slate-100 dark:bg-black p-2 rounded">
{isRecord ? '#{' : '{'}
  id: 1, 
  color: 'red' 
{isRecord ? '}' : '}'}
                    </pre>
                </div>

                {/* Operator */}
                <div className="text-2xl font-black text-gray-300">
                    ===
                </div>

                {/* Item B */}
                 <div className="relative p-6 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 shadow-lg w-64 text-center">
                    <div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-gray-200 dark:bg-slate-700 px-2 py-1 rounded text-[10px] font-mono font-bold text-gray-500">
                         {isRecord ? 'Value: #{...}' : `Ref: ${addrB}`}
                    </div>
                    <div className="mx-auto mb-4 text-4xl">
                        {isRecord ? '📦' : '📦'}
                    </div>
                    <pre className="text-left text-xs bg-slate-100 dark:bg-black p-2 rounded">
{isRecord ? '#{' : '{'}
  id: 1, 
  color: 'red' 
{isRecord ? '}' : '}'}
                    </pre>
                </div>

            </div>
            
            {/* Result */}
            <div className={`mt-10 p-6 rounded-2xl flex items-center justify-center gap-4 text-2xl font-black border-2 ${
                areEqual 
                ? 'bg-green-50 dark:bg-green-900/20 border-green-500 text-green-600' 
                : 'bg-red-50 dark:bg-red-900/20 border-red-500 text-red-600'
            }`}>
                <span className="text-3xl">{areEqual ? '✅' : '❌'}</span>
                {areEqual ? 'TRUE' : 'FALSE'}
            </div>
            
             <p className="text-center mt-4 text-gray-500 text-sm">
                {isRecord 
                    ? "Records compare by their content. Since the content is identical, they are equal." 
                    : "Objects compare by reference (memory address). Even if content is identical, they are different instances."}
            </p>

        </div>
    );
}