XLOOKUP

Search a range or array and return an item corresponding to the first match found.

Lookup & ReferenceIntermediate
Purpose
Searches a column or row for a value and returns the matching value from a separate column or row, in any direction, with optional fallback.
Returns
A matched value from the return_array (or your if_not_found fallback)
Syntax
=XLOOKUP(lookup_value, lookup_array, return_array, [if_not_found], [match_mode], [search_mode])
Excel version
Excel 2021 / Microsoft 365

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.

= XLOOKUP(lookup_value, lookup_array, return_array, [if_not_found], [match_mode], [search_mode])

Arguments

ArgumentTypeRequiredDescription
lookup_valueany✓ RequiredThe value you want to find. Usually a cell reference like A2. Can be text, a number, or a cell reference.
lookup_arrayrange✓ RequiredThe 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_arrayrange✓ RequiredThe 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_foundany✗ OptionalValue 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_modenumber✗ Optional0 = exact match (default). -1 = exact match or next smaller. 1 = exact match or next larger. 2 = wildcard match (use ? and *).
search_modenumber✗ Optional1 = 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.
H2
fx
=XLOOKUP(G2,$A$2:$A$9,$D$2:$D$9)
ABCDEFGHI
1Product CodeProduct NameCategoryPriceOrderProduct CodePrice
2P-1001Wireless MousePeripherals$29.99PO-2024-001P-1042$349.99
3P-1015Mechanical KeyboardPeripherals$89.99PO-2024-002P-1156$129.99↓ drag down
4P-104227" MonitorDisplays$349.99PO-2024-003P-1001$29.99
5P-1088USB-C HubAccessories$44.99
6P-1103Laptop StandAccessories$67.99
7P-1156Webcam HDPeripherals$129.99
8P-1201Desk Lamp LEDLighting$39.99
9P-1247Cable ManagerAccessories$14.99
Formula entered in cell H2. Searches the red Code column for G2's value (P-1042) and pulls back the matching value from the green Price column. No col_index_num to count.
Row 4 highlighted in yellow. That's where XLOOKUP found P-1042. It returned $349.99 from the Price column.

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
F2
fx
=XLOOKUP(E2,$A$2:$A$8,$B$2:$B$8,"Not found")
ABCDEFG
1CodePriceOrderCodePrice
2P-1001$29.99PO-001P-1042$349.99
3P-1042$349.99PO-002P-9999Not found↓ drag down
4P-1156$129.99PO-003P-1001$29.99
5P-2024$44.99PO-004P-0000Not found
6P-1088$19.99
7P-1450$79.99
8P-2107$14.99
9
Formula entered in cell F2. The fourth argument 'Not found' replaces the IFERROR wrapper VLOOKUP would need.
Catalog on the left, orders on the right. PO-002 and PO-004 use codes that don't exist in the catalog. The if_not_found argument turns those #N/A errors into clean 'Not found' text. No IFERROR needed.
The if_not_found argument is XLOOKUP's killer feature. Always pass it, even just 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
Sheet1: Employee Roster (your working sheet)
G2
fx
=XLOOKUP(A2,Sheet2!A:A,Sheet2!B:B)
ABCDEFGH
1Employee IDNameDepartmentTitleHire DateManagerSalary
2E-1001Sarah ChenFinanceSenior Analyst2019-03-15Mark Lee$72,000
3E-1042James MillerHRHR Specialist2020-06-22Anna Park$61,000↓ drag down
4E-1088Maria SantosMarketingMarketing Lead2018-11-04Tom Davis$58,000
5E-1103David BrownFinanceJunior Analyst2021-08-12Mark Lee$68,000
6E-1156Emma DavisITSystems Engineer2017-02-28Tom Davis$91,000
7E-1201Alex KimSalesAccount Manager2022-01-10Lisa Wong$64,000
8E-1245Rachel WoodEngineeringSoftware Engineer2020-09-15Mike Chen$85,000
9E-1287Carlos GomezOperationsOps Manager2019-12-01Anna Park$76,000
10E-1310Jenny LiuMarketingContent Writer2023-04-18Tom Davis$52,000
11E-1356Mark ReillyEngineeringSenior Engineer2018-07-25Mike Chen$98,000
12
13
14
Formula entered in cell G2. Searches Sheet2's red ID column for A2 (E-1001) and pulls Sarah Chen's $72,000 from the green Salary column.
Sheet1
Sheet2
Sheet2: Compensation table (referenced by Sheet1)
A1
fx
ABCDE
1Employee IDSalaryBonusStockTotal Comp
2E-1001$72,000$5,000$8,000$85,000
3E-1042$61,000$3,500$5,000$69,500
4E-1088$58,000$3,000$4,500$65,500
5E-1103$68,000$4,500$7,000$79,500
6E-1156$91,000$7,000$12,000$110,000
7E-1201$64,000$4,000$6,000$74,000
8E-1245$85,000$6,500$10,000$101,500
9E-1287$76,000$5,500$8,500$90,000
10E-1310$52,000$2,500$3,000$57,500
11E-1356$98,000$8,000$13,500$119,500
Sheet1
Sheet2

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
C4
fx
=XLOOKUP(B4,$E$2:$E$5,$F$2:$F$5,,-1)
ABCDEFG
1Sales RepMonthly SalesCommissionSales FromRate
2Rachel Green$8,5005%$05%
3Tom Bradley$24,3008%$10,0008%
4Maria Santos$67,80012%$50,00012%
5Chris Evans$112,40015%$100,00015%
6
7
8
9
Formula entered in cell C4. Maria's $67,800 doesn't match any tier exactly. match_mode -1 picks the next-smaller threshold ($50,000) and returns its 12% rate.
Maria Santos sold $67,800. That falls between the $50,000 and $100,000 tiers. match_mode -1 picks the largest threshold ≤ $67,800 (the $50,000 row) and returns its 12% rate.
Use 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
G2
fx
=XLOOKUP(F2,$B$2:$B$9,$A$2:$A$9)
ABCDEFGH
1CodeNameCategoryPriceSearchCode
2P-1001Wireless MousePeripherals$29.9927" MonitorP-1042
3P-104227" MonitorDisplays$349.99USB-C HubP-1088↓ drag down
4P-1088USB-C HubAccessories$44.99Webcam HDP-1156
5P-1103Laptop StandAccessories$67.99
6P-1156Webcam HDPeripherals$129.99
7P-1201Desk Lamp LEDLighting$39.99
8P-1247Cable ManagerAccessories$14.99
9P-2024Cable Manager XLAccessories$24.99
Formula entered in cell G2. Searches the red Name column for "27" Monitor", then returns the matching Code from the green column on the LEFT. Impossible with VLOOKUP.
Row 3 highlighted in yellow. That's where XLOOKUP found the match. The return_array points LEFT at the Code column, which VLOOKUP would refuse to do.
Anytime your data isn't structured perfectly with the lookup column on the far left, use XLOOKUP. No more restructuring spreadsheets just to satisfy VLOOKUP.

Where people go wrong

  1. 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).
  2. 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.
  3. 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.
  4. 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 2 to 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 -1 to 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 →
Free account · No credit card · Cancel anytime
Practice XLOOKUP
XLOOKUP in Excel: The Modern VLOOKUP Replacement · CellSkill