find()
ES6+Returns the first element in the array that satisfies the provided testing function.
Syntax
array.find(callback(element, index, array), thisArg)Parameters
callback Function Function to test each element
Return Value
The first element that satisfies the condition, or undefined
Examples
const numbers = [5, 12, 8, 130, 44];
const found = numbers.find(x => x > 10);
console.log(found); 📌 When to Use
Use find() when you want the first element that satisfies a condition - most commonly a lookup by id, slug, or some unique key in an array of objects. Its two defining traits guide the choice: it stops scanning at the first match, and it hands you the element itself rather than a copy, an index, or a boolean. That makes it the natural fit for "fetch the record so I can read or update it" situations. Compare the alternatives explicitly: filter() when several elements might match and you want all of them; findIndex() when you need the position, typically to splice or replace; some() when a true/false answer is all you need (returning the element from find() and coercing it to boolean breaks on legitimately falsy elements like 0 or empty strings); includes() or indexOf() when matching a primitive value by equality rather than by predicate; and findLast() (ES2023) when the most recent match in an ordered list matters, such as the latest log entry meeting a threshold. If the same array will be queried by key many times - inside a render loop, for instance - convert it once to a Map and use get() instead of repeating linear scans.
⚠️ Common Mistakes
Forgetting that a miss returns undefined, not null and not an error. The failure only surfaces later, as "cannot read properties of undefined" on the next property access - often several lines or components away from the find() call that actually failed. Check the result or use optional chaining immediately at the lookup site.
Using find() when several elements can legitimately match. find() silently returns only the first hit, so duplicate data produces plausible-looking but incomplete results - a nasty bug class because nothing fails. If "all matches" is even possible in your data model, filter() is the correct tool, and getting exactly one result can then be asserted explicitly.
Writing code that assumes the element exists because "it always does". Data changes: records get deleted, APIs return partial lists, ids arrive stale from an old browser tab. Every find() over external data needs a deliberate miss path - a fallback value, an early return, or a thrown error with a useful message.
Comparing ids with mismatched types. user.id === "42" fails when the id is the number 42, which happens constantly because URL params and form inputs are always strings. find() just returns undefined with no hint that the predicate never matched; normalize with Number() or String() before comparing.
Confusing find() with filter()[0] performance-wise in the other direction: writing filter(...)[0] out of habit. Both return the same element, but filter() scans the entire array and allocates a throwaway array for one item. The habit is harmless at 10 elements and wasteful at 100,000.
✅ Best Practices
Handle the miss at the lookup site with optional chaining and nullish coalescing: users.find(u => u.id === id)?.name ?? "Unknown". This keeps the undefined case visible exactly where it can occur instead of letting it propagate into unrelated code as a mystery crash.
Match the tool to the question: includes() for "is this primitive in the list", some() for "does anything match this condition", find() for "give me the matching object". Using the most specific method documents the intent and returns exactly the type you need - no coercion tricks required.
Destructure with a fallback object when you only need a couple of fields: const { name, email } = users.find(u => u.id === id) ?? {}. The ?? {} guard turns a missing record into undefined fields instead of a TypeError, which is often exactly the right degradation for display code.
For repeated lookups by the same key, build a Map once - new Map(users.map(u => [u.id, u])) - and call map.get(id) thereafter. One O(n) construction replaces an O(n) scan per lookup, which transforms list-heavy rendering code from quadratic back to linear.
Use findLast() and findLastIndex() (ES2023) when the latest match matters in chronologically ordered data - searching backwards through an append-only event log finds recent entries in a couple of steps instead of scanning the whole history from the top.
⚡ Performance Notes
find() short-circuits: on average it inspects half the array when matches are uniformly distributed, and just one element when the match sits at the front, versus filter()[0] which always walks everything and allocates a result array it immediately throws away. The real performance story, though, is repetition. A single find() over 10,000 items costs microseconds and is never worth optimizing; a find() inside a loop over another 10,000-item list is 100 million predicate calls and very much is. That O(n*m) pattern - matching two datasets by id with nested find() - is one of the most common accidental performance cliffs in frontend code, and the fix is always the same: index one side into a Map (O(n) once) and look up the other side with get() (O(1) each). One spec detail worth knowing: unlike map() and filter(), find() does not skip holes in sparse arrays - the callback receives undefined for them - so a predicate like x => x === undefined can "match" a hole.
🌍 Real World Example
Finding a User by ID in a List
Session handling code constantly needs to resolve "the current user id" into a full user record for permissions and display. find() does the lookup by unique id, and the example shows both halves of the pattern: the happy path where the record exists, and the guarded path where an unknown id (a logged-out session, a deleted account) degrades gracefully to a "Guest" fallback via optional chaining and the nullish coalescing operator. Note that ?? is deliberately used instead of || so that falsy-but-valid values would survive; with strings it makes no difference here, but the habit prevents subtle bugs elsewhere.
const users = [
{id: 1, name: 'Alice', role: 'admin'},
{id: 2, name: 'Bob', role: 'user'},
{id: 3, name: 'Charlie', role: 'user'}
];
const currentUserId = 2;
const currentUser = users.find(user => user.id === currentUserId);
// currentUser: {id: 2, name: 'Bob', role: 'user'}
// Safe access with optional chaining
const userName = users.find(u => u.id === 999)?.name ?? 'Guest';