Array.prototype.with()
with(index, value) The with() method returns a new array with the element at the specified index replaced with the new value. Unlike the traditional approach of mutating an array using bracket notation, with() creates a new array and leaves the original unchanged—making your code more predictable and functional.
This method is part of the ES2023 array copying methods that provide immutable alternatives to mutating operations.
Syntax
array.with(index, value)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
index | integer | Required | The zero-based index at which to replace the element. Negative indices count from the end of the array. |
value | any | Required | The value to place at the specified index. |
Examples
Basic usage
const fruits = ['apple', 'banana', 'cherry'];
// Replace element at index 1
const updated = fruits.with(1, 'blueberry');
console.log(updated);
// ['apple', 'blueberry', 'cherry']
// Original array remains unchanged
console.log(fruits);
// ['apple', 'banana', 'cherry']
The result is a fresh array containing the replacement — the original is untouched. When working with data pipelines or functional state updates, this lets you chain with() alongside map() and filter() without worrying about side effects. Test assertions also benefit, since you can compare the before and after arrays directly.
Using negative indices
const nums = [1, 2, 3, 4, 5];
// Replace last element using negative index
const result = nums.with(-1, 10);
console.log(result);
// [1, 2, 3, 4, 10]
// Replace second-to-last element
console.log(nums.with(-2, 99));
// [1, 2, 3, 99, 5]
Negative indices count backwards from the end of the array, so -1 always targets the last element regardless of array length. This is cleaner than computing arr.length - 1 manually and works the same whether the array has three elements or three thousand.
Updating nested array elements
const matrix = [
[1, 2],
[3, 4]
];
// Replace entire row at index 0
const newMatrix = matrix.with(0, [10, 20]);
console.log(newMatrix);
// [[10, 20], [3, 4]]
console.log(matrix);
// [[1, 2], [3, 4]] - unchanged
with() works on any element type — primitives, objects, or even nested arrays. The new array is a shallow copy, so the replaced row is a new reference while the other rows still point to the original sub-arrays. Keep that in mind when working with deeply nested data: only the top-level array is fresh.
Common Patterns
Immutable array updates in Redux-style state
function updateItem(arr, index, newValue) {
return arr.with(index, newValue);
}
const state = [1, 2, 3, 4, 5];
const newState = updateItem(state, 2, 42);
// [1, 2, 42, 4, 5]
Wrapping with() in a helper like updateItem makes immutable updates read naturally inside reducers and state-management logic. Because with() already returns a new array, there is no need to spread into a copy first — the method handles that for you.
Chain with other array methods
const data = ['a', 'b', 'c', 'd'];
// Chain with filter, map, etc.
const result = data
.filter((_, i) => i !== 1)
.with(0, 'z');
console.log(result);
// ['z', 'c', 'd']
When to Use
- Functional programming: When you need to avoid mutating state
- React/Redux: For immutable state updates
- Debugging: When you want to preserve the original array for comparison
When not to use
- Performance-critical code: Creating new arrays has overhead; use mutating methods (
arr[i] = value) when performance matters - Simple scripts: When you don’t care about immutability
with() vs bracket assignment
The traditional way to update an array element is bracket notation, which mutates the array in place. This is simple and fast, but any code holding a reference to the same array sees the change — a source of hard-to-track bugs in shared state.
const arr = [1, 2, 3];
arr[1] = 99; // mutates arr
console.log(arr); // [1, 99, 3]
with() sidesteps this entire class of problem by returning a new array. The original stays exactly as it was, so any code that reads it later gets the same values. This makes with() a natural fit for React state updates, Redux reducers, and any workflow where data flows in one direction.
const original = [1, 2, 3];
const updated = original.with(1, 99);
console.log(original); // [1, 2, 3] — unchanged
console.log(updated); // [1, 99, 3]
The trade-off is memory: with() allocates a new array on every call. For a tight loop updating thousands of elements, prefer direct assignment.
Part of the ES2023 copying methods
with() was introduced alongside three other non-mutating array methods in ES2023: toSorted(), toReversed(), and toSpliced(). These methods mirror their mutating counterparts (sort(), reverse(), splice()) but return new arrays instead of modifying the original. Together they provide a complete set of functional array operations.
const arr = [3, 1, 4, 1, 5];
arr.toSorted(); // [1, 1, 3, 4, 5] — new array
arr.toReversed(); // [5, 1, 4, 1, 3] — new array
arr.toSpliced(2, 1, 99); // [3, 1, 99, 1, 5] — new array
arr.with(0, 9); // [9, 1, 4, 1, 5] — new array
console.log(arr); // [3, 1, 4, 1, 5] — original unchanged
All four methods are available in Chrome/Edge 110+, Firefox 115+, Safari 16+, and Node.js 20+.
Why with() Is Helpful
with() is a clear fit when you need a one-element update without mutating the original array. It removes the boilerplate of copying the array, editing the copy, and then returning it. That makes immutable state updates easier to read and safer to share. When the change is local and the old array no longer matters, direct assignment can still be simpler, but with() keeps the “copy then replace” pattern obvious.
See Also
Array.prototype.toSorted()— Return a sorted copy of an arrayArray.prototype.toSpliced()— Return a copy with elements removed/inserted