How XLOOKUP works
XLOOKUP is the modern replacement for VLOOKUP. You give it three things: the value you want to find, the column or row to search in, and the column or row to return a result from. Done. No col_index_num, no first-column constraint, no range_lookup gotcha.
Because lookup_array and return_array are separate arguments, XLOOKUP can search a column on the right and return a value from a column on the left, something VLOOKUP physically cannot do. And because if_not_found is built in, you don't need to wrap it in IFERROR to avoid #N/A errors.
If you're on Excel 2021, Microsoft 365, or Excel for the web, XLOOKUP should be your default lookup function. It's cleaner, safer, and more flexible.
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
| lookup_value | any | ✓ Required | The value you want to find. Usually a cell reference like A2. Can be text, a number, or a cell reference. |
| lookup_array | range | ✓ Required | The single column or row to search in. Unlike VLOOKUP's table_array, this is just the column you're searching, not the whole table. |
| return_array | range | ✓ Required | The single column or row to pull the matched value from. Can sit anywhere: to the left, right, above, or below the lookup_array. |
| if_not_found | any | ✗ Optional | Value to return when no match is found. Set this to skip wrapping the formula in IFERROR. A string like "Not found", 0, or "" all work. |
| match_mode | number | ✗ Optional | 0 = exact match (default). -1 = exact match or next smaller. 1 = exact match or next larger. 2 = wildcard match (use ? and *). |
| search_mode | number | ✗ Optional | 1 = first to last (default). -1 = last to first (useful for finding the most recent entry). 2 / -2 = binary search on sorted data. |
XLOOKUP basic: find product price by code
You work in sales at TechDirect. A customer wants the price of product P-1042. Your catalog has 500 rows. One formula finds it instantly, and you don't need to count columns.
=XLOOKUP(G2,$A$2:$A$9,$D$2:$D$9)- •
G2→ the product code you're looking up - •
$A$2:$A$9→ the column to search in (the Code column) - •
$D$2:$D$9→ the column to return from (the Price column) - •
No col_index_num→ you point at the return column directly. No counting.
| A | B | C | D | E | F | G | H | I | |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Product Code | Product Name | Category | Price | Order | Product Code | Price | ||
| 2 | P-1001 | Wireless Mouse | Peripherals | $29.99 | PO-2024-001 | P-1042 | $349.99 | ||
| 3 | P-1015 | Mechanical Keyboard | Peripherals | $89.99 | PO-2024-002 | P-1156 | $129.99 | ↓ drag down | |
| 4 | P-1042 | 27" Monitor | Displays | $349.99 | PO-2024-003 | P-1001 | $29.99 | ||
| 5 | P-1088 | USB-C Hub | Accessories | $44.99 | |||||
| 6 | P-1103 | Laptop Stand | Accessories | $67.99 | |||||
| 7 | P-1156 | Webcam HD | Peripherals | $129.99 | |||||
| 8 | P-1201 | Desk Lamp LED | Lighting | $39.99 | |||||
| 9 | P-1247 | Cable Manager | Accessories | $14.99 |
XLOOKUP with if_not_found: built-in error handling
With VLOOKUP, missing matches return #N/A and you have to wrap the whole formula in IFERROR. XLOOKUP has a fourth argument called if_not_found. Pass a fallback value and you're done. No nesting, no IFERROR, no extra parentheses to balance.
=XLOOKUP(E2,$A$2:$A$8,$B$2:$B$8,"Not found")- •
E2→ the order's product code (lookup_value) - •
$A$2:$A$8→ the Code column to search in (lookup_array) - •
$B$2:$B$8→ the Price column to return from (return_array) - •
"Not found"→ shown instead of #N/A when the code doesn't exist
| A | B | C | D | E | F | G | |
|---|---|---|---|---|---|---|---|
| 1 | Code | Price | Order | Code | Price | ||
| 2 | P-1001 | $29.99 | PO-001 | P-1042 | $349.99 | ||
| 3 | P-1042 | $349.99 | PO-002 | P-9999 | Not found | ↓ drag down | |
| 4 | P-1156 | $129.99 | PO-003 | P-1001 | $29.99 | ||
| 5 | P-2024 | $44.99 | PO-004 | P-0000 | Not found | ||
| 6 | P-1088 | $19.99 | |||||
| 7 | P-1450 | $79.99 | |||||
| 8 | P-2107 | $14.99 | |||||
| 9 |
0 or "", so a missing match never crashes your report.XLOOKUP across sheets: pulling data from another tab
Your employee list is on Sheet1. Salaries live on Sheet2 to keep them restricted. XLOOKUP works across sheets exactly like a same-sheet lookup. Just prefix the lookup_array and return_array with the sheet name.
=XLOOKUP(A2,Sheet2!A:A,Sheet2!B:B)- •
A2→ the employee ID on Sheet1 - •
Sheet2!A:A→ the Employee ID column on Sheet2 (where to look) - •
Sheet2!B:B→ the Salary column on Sheet2 (what to return) - •
No match_mode needed→ exact match is the default for XLOOKUP
| A | B | C | D | E | F | G | H | |
|---|---|---|---|---|---|---|---|---|
| 1 | Employee ID | Name | Department | Title | Hire Date | Manager | Salary | |
| 2 | E-1001 | Sarah Chen | Finance | Senior Analyst | 2019-03-15 | Mark Lee | $72,000 | |
| 3 | E-1042 | James Miller | HR | HR Specialist | 2020-06-22 | Anna Park | $61,000 | ↓ drag down |
| 4 | E-1088 | Maria Santos | Marketing | Marketing Lead | 2018-11-04 | Tom Davis | $58,000 | |
| 5 | E-1103 | David Brown | Finance | Junior Analyst | 2021-08-12 | Mark Lee | $68,000 | |
| 6 | E-1156 | Emma Davis | IT | Systems Engineer | 2017-02-28 | Tom Davis | $91,000 | |
| 7 | E-1201 | Alex Kim | Sales | Account Manager | 2022-01-10 | Lisa Wong | $64,000 | |
| 8 | E-1245 | Rachel Wood | Engineering | Software Engineer | 2020-09-15 | Mike Chen | $85,000 | |
| 9 | E-1287 | Carlos Gomez | Operations | Ops Manager | 2019-12-01 | Anna Park | $76,000 | |
| 10 | E-1310 | Jenny Liu | Marketing | Content Writer | 2023-04-18 | Tom Davis | $52,000 | |
| 11 | E-1356 | Mark Reilly | Engineering | Senior Engineer | 2018-07-25 | Mike Chen | $98,000 | |
| 12 | ||||||||
| 13 | ||||||||
| 14 |
| A | B | C | D | E | |
|---|---|---|---|---|---|
| 1 | Employee ID | Salary | Bonus | Stock | Total Comp |
| 2 | E-1001 | $72,000 | $5,000 | $8,000 | $85,000 |
| 3 | E-1042 | $61,000 | $3,500 | $5,000 | $69,500 |
| 4 | E-1088 | $58,000 | $3,000 | $4,500 | $65,500 |
| 5 | E-1103 | $68,000 | $4,500 | $7,000 | $79,500 |
| 6 | E-1156 | $91,000 | $7,000 | $12,000 | $110,000 |
| 7 | E-1201 | $64,000 | $4,000 | $6,000 | $74,000 |
| 8 | E-1245 | $85,000 | $6,500 | $10,000 | $101,500 |
| 9 | E-1287 | $76,000 | $5,500 | $8,500 | $90,000 |
| 10 | E-1310 | $52,000 | $2,500 | $3,000 | $57,500 |
| 11 | E-1356 | $98,000 | $8,000 | $13,500 | $119,500 |
Sheet1 G2 looks up E-1001 on Sheet2 and pulls back $72,000. The Sheet2! prefix tells XLOOKUP exactly which tab to search. The rest of the formula works identically to a same-sheet lookup.
XLOOKUP with match_mode -1: commission tiers
Commission tiers. Tax brackets. Grade scales. When you need to find which RANGE a value falls into rather than an exact match, use XLOOKUP's fifth argument: match_mode.
Pass -1 and XLOOKUP returns the next-smaller value if no exact match is found, perfect for tiered lookups. And unlike VLOOKUP's TRUE, XLOOKUP doesn't require the tier table to be sorted ascending, though sorting still keeps it readable.
=XLOOKUP(B4,$E$2:$E$5,$F$2:$F$5,,-1)- •
B4→ the rep's monthly sales (lookup_value) - •
$E$2:$E$5→ the tier thresholds (lookup_array) - •
$F$2:$F$5→ the tier rates (return_array) - •
[if_not_found]→ skipped: leave blank with the empty 4th argument - •
-1→ match_mode: exact match, or next smaller if not found
| A | B | C | D | E | F | G | |
|---|---|---|---|---|---|---|---|
| 1 | Sales Rep | Monthly Sales | Commission | Sales From | Rate | ||
| 2 | Rachel Green | $8,500 | 5% | $0 | 5% | ||
| 3 | Tom Bradley | $24,300 | 8% | $10,000 | 8% | ||
| 4 | Maria Santos | $67,800 | 12% | $50,000 | 12% | ||
| 5 | Chris Evans | $112,400 | 15% | $100,000 | 15% | ||
| 6 | |||||||
| 7 | |||||||
| 8 | |||||||
| 9 |
match_mode 1 instead of -1 when you want the next-LARGER value (e.g. shipping cost brackets where you round up). Both work without needing the lookup table to be sorted.XLOOKUP looks left: pull any column from any direction
The single biggest reason to switch from VLOOKUP to XLOOKUP. VLOOKUP can only return values from columns to the right of the lookup column. The lookup must always be in column 1 of your range. XLOOKUP doesn't care; lookup_array and return_array are independent.
Say you have a product NAME and you need its CODE. The Code column sits to the LEFT of Name in your catalog. VLOOKUP physically can't do this. XLOOKUP does it cleanly.
=XLOOKUP(F2,$B$2:$B$9,$A$2:$A$9)- •
F2→ the product name you're searching for - •
$B$2:$B$9→ the Name column (lookup_array) - •
$A$2:$A$9→ the Code column to RETURN: sits LEFT of the Name column - •
No match_mode→ exact match by default: leave it off
| A | B | C | D | E | F | G | H | |
|---|---|---|---|---|---|---|---|---|
| 1 | Code | Name | Category | Price | Search | Code | ||
| 2 | P-1001 | Wireless Mouse | Peripherals | $29.99 | 27" Monitor | P-1042 | ||
| 3 | P-1042 | 27" Monitor | Displays | $349.99 | USB-C Hub | P-1088 | ↓ drag down | |
| 4 | P-1088 | USB-C Hub | Accessories | $44.99 | Webcam HD | P-1156 | ||
| 5 | P-1103 | Laptop Stand | Accessories | $67.99 | ||||
| 6 | P-1156 | Webcam HD | Peripherals | $129.99 | ||||
| 7 | P-1201 | Desk Lamp LED | Lighting | $39.99 | ||||
| 8 | P-1247 | Cable Manager | Accessories | $14.99 | ||||
| 9 | P-2024 | Cable Manager XL | Accessories | $24.99 |
Where people go wrong
- Mismatched lookup_array and return_array sizes
You set lookup_array to A2:A100 but return_array to B2:B50. XLOOKUP returns #VALUE! because the two arrays must have the same number of rows (or columns, for horizontal lookups).
Fix: Always make lookup_array and return_array the same length. The cleanest pattern: use whole columns ($A:$A, $B:$B) or matching row ranges ($A$2:$A$100, $B$2:$B$100). - Skipping if_not_found and getting #N/A
The whole point of switching to XLOOKUP was to avoid IFERROR. But if you forget to pass the if_not_found argument, missing matches still return #N/A and your report still looks broken.
Fix: Pass if_not_found on every XLOOKUP. Even if the value should always exist, set it to "Not found" or 0 so a stray missing match never crashes the worksheet. - Forgetting $ signs when copying down
XLOOKUP doesn't suffer from VLOOKUP's column-shift problem, but you still need $ signs to lock the lookup_array and return_array. Without them, the ranges shift one row down with each drag and the lookup eventually misses entries.
Fix: Lock both arrays with $ signs: =XLOOKUP(F2,$B$2:$B$9,$A$2:$A$9). Press F4 with the cursor inside a reference to add the $ signs automatically. - Using XLOOKUP in a workbook shared with older Excel users
XLOOKUP is only available in Excel 2021, Microsoft 365, and Excel for the web. If a colleague opens your file in Excel 2019 or older, every XLOOKUP becomes #NAME?. Even if you saved the file with values intact, refreshing breaks them.
Fix: Check who else needs to edit the workbook. If anyone is on Excel 2019 or earlier, fall back to VLOOKUP or INDEX MATCH for compatibility.
Notes
- XLOOKUP is case-insensitive. "apple" and "APPLE" return the same result
- Pass
match_mode 2to enable wildcard matching (use?for any single character,*for any sequence) - XLOOKUP can return an entire ROW or COLUMN by passing a range as return_array. Useful with dynamic arrays.
- For horizontal lookups, just pass row ranges as lookup_array and return_array. XLOOKUP works in any direction.
- Use
search_mode -1to search from the bottom up. Handy for finding the most recent matching entry. - Available in Excel 2021, Microsoft 365, and Excel for the web. Older versions return #NAME?. Fall back to VLOOKUP or INDEX MATCH for those workbooks.
- Approximate match modes (1 and -1) work without requiring the lookup table to be sorted, unlike VLOOKUP's TRUE
Now prove it
Reading about XLOOKUP is one thing.
Wiring it into a real spreadsheet, with the right lookup_array, return_array, and if_not_found, is where it actually pays off.
These exercises drop you into real job scenarios where XLOOKUP's flexibility matters.
Here is the thing about XLOOKUP.
Reading about lookup_array and return_array doesn't teach you which one to put first when your manager drops a 10,000-row spreadsheet on your desk and says the report is due in an hour.
That gap, between knowing what XLOOKUP does and reaching for it confidently when it actually matters, is exactly what CellSkill is built to close.
Not with more reading.
With practice on scenarios that look like your actual job.
Start practicing XLOOKUP for free →