VLOOKUP vs XLOOKUP in Excel: When Each Works, When Both Fail, and Why INDEX/MATCH Still Matters
VLOOKUP vs XLOOKUP is not a simple upgrade question — each has specific failure modes, version constraints, and edge cases that determine which is correct for a given workbook. INDEX/MATCH remains relevant in 2026 despite XLOOKUP’s advantages because it handles scenarios neither VLOOKUP nor XLOOKUP manage cleanly. This article maps every failure mode, limitation, and use case for all three functions, with real scenarios from healthcare IT, revenue cycle, and QA analyst workbooks.
- VLOOKUP: Syntax, How It Works, and Its Three Failure Modes
- XLOOKUP: What It Fixes and What It Does Not
- INDEX/MATCH: Why It Still Wins in Specific Situations
- VLOOKUP vs XLOOKUP vs INDEX/MATCH: Full Comparison
- When All Three Fail: Shared Limitations
- Real Analyst Scenarios: Healthcare IT and Finance
- Error Handling: IFERROR vs IFNA for Lookup Functions
- Decision Framework: Choosing the Right Lookup Function
- Downloads
VLOOKUP: Syntax, How It Works, and Its Three Failure Modes
VLOOKUP searches for a value in the first column of a range and returns a value from a specified column to the right. Its four arguments are lookup value, table array, column index number, and range lookup (exact or approximate match).
=VLOOKUP(A2, CodeTable, 2, FALSE)
The fourth argument, range_lookup, is one of the most misunderstood parameters in Excel. FALSE means exact match — the lookup value must appear exactly in the first column. TRUE or omitting the argument means approximate match — VLOOKUP finds the largest value less than or equal to the lookup value, but the first column must be sorted ascending for this to work correctly. Most analyst lookups should use FALSE. Omitting the argument defaults to TRUE, which produces wrong results on unsorted data without any error.
VLOOKUP Failure Mode 1: It Cannot Look Left
VLOOKUP always searches in the first column of the table array and returns from a column to the right. If the value you want to return sits to the left of the lookup column, VLOOKUP cannot retrieve it without rearranging the table or adding helper columns. This is a structural constraint, not a formula error.
In practice: a denial code reference table has Code in column C and Description in column B. VLOOKUP cannot look up the Code in column C and return the Description from column B. You must either restructure the table (put Code first), add a helper column, or use INDEX/MATCH or XLOOKUP instead.
VLOOKUP Failure Mode 2: Column Insertion Breaks the Index Number
The column index number in VLOOKUP is a hardcoded integer — 2 for the second column, 3 for the third, and so on. When anyone inserts a column inside the table array, all column index numbers shift. A VLOOKUP with col_index_num=3 that used to return Description now returns whatever is in the new third column — without any error, without any warning.
This is the silent wrong number failure — the most dangerous failure mode in any lookup function. The formula continues to run, returns a value that looks plausible, and no one notices until a stakeholder spots an implausible result in a report. Workbooks shared with teams who may add columns are particularly vulnerable.
VLOOKUP Failure Mode 3: Duplicate Lookup Values Return Only the First Match
VLOOKUP stops at the first match. If the lookup column contains duplicate values, VLOOKUP returns the result for the first occurrence and ignores all subsequent ones. In a claim export where a patient has multiple encounters on the same date, a VLOOKUP on patient ID may return only the first encounter’s data — silently missing the others.
This is not a bug — it is documented behaviour. The fix for multiple matches is not a lookup function at all. Use FILTER (Microsoft 365) to return all matching rows, or SUMIFS/COUNTIFS for aggregating across duplicates.
XLOOKUP: What It Fixes and What It Does Not
XLOOKUP (available in Microsoft 365 and Excel 2021) replaces VLOOKUP’s table array and column index with separate lookup array and return array arguments. This separation eliminates two of VLOOKUP’s three failure modes immediately.
=XLOOKUP(A2, CodeTable[Code], CodeTable[Description], “Unknown code”)
— Example: look LEFT — return Description column when Code is to its right
=XLOOKUP(A2, CodeTable[Code], CodeTable[ShortName], “Not found”)
What XLOOKUP Fixes Over VLOOKUP
XLOOKUP’s lookup array and return array are specified separately. The return array can be to the left, right, above, or below the lookup array — direction is irrelevant. Insert a column between them and the formula still references its arrays by address, not by a hardcoded position number. XLOOKUP also has a built-in if_not_found argument — you specify the fallback value directly in the formula without wrapping in IFERROR.
XLOOKUP’s match_mode and search_mode Arguments
XLOOKUP has two optional arguments that VLOOKUP lacks. match_mode controls how the match is performed: 0 = exact match (default), -1 = exact or next smaller, 1 = exact or next larger, 2 = wildcard match. search_mode controls search direction: 1 = first-to-last (default), -1 = last-to-first (useful for returning the most recent match), 2 = binary search ascending, -2 = binary search descending.
The last-to-first search mode is particularly useful in healthcare IT. If a patient has multiple entries in a registration table, search_mode=-1 returns the most recent entry without sorting the table. This is a capability neither VLOOKUP nor INDEX/MATCH provides without additional complexity.
What XLOOKUP Does Not Fix
XLOOKUP still returns only the first match in forward search mode, same as VLOOKUP. It is also version-restricted: Microsoft 365 and Excel 2021 only. Any workbook using XLOOKUP that is opened in Excel 2019 or earlier will show a #NAME? error where the formula appears. If your workbook is shared with colleagues on older Excel versions, XLOOKUP is not safe to use as the primary lookup function.
XLOOKUP also does not handle two-condition lookups natively — you cannot look up a combination of two columns as a single key without a helper column or an array formula approach. INDEX/MATCH handles this more cleanly.
INDEX/MATCH: Why It Still Wins in Specific Situations
INDEX/MATCH is two functions combined. INDEX returns a value from a range based on row and column numbers. MATCH returns the position of a value within a range. Together, they replicate VLOOKUP but without its directional constraint, without a hardcoded column number, and without the column insertion failure mode — and they work in every Excel version.
=INDEX(CodeTable[Description], MATCH(A2, CodeTable[Code], 0))— Two-condition lookup: match on both Payer AND ServiceType
=INDEX(RateTable[Rate],
MATCH(1, (RateTable[Payer]=A2)*(RateTable[ServiceType]=B2), 0))
Why INDEX/MATCH Is Immune to Column Insertion
INDEX references the return range directly: CodeTable[Description]. MATCH references the lookup column directly: CodeTable[Code]. Insert a new column between Description and Code — both references still track to their named columns. The formula does not break. This is the core reason INDEX/MATCH remains the lookup function of choice in shared workbooks where column structure may change.
Two-Condition Lookups: The INDEX/MATCH Advantage
The two-condition INDEX/MATCH pattern uses multiplication of two boolean arrays inside MATCH. Each condition returns an array of TRUE/FALSE values (1/0). Multiplying them produces 1 only where both conditions are TRUE in the same row. MATCH then finds the first 1 in that product array and returns its row position.
=INDEX(RateTable[Rate],
MATCH(1,
(RateTable[Payer]=”BCBS”)*(RateTable[ServiceType]=”Inpatient”),
0))
In Excel 2019 and earlier, this formula requires Ctrl+Shift+Enter to enter as an array formula (CSE). In Microsoft 365, it evaluates as a regular formula because dynamic arrays are available. XLOOKUP can replicate this with a helper column or a concatenated lookup value, but the native INDEX/MATCH array approach is more maintainable when the lookup has two or more conditions and no helper column is acceptable.
Two-Dimensional Lookup: INDEX with Two MATCH Functions
INDEX can return a value from a matrix using both a row position and a column position. Two MATCH functions supply both positions. This is the two-dimensional lookup that neither VLOOKUP nor XLOOKUP handles natively.
— Row: payer name in A2, Column: service type in B2
=INDEX(RateMatrix,
MATCH(A2, RateMatrix[Payer], 0),
MATCH(B2, RateHeaders, 0))
XLOOKUP can approximate this by nesting one XLOOKUP inside another — XLOOKUP on rows, inner XLOOKUP on columns. The INDEX/MATCH/MATCH pattern is shorter and clearer for analysts already familiar with MATCH logic.
VLOOKUP vs XLOOKUP vs INDEX/MATCH: Full Comparison
| Capability | VLOOKUP | XLOOKUP | INDEX/MATCH |
|---|---|---|---|
| Excel version compatibility | All versions | M365 / 2021 only | All versions |
| Look left (return col before lookup col) | ✗ No | ✓ Yes | ✓ Yes |
| Immune to column insertion | ✗ Breaks silently | ✓ Yes | ✓ Yes |
| Built-in not-found fallback | ✗ Needs IFERROR | ✓ 4th argument | ✗ Needs IFERROR |
| Last-to-first search (most recent match) | ✗ Not available | ✓ search_mode=-1 | ✗ Requires workaround |
| Wildcard match (*,?) | ✓ With FALSE | ✓ match_mode=2 | ✓ With MATCH type 0 |
| Two-condition lookup (no helper column) | ✗ Not possible | Workaround needed | ✓ Boolean array |
| Two-dimensional (row + column lookup) | ✗ Not possible | Nested XLOOKUP | ✓ INDEX with 2×MATCH |
| Approximate match (sorted data, tiered lookup) | ✓ range_lookup=TRUE | ✓ match_mode=-1 or 1 | ✓ MATCH type 1 or -1 |
| Formula length / readability | Short — easy to read | Medium — readable | Longer — complex scenarios are harder to audit |
| Safe in shared workbooks with column changes | ✗ No | ✓ Yes | ✓ Yes |
When All Three Fail: Shared Limitations
VLOOKUP, XLOOKUP, and INDEX/MATCH share three failure modes that no amount of syntax knowledge can fix. Understanding these prevents the mistake of reaching for a lookup function when a different approach is needed.
Shared Failure 1: Duplicate Lookup Values — All Return Only One Result
All three functions return a single value. When the lookup key appears multiple times in the lookup array, all three return only the first match (or last match if XLOOKUP uses search_mode=-1). No lookup function returns multiple rows. When you need all rows where a condition is met, use FILTER (Microsoft 365) or SUMIFS/COUNTIFS for numeric aggregation.
Shared Failure 2: Text vs Number Type Mismatch
All three functions match by value and data type. A lookup value of the number 197 does not match a lookup array cell containing the text “197”. This is the most common cause of unexpected #N/A results in lookup formulas used against data exported from EHR systems, ticketing tools, and billing platforms — where codes that look like numbers are often stored as text.
Diagnose type mismatches: left-aligned numbers in a cell are stored as text. Right-aligned numbers are stored as numbers. Check alignment before building any lookup formula against exported data. Fix with VALUE() to convert text-numbers to numbers, or TEXT() to convert numbers to text, depending on which side needs adjusting.
Shared Failure 3: Trailing Spaces Prevent Matches
“BCBS ” (with trailing space) does not match “BCBS”. All three lookup functions treat them as different strings. Exported data from EHR systems, billing systems, and project management tools frequently contains invisible trailing spaces. Wrap the lookup value and lookup array in TRIM before using them:
=XLOOKUP(TRIM(A2), TRIM(ClaimData[Payer]), ClaimData[Rate], “Not found”)— Better: clean trailing spaces in Power Query before they reach the worksheet
— Text.Trim([Payer]) in Power Query Applied Steps
The more sustainable fix is to clean trailing spaces in Power Query before the data reaches the worksheet. Wrapping every lookup formula in TRIM is functional but adds formula length and recalculation overhead on large datasets.
Real Analyst Scenarios: Healthcare IT and Finance
Scenario 1 — Healthcare IT: ICD-10 Code Mapping in Epic Clarity Exports
A reporting analyst at a health system exports encounter data from Epic Clarity. The export includes ICD-10 diagnosis codes but not descriptions. A separate reference Table called CodeMap has Code in column A and Description in column B. The analyst needs to add a Description column to the encounter export for a readability report sent to clinical leadership.
Three formulas, three different approaches:
=IFERROR(VLOOKUP([@DiagCode], CodeMap, 2, FALSE), “Code not in reference”)— XLOOKUP: direction-independent, built-in fallback, safer long-term
=XLOOKUP([@DiagCode], CodeMap[Code], CodeMap[Description], “Code not in reference”)
— INDEX/MATCH: works in all Excel versions, immune to column insertion
=IFERROR(INDEX(CodeMap[Description], MATCH([@DiagCode], CodeMap[Code], 0)), “Code not in reference”)
The VLOOKUP formula works correctly today but will break silently if a new column is inserted into CodeMap between Code and Description. The XLOOKUP formula is cleaner and safe from that failure. The INDEX/MATCH formula is the correct choice if the workbook must support colleagues still on Excel 2019.
One practical issue specific to ICD-10 codes in Clarity exports: some ICD-10 codes are stored without the decimal separator (F321 instead of F32.1). The CodeMap reference table may use the formatted version. If the formats do not match, all three functions return #N/A. Fix the mismatch in Power Query before the data reaches the worksheet — either remove decimals from the reference table or add them to the export, consistently. This is the kind of data quality constraint that appears in every healthcare IT workbook that joins Clarity data against external reference tables.
Scenario 2 — Revenue Cycle: Payer-Service Rate Lookup with Two Conditions
A revenue cycle analyst needs to pull the contracted rate for each claim. The rate table has three columns: Payer, ServiceType, and Rate. Each payer-service combination has a unique rate. A claim with Payer=”BCBS” and ServiceType=”Inpatient” should return the rate for that specific combination — not just the first BCBS rate or the first Inpatient rate.
VLOOKUP cannot do this without a helper column. XLOOKUP requires a concatenated helper column or a nested array approach. INDEX/MATCH handles it natively:
=IFERROR(
INDEX(RateTable[Rate],
MATCH(1,
(RateTable[Payer]=[@Payer])*(RateTable[ServiceType]=[@ServiceType]),
0)),
“Rate not found”)— XLOOKUP approach using concatenated helper column (less elegant)
— Helper column in RateTable: =[@Payer]&”|”&[@ServiceType]
=XLOOKUP([@Payer]&”|”&[@ServiceType], RateTable[LookupKey], RateTable[Rate], “Rate not found”)
The INDEX/MATCH approach avoids adding a helper column to the rate table. If the rate table is managed by another team and you cannot modify its structure, the INDEX/MATCH array formula is the only option without column additions. The XLOOKUP helper column approach requires modifying the rate table — which may be a governance issue in a shared workbook.
Scenario 3 — QA: Test Case Status Lookup with Most Recent Entry
A UAT workbook has a TestLog Table where test cases are re-entered each time they are retested. Scenario SC-042 may appear three times in the log — initial test (Fail), first retest (Fail), second retest (Pass). A summary formula needs the most recent status for each scenario ID, not the first.
=XLOOKUP(A2, TestLog[ScenarioID], TestLog[Status], “Not found”, 0, -1)— VLOOKUP: always returns the FIRST match — wrong for retest tracking
=VLOOKUP(A2, TestLog, 3, FALSE) — returns initial test result, not most recent
— INDEX/MATCH workaround for last match (complex, requires helper or LARGE)
=IFERROR(
INDEX(TestLog[Status],
MATCH(2, 1/(TestLog[ScenarioID]=A2), 1)),
“Not found”)
This is the scenario where XLOOKUP is unambiguously the best choice. The search_mode=-1 argument provides exactly this capability in a single, readable formula. The INDEX/MATCH workaround for last match is functional but significantly more complex and harder to maintain. VLOOKUP cannot do it at all without restructuring the data.
This UAT tracking pattern connects directly to the BAT vs UAT sign-off requirements — the sign-off condition depends on the most recent status, not the original test result.
Error Handling: IFERROR vs IFNA for Lookup Functions
VLOOKUP and INDEX/MATCH return #N/A when the lookup value is not found. XLOOKUP returns the if_not_found value instead — no wrapper needed. For VLOOKUP and INDEX/MATCH, use IFNA rather than IFERROR when the only expected error is “not found”:
=IFNA(VLOOKUP(A2, CodeMap, 2, FALSE), “Code not in reference”)
=IFNA(INDEX(CodeMap[Description], MATCH(A2, CodeMap[Code], 0)), “Code not in reference”)— IFERROR: catches all errors — hides structural problems silently
=IFERROR(VLOOKUP(A2, CodeMap, 2, FALSE), “Code not in reference”)
The distinction matters in analyst workbooks. A VLOOKUP wrapped in IFERROR that returns #REF! because the table array reference is broken will silently show “Code not in reference” for every row — making a structural formula error look like a data quality issue. IFNA lets #REF! and #VALUE! errors surface where they should be visible.
| Error wrapper | What it catches | Best used with | Risk |
|---|---|---|---|
| IFNA | #N/A only | VLOOKUP, INDEX/MATCH, MATCH when “not found” is the only expected error | Low — structural errors remain visible |
| IFERROR | All errors: #N/A, #REF!, #VALUE!, #DIV/0!, etc. | Division formulas where zero denominator is expected; IFERROR(formula, 0) | High if overused — hides structural formula problems |
| XLOOKUP if_not_found | #N/A only (built into the function) | Any XLOOKUP formula — cleaner than an IFNA wrapper | Low — structural errors still surface separately |
Decision Framework: Choosing the Right Lookup Function
Answer four questions about the lookup before choosing a function. The answers determine which function is correct — not preference or familiarity.
The Default for Most Analyst Workbooks
For analysts on Microsoft 365 building workbooks where all colleagues are also on Microsoft 365: XLOOKUP is the default for single-condition lookups. It handles left-lookup, built-in fallback, and is safe from column insertion. Use INDEX/MATCH for two-condition lookups and two-dimensional lookups. Never use VLOOKUP in new workbooks unless there is a specific version compatibility requirement.
For analysts in healthcare organisations where Excel versions vary across departments: INDEX/MATCH is safer as the default. It works in every version, handles left-lookup, and is immune to column insertion. The formula is longer, but it is correct in every environment the workbook may reach.
VLOOKUP should only be chosen when all four of these conditions are true: the table structure is stable and controlled, no colleague will add columns to the lookup table, all users are on a consistent Excel version, and a single condition lookup is sufficient. In practice, few shared analyst workbooks meet all four conditions. The column insertion failure mode alone eliminates VLOOKUP from most shared workbooks in enterprise environments.
The lookup function choice connects to the broader Excel skill stack described in the Excel for IT Analysts hub. XLOOKUP and INDEX/MATCH are Analyst Core level skills that determine whether workbooks remain accurate and maintainable over time — or become sources of silent wrong numbers that reach leadership reports.
Converting VLOOKUP to XLOOKUP or INDEX/MATCH: The Conversion Pattern
When you inherit a workbook with VLOOKUP formulas and need to convert them to a safer function, the pattern is mechanical:
| VLOOKUP argument | Maps to in XLOOKUP | Maps to in INDEX/MATCH |
|---|---|---|
| lookup_value (A2) | lookup_value (A2) | MATCH lookup_value (A2) |
| table_array, column 1 ($F$1:$H$50 → col 1 = F) | lookup_array ($F$1:$F$50) | MATCH lookup_array ($F$1:$F$50) |
| table_array, col_index 2 ($F$1:$H$50 → col 2 = G) | return_array ($G$1:$G$50) | INDEX return_range ($G$1:$G$50) |
| range_lookup (FALSE) | match_mode (0) | MATCH match_type (0) |
| IFERROR fallback (added separately) | if_not_found (“Not found”) | IFNA fallback (added separately) |
Performance Differences: Does the Lookup Function Choice Affect Speed?
For datasets under 50,000 rows, the performance difference between VLOOKUP, XLOOKUP, and INDEX/MATCH is negligible in practice — all three execute in milliseconds. For workbooks with 100,000+ rows or with hundreds of lookup formulas referencing large Tables, the differences become measurable.
XLOOKUP uses binary search by default when data is sorted (search_mode=2), which is significantly faster than sequential search on large sorted datasets. VLOOKUP with range_lookup=TRUE also uses binary search — but requires sorted data and uses approximate match, not exact. INDEX/MATCH with MATCH type 0 always uses sequential search, making it slower on very large unsorted arrays than XLOOKUP with binary search mode.
For most analyst workbooks, this distinction does not matter. Reference tables for ICD-10 codes, payer names, and denial codes have hundreds to a few thousand rows — not hundreds of thousands. If performance is a genuine concern on a large workbook, the correct fix is usually to move the lookup logic to Power Query (which processes the join once during query refresh rather than recalculating on every worksheet change) rather than switching lookup functions.
Approximate Match Lookups: Tiered Rates and Aging Buckets
Approximate match is the least understood capability of all three functions — and the most useful for tiered rate lookups and aging bucket categorisation. Instead of finding an exact value, approximate match finds the largest value in the lookup array that is less than or equal to the lookup value.
For approximate match to work correctly, the lookup array must be sorted in ascending order. All three functions require this for approximate match — the VLOOKUP documentation notes this explicitly but the warning is often ignored.
— Bucket names: 0-30, 31-60, 61-90, 90+— VLOOKUP approximate match (range_lookup=TRUE)
=VLOOKUP([@DaysOutstanding], AgingBuckets, 2, TRUE)
— XLOOKUP with match_mode=-1 (exact or next smaller)
=XLOOKUP([@DaysOutstanding], AgingBuckets[Threshold], AgingBuckets[Label], “Unknown”, -1)
— INDEX/MATCH with match_type=1 (next smaller on sorted ascending data)
=INDEX(AgingBuckets[Label], MATCH([@DaysOutstanding], AgingBuckets[Threshold], 1))
All three produce the same result for sorted data. XLOOKUP with match_mode=-1 has an advantage: it does not require sorted data for the approximate match to work correctly. The VLOOKUP and INDEX/MATCH approaches will return wrong results silently if the threshold column is not sorted ascending. XLOOKUP’s explicit match mode argument makes the intent clear and removes the sorting dependency.
VLOOKUP on Tables vs Plain Ranges: The Hidden Fragility
The column insertion failure mode of VLOOKUP is most dangerous when the table array is a plain range with a hardcoded end row: $F$1:$H$200. When a new column is inserted between F and H, the column index number is wrong. When data grows past row 200, rows 201 and beyond are excluded silently.
Using VLOOKUP with an Excel Table as the table array partially mitigates the second problem. =VLOOKUP(A2, CodeMap, 2, FALSE) where CodeMap is a Table name will include all Table rows regardless of how many exist. But the column index number problem remains — insert a column inside CodeMap and the 2 now points to the wrong column.
XLOOKUP and INDEX/MATCH with structured references solve both problems completely:
=XLOOKUP(A2, CodeMap[Code], CodeMap[Description], “Not found”)— INDEX/MATCH with structured references: same immunity, all Excel versions
=IFNA(INDEX(CodeMap[Description], MATCH(A2, CodeMap[Code], 0)), “Not found”)
Both formulas reference the columns by name. Insert any number of columns inside CodeMap — the formulas still find their columns correctly. Add any number of rows — the Table expands and the formulas include every row automatically. This combination of Table structure with XLOOKUP or INDEX/MATCH is the most maintainable lookup pattern available in Excel for analyst-managed workbooks.
The complete Excel Tables coverage — why to convert before touching any dataset, how structured references work, and the integration with Power Query and pivot tables — is in the Excel for IT Analysts hub. That foundation is what makes lookup formulas like the ones in this article maintainable over time rather than fragile workarounds.
Wildcard Matching: Partial Text Lookups
All three functions support wildcard matching for partial text lookups. The asterisk (*) matches any sequence of characters. The question mark (?) matches any single character.
=XLOOKUP(“*Blue*”, PayerTable[PayerName], PayerTable[ContractTier], “Not found”, 2)— Find denial codes that start with “CO-“
=VLOOKUP(“CO-*”, DenialTable, 2, FALSE)
— INDEX/MATCH with wildcard
=IFNA(INDEX(PayerTable[ContractTier], MATCH(“*Blue*”, PayerTable[PayerName], 0)), “Not found”)
Wildcard matching has an important constraint: it always returns the first match, and it is case-insensitive. “Blue Cross”, “BlueCross”, and “bluecross” would all match “*Blue*”. For strict pattern matching on structured codes like ICD-10 or denial codes where partial matches could produce ambiguous results, use exact match (FALSE or 0) rather than wildcards. Reserve wildcards for genuinely fuzzy text lookups where multiple variations of the same entity name appear in different data sources.
The Case for Never Using VLOOKUP in New Workbooks
The argument against VLOOKUP in new workbooks is not that it is technically incorrect — it is that it is fragile by design. The column index number is a maintenance liability that no amount of documentation prevents in shared workbooks. Every experienced analyst who works in environments where multiple people maintain the same workbook has encountered a VLOOKUP that silently started returning wrong values after a column was inserted. The fix is always the same: switch to XLOOKUP or INDEX/MATCH.
VLOOKUP has one remaining legitimate use case: workbooks that must remain compatible with Excel 2016 or earlier, AND where the analyst controls the full table structure, AND where no other user will modify the column layout. This describes a very small percentage of analyst workbooks in enterprise healthcare IT environments. In most cases, the version compatibility argument for VLOOKUP is actually an argument for INDEX/MATCH — which is also universally compatible and does not have the column index number problem.
The practical transition: in any workbook you build from scratch, use XLOOKUP or INDEX/MATCH. In inherited workbooks with VLOOKUP, convert to INDEX/MATCH when you first encounter a VLOOKUP that has produced a wrong result — which is usually the first time a column is inserted into the lookup table after you inherit the workbook. After converting it once, the pattern becomes fast and the habit becomes automatic.
Check your organisation’s Excel version before choosing a lookup function, not after. If Microsoft 365 is the standard for everyone who will open the workbook — use XLOOKUP for single-condition lookups, INDEX/MATCH for two-condition and two-dimensional lookups. If any user might open the workbook on Excel 2019 or earlier — use INDEX/MATCH throughout. Never use VLOOKUP in a new shared workbook regardless of version. The column insertion silent failure mode alone makes VLOOKUP the wrong default for any workbook that will be maintained by more than one person.
Authoritative References
- Microsoft Support — XLOOKUP Function: Full syntax reference covering all six arguments, match modes, and search modes with examples
- Microsoft Support — INDEX Function: Syntax and array form documentation for two-dimensional and multi-condition lookup patterns
