Excel VBA Find Function

If you just need to excel vba find function and move on, the boxed answer at the top is all you need. The rest of this page is for when you want to understand why it works in Excel, adapt it to a trickier version, or make it robust enough to hand to a colleague. We keep the opening short on purpose — the depth is here when you want it, not in your way when you don’t.

Exact answer

In Excel: Set hit = ws.Cells.Find(What:="text", LookIn:=xlValues, LookAt:=xlPart) locates the first match, and ws.Cells.Replace What:="old", Replacement:="new" changes them all in one call. Excel for the web cannot create, run, or edit VBA macros. Open the workbook in the Excel desktop app to work with macros.

On this page8

VBA macro: Find and Replace with VBA

Scroll code horizontally to see more
Sub FindAndReplaceInSheet()
    Dim ws As Worksheet
    Dim hit As Range
    Set ws = ActiveSheet
    Set hit = ws.Cells.Find(What:="draft", LookIn:=xlValues, _
                            LookAt:=xlPart, MatchCase:=False)
    If hit Is Nothing Then
        MsgBox "Nothing to replace.", vbExclamation
        Exit Sub
    End If
    MsgBox "First match: " & hit.Address, vbInformation
    ws.Cells.Replace What:="draft", Replacement:="final", _
                     LookAt:=xlPart, MatchCase:=False
End Sub

Range.Find returns Nothing rather than raising an error when there is no match, so the If hit Is Nothing guard is required — using .Address on Nothing is error 91. Always name LookIn, LookAt and MatchCase explicitly; unset arguments inherit whatever the user last chose in the Find dialog.

How to run this macro

  1. Press Alt + F11 to open the VBA editor.
  2. Insert > Module.
  3. Paste the code above.
  4. Press F5, or close the editor and run it from Developer > Macros.
  5. Save the file as .xlsm so the macro is kept.
▤Annotated stepsExcel
1

Decide whether you need the location of matches (Find) or just to change them (Replace).

2

For Find, assign the result with Set and name What, LookIn, LookAt and MatchCase explicitly.

3

Guard immediately with If hit Is Nothing Then — a miss returns Nothing, not an error.

4

To visit every match, save the first hit's Address and loop with FindNext until the address comes round again.

5

For a bulk change, call Range.Replace once on the whole range instead of looping — it is dramatically faster.

Ctrl+CthenCtrl+Shift+V⌘+Cthen⌘+Ctrl+VPaste values · WindowsMac

What this does

VBA offers three related tools and they solve different problems. Range.Find locates a value and hands back the cell as a Range object, or Nothing when there is no match — it tells you where something is. Range.Replace changes every match in the target range in a single call and returns only True or False; it is far faster than looping when you do not need to know which cells changed. FindNext continues a search from a previous hit, and the correct way to iterate is to remember the address of the first result and stop when the search wraps back round to it, since Find loops the range endlessly otherwise. Separately, VBA's own Replace() function works on a string rather than a sheet: Replace(text, "a", "b") returns a modified copy and never touches a cell, which is what you want inside a loop over values you have already read. The single biggest trap is that Find's unspecified arguments persist from the last search anyone performed, including one a user did through the dialog — so a macro that omits LookIn behaves differently depending on the session. Before the code does anything, check the environment it needs: desktop Excel, a macro-enabled workbook (.xlsm) and macros allowed for this file. If the workbook came by email or download, Office can block its macros until the file is trusted. Treat “excel vba find function” as a small repeatable workflow rather than a one-off click you hope to remember next time. Use a small test block before the live file, so any surprise in the affected formula shows up while it is still harmless. When this is a ribbon command, the selection matters more than the button: confirm the range, apply the command, then spot-check the output before saving. That turns a workflow that saves repeating the same clicks every week into a method you can reuse, explain, and defend when the workbook leaves your screen.

A worked example

A sheet full of records is marked "DRAFT" in mixed positions and casing, and every one should read "FINAL". Sub FindAndReplaceInSheet() first calls Find to confirm at least one match exists and reports its address, then calls Replace once to change them all. The If hit Is Nothing guard is what stops the macro dying on the .Address line when the sheet is already clean. When you need to act on each match rather than just rewrite it — say, colouring every cell containing "draft" — use the FindNext pattern: store firstAddress = hit.Address, do the work, then Set hit = ws.Cells.FindNext(hit) and loop while Not hit Is Nothing And hit.Address <> firstAddress. Without that address comparison the loop never ends, because Find wraps around to the beginning of the range. Find and Replace is where macros meet messy real data — inconsistent labels, stray markers, values that need normalising before anything else can run. The two behaviours worth internalising are that a miss returns Nothing rather than erroring, and that omitted arguments are inherited rather than defaulted; between them they account for most of the bug reports this pair of methods generates. Step through the code once (Debug ▸ Step Into in the VBA editor) and watch the sheet after each line. It shows exactly what the macro changes before you let it run end to end.

In Google Sheets

Google Sheets does not run VBA. A macro written for Excel has to be rewritten in Google Apps Script (Extensions ▸ Apps Script), and an .xlsm file converted to Sheets keeps its cells but drops the code. Keep this page bookmarked for the next time the same question comes up. Better still, repeat the steps once in your own workbook — doing it yourself is what turns a copied answer into something you remember. For “excel vba find function”, the checks are about the environment as much as the code: desktop Excel with macros enabled for this file, a macro-enabled workbook and a saved copy to fall back on. Run it once on test data, compare the result with what the code says it does, and only then run it on the real sheet.

Common mistakes

  • Using the result of Find without checking for Nothing. Any property access on Nothing raises error 91, and a sheet that simply has no match is the normal case, not an exceptional one.
  • Leaving LookIn and LookAt unspecified. Those arguments persist between searches — including from the user's last use of Ctrl + F — so the same macro can match whole cells one day and partial text the next.
  • Looping FindNext without tracking the first address. The search wraps around at the end of the range, so the loop runs forever; comparing against the stored first address is the standard termination test.
  • Searching with LookIn:=xlValues when you meant formulas. xlValues sees displayed results, xlFormulas sees the underlying text, so a macro hunting for a cell reference must use the latter.
  • Confusing Range.Replace with VBA's Replace() function. The first edits cells on a sheet, the second returns a modified string and changes nothing — using the wrong one produces code that appears to do nothing at all.

Frequently asked questions

Why does my Find raise error 91?

Because it found nothing and returned Nothing, and the next line asked it for a property. Test If hit Is Nothing Then before touching the result — that branch is not optional.

How do I loop through every match?

Save the first hit's Address, act on it, then Set hit = rng.FindNext(hit) and repeat while the address differs from the saved one. Find wraps at the end of the range, so that comparison is what ends the loop.

Is Replace faster than looping over cells?

Considerably. Range.Replace performs the whole operation inside Excel in one call, while a loop crosses the VBA-to-Excel boundary once per cell. Loop only when each match needs different handling.

How do I search only part of a cell rather than the whole value?

Pass LookAt:=xlPart. LookAt:=xlWhole requires the entire cell to equal the search text. Always state it explicitly, because the unset value carries over from the previous search.