Back to Blog
Lookup Functions
Excel
XLOOKUP
VLOOKUP
Formulas

XLOOKUP vs VLOOKUP: When to Use Each (and How to Switch)

02/08/2026
XLOOKUP vs VLOOKUP: When to Use Each (and How to Switch)

Quick Summary

Key points from this article

  • πŸ”„ The one-line rewrite that turns any VLOOKUP into an XLOOKUP
  • ⬅️ Looking left, looking up, and retiring HLOOKUP for good
  • πŸ›‘οΈ Built-in if_not_found instead of wrapping everything in IFERROR
  • 🎯 Match modes and search modes: approximate matching without the silent errors
  • πŸ“Š Multi-column returns, two-way lookups, and finding the latest record
  • βœ… When VLOOKUP (or INDEX/MATCH) is still the right call
Reading time: ~11 min

VLOOKUP is the formula everybody learns first and the formula everybody eventually fights with. XLOOKUP was built to end those fights. This guide shows you exactly where VLOOKUP breaks, how XLOOKUP fixes it, and how to migrate your existing sheets without breaking anything β€” with real business scenarios and copy-paste examples.

Tip: Follow along with your own data. Every example below works on any table with a key column and some values.


1) The Real Difference (Quick Context)

Both functions answer the same question: "Find this value in a table and give me something from the matching row." They just get there very differently.

VLOOKUP asks for a table and a column number:

=VLOOKUP(lookup_value, table_array, col_index_num, [range_lookup])

XLOOKUP asks for two ranges β€” where to look, and what to return:

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

That one design change fixes four long-standing problems:

ProblemVLOOKUPXLOOKUP
Return a column to the left of the keyImpossibleWorks normally
Someone inserts a columnFormula silently returns the wrong columnUnaffected
Value not found#N/A, needs IFERRORBuilt-in if_not_found argument
Default match typeApproximate (dangerous)Exact

The availability catch: XLOOKUP needs Microsoft 365 or Excel 2021+. In Excel 2019, 2016, or older, it simply doesn't exist β€” see section 9.

Product Catalogue for Lookup Practice

Note where the key columns sit: Product is in column A, but SKU is in column E. That layout is exactly where VLOOKUP runs out of road and XLOOKUP keeps going.

ABCDE
1
Product
Category
Unit Price
Stock
SKU
2
Laptop
Computers
1200
45
SKU-1001
3
Tablet
Computers
450
120
SKU-1002
4
Phone
Mobile
800
78
SKU-1003
5
Monitor
Displays
320
60
SKU-1004
6
Keyboard
Accessories
75
210
SKU-1005
7
Mouse
Accessories
35
340
SKU-1006

fxCells with formulas are highlighted in green

Hover over formula cells to see the formula and highlight referenced cells


2) Your First XLOOKUP: Replace a VLOOKUP in 30 Seconds

🎯 Scenario: You have a product catalogue and need the unit price for "Monitor".

Data Setup:

  • Column A: Product
  • Column B: Category
  • Column C: Unit Price
  • Column D: Stock
  • Column E: SKU

The VLOOKUP way:

=VLOOKUP("Monitor", A2:C7, 3, FALSE)

The XLOOKUP way:

=XLOOKUP("Monitor", A2:A7, C2:C7)

Both return 320. Notice what disappeared:

  • No column counting. You point at the price column directly instead of counting to 3.
  • No FALSE. XLOOKUP defaults to exact match, so you can't forget it.
  • No fragile table. A2:C7 had to cover both the key and the answer; XLOOKUP's two ranges are independent.

The migration recipe β€” any =VLOOKUP(key, A:D, 3, FALSE) becomes =XLOOKUP(key, A:A, C:C). The lookup array is the first column of the old table; the return array is the column you were counting to.

Pitfall: lookup_array and return_array must be the same height. =XLOOKUP("Monitor", A2:A7, C2:C6) returns #VALUE! because one range has 6 rows and the other has 5.

Mini exercise: Rewrite =VLOOKUP("Phone", A2:D7, 4, FALSE) as an XLOOKUP.


3) Look Left, Look Up, Look Anywhere

🎯 Scenario: Your warehouse system exports SKUs. You need to know which product each SKU is.

SKU lives in column E. Product lives in column A. VLOOKUP can only look right from its key column, so this is impossible without restructuring your sheet or falling back to INDEX/MATCH.

XLOOKUP doesn't care about direction:

=XLOOKUP("SKU-1004", E2:E7, A2:A7)

Result: Monitor

The lookup array and return array are two independent ranges. Left, right, or twenty columns apart β€” same formula.

Horizontal lookups too. VLOOKUP has a separate function for rows (HLOOKUP). XLOOKUP handles both, because a range is a range:

=XLOOKUP("Stock", A1:E1, A2:E2)

That searches the header row and returns the matching value from row 2. One function replaces both VLOOKUP and HLOOKUP.

Pitfall: Text keys must match exactly, including trailing spaces. "SKU-1004 " will not find "SKU-1004". Wrap the lookup value in TRIM() when the key comes from an import.

Mini exercise: Return the Category for SKU-1006 using a single XLOOKUP.


