---
id: array-methods
title: Array Methods Plugin
---
## Overview
The Array Methods Plugin (`enableArrayMethods()`) optimizes array operations within Immer producers by avoiding unnecessary Proxy creation during iteration. This provides significant performance improvements for array-heavy operations.
**Why does this matter?** Without the plugin, every array element access during iteration (e.g., in `filter`, `find`, `slice`) creates a Proxy object. For a 1000-element array, this means 1000+ proxy trap invocations just to iterate. With the plugin enabled, callbacks receive base (non-proxied) values, and proxies are only created as needed for mutation tracking.
## Installation
Enable the plugin once at your application's entry point:
```javascript
import {enableArrayMethods} from "immer"
enableArrayMethods()
```
This adds approximately **2KB** to your bundle size.
## Mutating Methods
These methods modify the array in-place and operate directly on the draft's internal copy without creating per-element proxies:
| Method | Returns | Description |
| ----------- | ---------------- | ------------------------------------- |
| `push()` | New length | Adds elements to the end |
| `pop()` | Removed element | Removes and returns the last element |
| `shift()` | Removed element | Removes and returns the first element |
| `unshift()` | New length | Adds elements to the beginning |
| `splice()` | Removed elements | Adds/removes elements at any position |
| `sort()` | The draft array | Sorts elements in place |
| `reverse()` | The draft array | Reverses the array in place |
```javascript
import {produce, enableArrayMethods} from "immer"
enableArrayMethods()
const base = {items: [3, 1, 4, 1, 5]}
const result = produce(base, draft => {
draft.items.push(9) // Adds 9 to end
draft.items.sort() // Sorts: [1, 1, 3, 4, 5, 9]
draft.items.reverse() // Reverses: [9, 5, 4, 3, 1, 1]
})
```
## Non-Mutating Methods
Non-mutating methods are categorized based on what they return:
### Subset Operations (Return Drafts)
These methods select items that exist in the original array and **create draft proxies** for the returned items. The callbacks receive **base values** (the optimization), but the **returned array** contains newly created draft proxies that point back to the original positions. **Mutations to returned items WILL affect the draft state.**
| Method | Returns | Drafts? |
| ------------ | ---------------------------------- | ------- |
| `filter()` | Array of matching items | ✅ Yes |
| `slice()` | Array of items in range | ✅ Yes |
| `find()` | First matching item or `undefined` | ✅ Yes |
| `findLast()` | Last matching item or `undefined` | ✅ Yes |
```javascript
const base = {
items: [
{id: 1, value: 10},
{id: 2, value: 20},
{id: 3, value: 30}
]
}
const result = produce(base, draft => {
// filter returns drafts - mutations track back to original
const filtered = draft.items.filter(item => item.value > 15)
filtered[0].value = 999 // This WILL affect draft.items[1]
// find returns a draft - mutations track back
const found = draft.items.find(item => item.id === 3)
if (found) {
found.value = 888 // This WILL affect draft.items[2]
}
// slice returns drafts
const sliced = draft.items.slice(0, 2)
sliced[0].value = 777 // This WILL affect draft.items[0]
})
console.log(result.items[0].value) // 777
console.log(result.items[1].value) // 999
console.log(result.items[2].value) // 888
```
### Transform Operations (Return Base Values)
These methods create **new arrays** that may include external items or restructured data. They return **base values**, NOT drafts. **Mutations to returned items will NOT track back to the draft state.**
| Method | Returns | Drafts? |
| ---------- | ------------------- | ------- |
| `concat()` | New combined array | ❌ No |
| `flat()` | New flattened array | ❌ No |
```javascript
const base = {items: [{id: 1, value: 10}]}
const result = produce(base, draft => {
// concat returns base values - mutations DON'T track
const concatenated = draft.items.concat([{id: 2, value: 20}])
concatenated[0].value = 999 // This will NOT affect draft.items[0]
// To actually use concat results, assign them:
draft.items = draft.items.concat([{id: 2, value: 20}])
})
// Original unchanged because concat result wasn't assigned
console.log(result.items[0].value) // 10 (unchanged)
```
**Why the distinction?**
- **Subset operations** (`filter`, `slice`, `find`) select items that exist in the original array. Returning drafts allows mutations to propagate back to the source.
- **Transform operations** (`concat`, `flat`) create new data structures that may include external items or restructured data, making draft tracking impractical.
### Primitive-Returning Methods
These methods return primitive values (numbers, booleans, strings). No tracking issues since primitives aren't draftable:
| Method | Returns |
| ------------------ | -------------------- |
| `indexOf()` | Number (index or -1) |
| `lastIndexOf()` | Number (index or -1) |
| `includes()` | Boolean |
| `some()` | Boolean |
| `every()` | Boolean |
| `findIndex()` | Number (index or -1) |
| `findLastIndex()` | Number (index or -1) |
| `join()` | String |
| `toString()` | String |
| `toLocaleString()` | String |
```javascript
const base = {
items: [
{id: 1, active: true},
{id: 2, active: false}
]
}
const result = produce(base, draft => {
const index = draft.items.findIndex(item => item.id === 2)
const hasActive = draft.items.some(item => item.active)
const allActive = draft.items.every(item => item.active)
console.log(index) // 1
console.log(hasActive) // true
console.log(allActive) // false
})
```
## Methods NOT Overridden
The following methods are **not** intercepted by the plugin and work through standard Proxy behavior. Callbacks receive drafts, and mutations track normally:
| Method | Description |
| --------------- | --------------------------------- |
| `map()` | Transform each element |
| `flatMap()` | Map then flatten |
| `forEach()` | Execute callback for each element |
| `reduce()` | Reduce to single value |
| `reduceRight()` | Reduce from right to left |
```javascript
const base = {
items: [
{id: 1, value: 10, nested: {count: 0}},
{id: 2, value: 20, nested: {count: 0}}
]
}
const result = produce(base, draft => {
// forEach receives drafts - mutations work normally
draft.items.forEach(item => {
item.value *= 2
})
// map is NOT overridden - callbacks receive drafts
// The returned array items are also drafts (extracted from draft.items)
const mapped = draft.items.map(item => item.nested)
// Mutations to the result array propagate back
mapped[0].count = 999 // ✅ This affects draft.items[0].nested.count
})
console.log(result.items[0].nested.count) // 999
```
## Callback Behavior
For overridden methods, callbacks receive **base values** (not drafts). This is the core optimization - it avoids creating proxies for every element during iteration.
```javascript
const base = {
items: [
{id: 1, value: 10},
{id: 2, value: 20}
]
}
produce(base, draft => {
draft.items.filter(item => {
// `item` is a base value here, NOT a draft
// Reading properties works fine
return item.value > 15
// But direct mutation here won't be tracked:
// item.value = 999 // ❌ Won't affect draft
})
// Instead, use the returned draft:
const filtered = draft.items.filter(item => item.value > 15)
filtered[0].value = 999 // ✅ This works because filtered[0] is a draft
})
```
## Method Return Behavior Summary
| Category | Methods | Returns | Mutations Track? |
| --- | --- | --- | --- |
| **Subset** | `filter`, `slice`, `find`, `findLast` | Draft proxies | ✅ Yes |
| **Transform** | `concat`, `flat` | Base values | ❌ No |
| **Primitive** | `indexOf`, `includes`, `some`, `every`, `findIndex`, `findLastIndex`, `lastIndexOf`, `join`, `toString`, `toLocaleString` | Primitives | N/A |
| **Mutating** | `push`, `pop`, `shift`, `unshift`, `splice`, `sort`, `reverse` | Various | ✅ Yes (modifies draft) |
| **Not Overridden** | `map`, `flatMap`, `forEach`, `reduce`, `reduceRight` | Standard behavior | ✅ Yes (callbacks get drafts) |
## When to Use
Enable the Array Methods Plugin when:
- Your application has significant array iteration within producers
- You frequently use methods like `filter`, `find`, `some`, `every` on large arrays
- Performance profiling shows array operations as a bottleneck
The plugin is most beneficial for:
- Large arrays (100+ elements)
- Frequent producer calls with array operations
- Read-heavy operations (filtering, searching) where most elements aren't modified
## Performance Benefit
**Without the plugin:**
- Every array element access during iteration creates a Proxy
- A `filter()` on 1000 elements = 1000+ proxy creations
**With the plugin:**
- Callbacks receive base values directly
- Proxies only created for the specific elements you actually mutate, or that match filtering predicates
```javascript
// Without plugin: ~3000+ proxy trap invocations
// With plugin: ~10-20 proxy trap invocations
const result = produce(largeState, draft => {
const filtered = draft.items.filter(x => x.value > threshold)
// Only items you mutate get proxied
filtered.forEach(item => {
item.processed = true
})
})
```