unshift()

ES3+

Adds one or more elements to the beginning of an array and returns the new length.

Syntax

array.unshift(element1, element2, ...)

Parameters

elements any

Elements to add to the front of the array

Return Value

number

The new length of the array

Examples

JavaScript
const fruits = ['banana', 'cherry'];
fruits.unshift('apple');
console.log(fruits);
Output:
// ['apple', 'banana', 'cherry']

📌 When to Use

Use unshift() when new items belong at the front of a mutable list: prepending the freshest notification to a newest-first feed, pushing recently-used entries to the head of an MRU cache, adding a header row before generated table data, or restoring an item to the front of a queue after a failed processing attempt. It mirrors push() at the opposite end and shares its multi-argument form - arr.unshift(a, b) inserts both, in the given order, before the existing elements. Before reaching for it, check three things. Ownership: like all mutators, unshift() on framework state changes data behind the same reference; the immutable spelling [newItem, ...state] is what React and friends expect. Frequency and size: every unshift() moves all existing elements up one index, an O(n) cost that is trivial for UI-sized lists but painful in loops over large arrays - if you are prepending repeatedly, push() everything and reverse() once at the end, or rethink the storage order so appends land at the back naturally (often the display layer can reverse instead). Intent: for a one-off combination of arrays, [...newItems, ...existing] or concat() reads better than mutating either input. unshift() earns its place when the array is genuinely a long-lived mutable structure whose front matters.

⚠️ Common Mistakes

Prepending in bulk with a loop of unshift() calls. Each call shifts the entire array up one slot, so inserting m items at the front of an n-element array costs O(m*n) - and it ALSO reverses the inserted items' order relative to each other, a second bug hiding inside the slow one. Build the prefix separately and combine once: arr = [...prefix, ...arr].

Using the return value as the array. Like push(), unshift() returns the new LENGTH, so const list = arr.unshift(x) stores a number and the subsequent list.map(...) throws "list.map is not a function" - an error message that names the symptom, not the cause. Mutate on one line, use the array on the next.

Unshifting into framework state. notifications.unshift(newest) mutates the existing reference, so the component tree sees "unchanged" and the new notification renders only after something else forces an update. Spell it immutably - setNotifications(prev => [newest, ...prev]) - and change detection works every time.

Expecting multi-argument unshift() to behave like repeated single unshifts. arr.unshift(a, b) yields [a, b, ...rest], but calling unshift(a) then unshift(b) yields [b, a, ...rest] - each later call pushes earlier insertions deeper. Batch the arguments when the intended order is "as written".

Prepending inside the same loop that iterates the array. New front elements shift every index up, so the loop revisits or skips elements depending on its direction, and in the worst case never terminates because the array keeps growing ahead of the cursor. Stage prepends in a separate array and merge after the loop finishes.

✅ Best Practices

Reach for [newItem, ...arr] as the default prepend in application code: it returns a fresh array (state-safe), it composes inside expressions, and its cost is the same O(n) copy that unshift() pays anyway - so with a single prepend you sacrifice nothing for the immutability.

If profiling shows unshift() hot, invert the storage order: keep the array oldest-to-newest, append with O(1) push(), and let the display layer read it backwards (toReversed(), a reversed loop, or CSS column-reverse). Same UX, linear-to-constant cost change, zero exotic data structures.

Prepend a whole batch in one call with arr.unshift(...items): the batch lands in its original order, the array shifts only once instead of once per item, and the intent ("put these before everything") is stated in a single line.

Cap newest-first lists at insertion time: unshift the new entry, then if (list.length > MAX) list.pop() drops the oldest from the tail. Both touched ends are the cheap ones conceptually, and the list can never grow unbounded between renders.

Use unshift() to reinstate work at the front of a retry queue - queue.unshift(failedJob) makes the failed item next in line rather than last - a small pattern that keeps FIFO fairness for everything else while prioritizing recovery.

⚡ Performance Notes

unshift() is O(n): every existing element moves to a higher index before the new ones drop into place, and if the backing store lacks spare capacity V8 also reallocates it. One unshift() on a thousand-element array is still sub-microsecond territory - the trouble is repetition. Building a 10,000-element array entirely with unshift() performs about fifty million element moves versus ten thousand for push(), a difference of several orders of magnitude that turns "instant" into "noticeable jank" right at UI-relevant sizes. The efficient substitutes are all reorderings of the same work: push() then one reverse() (two linear passes total), storing data tail-appended and reading it backwards, or a deque-style structure when both ends must be O(1) under heavy load. Note that the immutable [x, ...arr] spread is not a performance fix - it is the same O(n) copy per prepend - it just trades mutation for allocation, which is the correct trade at state boundaries and the wrong one inside tight loops.

🌍 Real World Example

Adding New Notifications to the Top

Notification centers show the newest item on top, and when the underlying array is a plain mutable list, unshift() maps one-to-one onto that requirement: each arrival enters at index 0 and everything older slides down. The example pairs it with the standard cap-and-trim - after inserting, pop() removes the oldest entry once the list exceeds 50 - so memory stays bounded no matter how chatty the app gets. Note the two ends working together: newest enters at the front, oldest leaves from the back. In a React or Svelte component the same logic would be written as [newNotification, ...prev].slice(0, 50) to produce a fresh capped array per update.

const notifications = [
  {id: 1, message: 'Welcome!', time: '10:00'},
  {id: 2, message: 'You have mail', time: '10:05'}
];

function addNotification(message) {
  const newNotification = {
    id: Date.now(),
    message,
    time: new Date().toLocaleTimeString()
  };

  // Add to beginning (newest first)
  notifications.unshift(newNotification);

  // Keep only last 50 notifications
  if (notifications.length > 50) {
    notifications.pop();
  }
}

Related Methods