4) Stop Wrapping Everything in IFERROR

🎯 Scenario: Someone searches for a product you don't stock. You want "Not in catalog" instead of #N/A.

The old way β€” two nested functions, and the lookup runs twice:

=IFERROR(VLOOKUP("Webcam", A2:C7, 3, FALSE), "Not in catalog")

The XLOOKUP way β€” a fourth argument:

=XLOOKUP("Webcam", A2:A7, C2:C7, "Not in catalog")

This is not just shorter, it's more correct. IFERROR swallows every error β€” including #REF! from a deleted column and #VALUE! from a broken argument. You end up seeing "Not in catalog" when the real story is a broken formula. XLOOKUP's if_not_found only fires on an actual miss; genuine errors still surface.

Common variations:

=XLOOKUP(A2, Catalog!A:A, Catalog!C:C, 0)
=XLOOKUP(A2, Catalog!A:A, Catalog!C:C, "")

Return 0 when the result feeds arithmetic, "" when it feeds a report.

Pitfall: If you still want #N/A (useful β€” charts skip #N/A points), just leave the argument out.

Mini exercise: Build a lookup that returns "Check SKU" when the SKU is missing.


5) Return Multiple Columns at Once

🎯 Scenario: You want Category, Unit Price and Stock for one product, all in one go.

With VLOOKUP you'd write three formulas with three different column numbers. XLOOKUP returns a whole slice:

=XLOOKUP("Phone", A2:A7, B2:D7)

Result: spills across three cells β€” Mobile, 800, 78.

The return array is 3 columns wide, so the answer is 3 cells wide. This is a dynamic array, so it needs empty cells to spill into β€” otherwise you get #SPILL!.

Reorder columns while you're at it:

=XLOOKUP("Phone", A2:A7, CHOOSE({1,2}, D2:D7, C2:C7))

Returns Stock first, then Unit Price β€” no matter how the source table is arranged.

Pitfall: One spilled formula is faster than three separate lookups, but only if the target cells are genuinely empty. Clear the range first β€” even a stray space triggers #SPILL!.

Mini exercise: Return Product, Category and Stock for SKU-1002 in a single formula.


6) Approximate Match Done Right

🎯 Scenario: Commission tiers. Sales of 0–999 earn 2%, 1000–4999 earn 5%, 5000+ earn 8%. You need the rate for any sales figure.

This is VLOOKUP's one genuine strength β€” and also its most dangerous default.

VLOOKUP with TRUE:

=VLOOKUP(B2, Tiers!A:B, 2, TRUE)

It works, but only if Tiers!A:A is sorted ascending. Sort it wrong and VLOOKUP returns a confidently incorrect number β€” no error, no warning.

XLOOKUP with match mode -1:

=XLOOKUP(B2, Tiers!A:A, Tiers!B:B, , -1)

-1 means "exact match, or the next smaller item". Use 1 for "exact match, or the next larger item" β€” handy for shipping weight brackets where you round up.

The four match modes:

ModeMeaning
0Exact match (default)
-1Exact, or next smaller
1Exact, or next larger
2Wildcard (* and ?)

Mode 2 is genuinely useful for messy data:

=XLOOKUP("Lap*", A2:A7, C2:C7, "No match", 2)

Finds Laptop and returns 1200.

Pitfall: XLOOKUP's approximate modes still expect sorted data to behave predictably, but unlike VLOOKUP they don't silently assume it β€” and the explicit -1 makes the intent visible to whoever reads the sheet next.

Mini exercise: Build a shipping-cost lookup that rounds parcel weight up to the next bracket.


7) Two-Way Lookup: XLOOKUP Inside XLOOKUP

🎯 Scenario: You want a cell where you type a product name and a column name and get the intersection.

The inner XLOOKUP picks the column; the outer one picks the row.

=XLOOKUP("Phone", A2:A7, XLOOKUP("Stock", A1:E1, A2:E7))

Result: 78

How it works:

  1. XLOOKUP("Stock", A1:E1, A2:E7) searches the header row and returns the entire matching column β€” D2:D7.
  2. The outer XLOOKUP searches A2:A7 for "Phone" and returns the matching row from that column.

Point the two lookup values at input cells and you have a mini report:

=XLOOKUP(H1, A2:A7, XLOOKUP(H2, A1:E1, A2:E7), "Not found")

The old equivalent was INDEX(A2:E7, MATCH(...), MATCH(...)) β€” same result, considerably harder to read six months later.

Pitfall: The inner return_array must span the full table width (A2:E7), not just the data columns. If it's narrower than the header range you'll get #VALUE!.

Mini exercise: Build a two-cell input that returns any field for any product.


8) Search From the Bottom: Find the Latest Record

🎯 Scenario: A transaction log where each customer appears many times. You want their most recent order, not their first.

VLOOKUP always returns the first match. Full stop. The workarounds involve reversing your data or writing an array formula.

XLOOKUP has a sixth argument:

=XLOOKUP("Acme Corp", Log!A:A, Log!D:D, "No orders", 0, -1)

search_mode = -1 searches last to first, so on a chronologically sorted log you get the latest entry.

The four search modes:

ModeMeaning
1First to last (default)
-1Last to first
2Binary search, ascending
-2Binary search, descending

Modes 2 and -2 are speed optimisations for large sorted lists. They're fast β€” and wrong without warning if the data isn't actually sorted. Only reach for them when a lookup over tens of thousands of rows is visibly slowing your workbook.

Pitfall: "Last row" means last physically, not latest by date. If your log isn't in date order, sort it first or look up MAX() of the date column instead.

Mini exercise: Return the most recent unit price for a product from a price-history sheet.


9) When VLOOKUP Is Still the Right Call

XLOOKUP wins on almost every technical point, but "almost every" isn't "every":

  • Excel 2019 and older. XLOOKUP doesn't exist there. A file that uses it opens with _xlfn.XLOOKUP and #NAME? in every cell.
  • Files shared with people on older versions. Your Microsoft 365 doesn't help the client still running Excel 2016.
  • Google Sheets compatibility. Sheets added XLOOKUP later than Excel did; older shared sheets and some third-party exports still choke on it.
  • Existing sheets that already work. A correct VLOOKUP is not a bug. Migrate when you're touching the formula anyway, not as a weekend project.

The universally compatible fallback is INDEX/MATCH, which works in every version ever shipped and also looks left:

=INDEX(A2:A7, MATCH("SKU-1004", E2:E7, 0))

Read it inside-out: MATCH finds which row the SKU is in, INDEX pulls that row from the Product column.

Pitfall: Don't mix all three styles in one workbook. Pick XLOOKUP if everyone's on 365, INDEX/MATCH if they're not, and be consistent β€” the next person to open the file will thank you.

Mini exercise: Rewrite the section 3 SKU lookup using INDEX/MATCH.


10) Migration Cheat Sheet

Old formulaNew formula
=VLOOKUP(K, A:D, 3, FALSE)=XLOOKUP(K, A:A, C:C)
=IFERROR(VLOOKUP(K, A:D, 3, FALSE), "")=XLOOKUP(K, A:A, C:C, "")
=VLOOKUP(K, A:B, 2, TRUE)=XLOOKUP(K, A:A, B:B, , -1)
=HLOOKUP(K, 1:5, 3, FALSE)=XLOOKUP(K, 1:1, 3:3)
=INDEX(C:C, MATCH(K, A:A, 0))=XLOOKUP(K, A:A, C:C)

Migrating a real workbook, safely:

  1. Find them all: Ctrl+F β†’ search "VLOOKUP" β†’ Look in: Formulas β†’ Find All.
  2. Convert one, verify one. Put the XLOOKUP in a scratch column next to the original and compare the two columns before you delete anything.
  3. Check the TRUEs carefully. Every VLOOKUP(..., TRUE) becomes match_mode -1, and every VLOOKUP(..., FALSE) becomes the default. Getting this backwards produces plausible-looking wrong numbers.
  4. Swap ranges for structured references. Table1[Product] beats A2:A7 β€” it grows with the table.
  5. Confirm the audience. If anyone opening the file is on Excel 2019 or older, stop and use INDEX/MATCH instead.

Quick Checklist (Before Sharing Your Sheet)

  • lookup_array and return_array are the same height
  • Every lookup has an if_not_found value, or #N/A is deliberate
  • Approximate matches use an explicit -1 or 1, never a bare default
  • Text keys are trimmed β€” imports carry invisible spaces
  • Lookup keys and table keys are the same data type (numbers, not text-that-looks-like-numbers)
  • Spill ranges have empty cells below and to the right
  • Everyone opening the file is on Excel 2021 or Microsoft 365

Common Pitfalls Summary

  1. Mismatched range heights: A2:A7 with C2:C6 gives #VALUE!. Select both ranges the same way.
  2. Text vs numbers: "1001" never matches 1001. Use VALUE() or Text to Columns to normalise the key.
  3. Trailing spaces: The single most common cause of a "correct" lookup returning #N/A. TRIM() the key.
  4. #SPILL! on multi-column returns: Clear the cells the result needs to occupy.
  5. Silent wrong answers from approximate match: Unsorted data plus -1, 2 or -2 returns a number that looks fine and isn't.
  6. #NAME? everywhere: The file was opened in Excel 2019 or older. XLOOKUP isn't there.
  7. IFERROR hiding real breakage: Prefer if_not_found, which only catches actual misses.

Conclusion

VLOOKUP taught a generation of people to look things up in Excel, and it earned its place. But it asks you to count columns, forbids looking left, defaults to a dangerous match type, and returns the first hit whether or not that's the one you meant.

XLOOKUP fixes all four with a signature you can read out loud: look for this, in here, and give me that. If you're on Microsoft 365 or Excel 2021, make it your default and use the migration table above when you touch old formulas. If your files travel to older versions, use INDEX/MATCH and skip both.

The one habit worth keeping from either function: always know what happens when the value isn't found. That's where lookups actually go wrong.

If you want hands-on practice with lookup functions, try the exercises in the app β€” each scenario drills these patterns against real business data.

Share this article:
Back to Blog