🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA Worksheet_Change in Excel — Run Code When a Cell Is Edited (and the Infinite Loop to Avoid)

|

VBA Worksheet_Change in Excel — Run Code When a Cell Is Edited (and the Infinite Loop to Avoid)

TL;DRWorksheet_Change is an event Excel fires every time a user (or your code) edits a cell on that sheet. Excel hands you the changed cell as Target, so you can react — timestamp the row, validate the entry, log the edit. The one thing you must know: if your handler writes to a cell, that write fires Worksheet_Change again — and again — until Excel freezes. The fix is a single pair of lines, Application.EnableEvents = False … = True, around every write. Get that reflex and everything else is detail.

' Lives in the sheet's own object (e.g. Sheet1), NOT a Module or ThisWorkbook.
Private Sub Worksheet_Change(ByVal Target As Range)
    If Intersect(Target, Range("B:B")) Is Nothing Then Exit Sub   ' only react to column B
    On Error GoTo Done
    Application.EnableEvents = False          ' stop our own write from re-firing this event
    Target.Offset(0, 1).Value = Now          ' stamp the time next to the edit
Done:
    Application.EnableEvents = True           ' ALWAYS turn events back on
End Sub

Reacting to edits is where VBA stops being "buttons that run macros" and starts feeling alive: a cell changes and the sheet responds on its own. Worksheet_Change is the event that powers audit trails, auto-timestamps, live validation, and dependent dropdowns. It's also the event that produces the most panicked "Excel froze and I lost my work" moments — because the obvious code is one small step from an infinite loop.

What you'll learn

  • The mental model — an edit sensor that hands you the changed cell as Target
  • The infinite-loop trap — and the Application.EnableEvents fix that defines event VBA
  • Why a crash can turn all your events off until you restart Excel
  • How to scope the handler with Intersect so it doesn't run on every edit
  • Why it ignores formula recalcs — and when you need Worksheet_Calculate instead

The mental model: an edit sensor, not a button

Worksheet_Change is a sensor wired to the sheet. You don't call it; the moment a cell's content changes, Excel calls you, passing the changed cell(s) as a Range named Target. Your job is to read Target and decide what to do about it.

That framing settles two things. First, Target is your entire input — it's where the edit happened, and it can be one cell or many (a paste, a fill, a delete-across-a-selection all arrive as a multi-cell Target). Second, the handler lives in the specific sheet's code object — Sheet1 in the Project Explorer, not ThisWorkbook and not a Module. The signature is fixed: Private Sub Worksheet_Change(ByVal Target As Range). (The workbook-wide version, for all sheets at once, is Workbook_SheetChange in ThisWorkbook.)

The trap that freezes Excel: your write re-fires the event

Here is the bug every VBA developer writes exactly once. You want to react to an edit by writing something back to the sheet:

Private Sub Worksheet_Change(ByVal Target As Range)
    Target.Offset(0, 1).Value = Now   ' write next to the edit... which is ITSELF an edit
End Sub

Writing to Target.Offset(0, 1) changes a cell — which fires Worksheet_Change again — whose write fires it again — forever. Excel recurses until it runs out of stack and throws, or simply appears frozen. The change event has no built-in guard against reacting to its own reaction.

The fix is the most important idiom in event VBA: switch events off around any write, then switch them back on.

Private Sub Worksheet_Change(ByVal Target As Range)
    Application.EnableEvents = False
    Target.Offset(0, 1).Value = Now
    Application.EnableEvents = True
End Sub

With EnableEvents = False, your write doesn't trigger a new Worksheet_Change, so there's no recursion. Then you restore it. Burn in the rule: inside any event that writes to cells, wrap the write in Application.EnableEvents = False … = True.

The rule that hides the trap's second half: a crash leaves events off

Application.EnableEvents is a single, application-global switch, and Excel does not restore it automatically. That creates a nasty second failure mode: if your handler errors after setting it to False but before setting it back, events stay off — for the whole Excel session, across every sheet and workbook.

The symptom is bewildering: "my macros just stopped working." Your Worksheet_Change didn't break — events are globally suppressed because an earlier run died mid-handler. That's why the safe pattern always re-enables events in an error handler:

Private Sub Worksheet_Change(ByVal Target As Range)
    On Error GoTo CleanExit
    Application.EnableEvents = False
    Target.Offset(0, 1).Value = Now
    ' ... more logic that might error ...
CleanExit:
    Application.EnableEvents = True     ' runs whether we succeeded or errored
End Sub

This is exactly the "one exit that always cleans up" discipline from VBA On Error, and it's non-negotiable here. (If events are already stuck off, run one line in the Immediate window — Application.EnableEvents = True — or just restart Excel.)

The rule that keeps it targeted: scope with Intersect

A bare Worksheet_Change fires for any cell on the sheet. If you only care about edits in one column or one table, you must say so — otherwise the handler runs (and maybe writes) on every unrelated edit. The tool is Intersect, which returns the overlap between Target and the range you care about, or Nothing if there's none:

Private Sub Worksheet_Change(ByVal Target As Range)
    ' Ignore edits outside B2:B1000
    If Intersect(Target, Range("B2:B1000")) Is Nothing Then Exit Sub
    ' ... react only to the part that matters ...
End Sub

Two related cautions. Target can be many cells — a paste or a column-fill hands you a multi-cell range, so code that assumes Target.Value is a single value will error or misbehave; either loop over Target or restrict with If Target.Count > 1 Then Exit Sub when you genuinely want single edits. And react to the intersection, not all of Target, when a paste spans your watched range and cells outside it.

The distinction that trips people: it ignores formula recalcs

Worksheet_Change fires on a change to a cell's content — a typed value, a pasted value, a deletion, a VBA write. It does not fire when a cell merely shows a new number because a formula recalculated. If C1 is =A1+B1 and A1 changes, the event fires for A1 (the edited cell), not for C1 (the recalculated one).

When you need to react to a result changing, that's a different event — Worksheet_Calculate — which fires on recalculation but gives you no Target (you have to inspect the cells yourself). Choosing the wrong one is a quiet failure: your handler simply never runs. The rule: Change = someone edited the cell; Calculate = a formula's output moved.

Worksheet_Change is one half of the "react to the user" pair. Its sibling, Worksheet_SelectionChange, fires when the cursor moves rather than when a value changes — and both belong to the same event family as Workbook_Open, the event that runs when the file first opens.

How ExcelMaster helps

An edit-reaction macro is deceptively fiddly: right object, Intersect to scope it, EnableEvents to avoid the loop, an error handler so a crash doesn't kill every event in Excel. Miss the EnableEvents pair and you freeze the file; miss the error handler and you break events silently.

ExcelMaster lets you state the behaviour instead. Say "when someone edits column B, put the current time in column C next to it," and it writes a Worksheet_Change in the right sheet object — scoped with Intersect, guarded with EnableEvents, wrapped so an error can't leave events stuck off. You keep the sheet and the code; you skip the one-time ritual of freezing your own workbook to learn the rule.

Frequently asked questions

Why does my Worksheet_Change cause an infinite loop or freeze?

Because your handler writes to a cell, and that write is itself a change that fires Worksheet_Change again — endlessly. Wrap every write in Application.EnableEvents = False before it and Application.EnableEvents = True after it, and always re-enable in an error handler so a crash can't leave events off.

How do I make Worksheet_Change run only when a specific column changes?

Use Intersect at the top of the handler: If Intersect(Target, Range("B:B")) Is Nothing Then Exit Sub. That exits immediately unless the edit touched column B. Intersect returns the overlap between the edited range and the range you care about, or Nothing when there's no overlap.

Does Worksheet_Change fire when a formula recalculates?

No. It fires only when a cell's content is edited — typed, pasted, deleted, or written by VBA. A cell that shows a new value because a formula recalculated does not trigger it. For that, use the Worksheet_Calculate event, which fires on recalculation but does not give you a Target.

Where does Worksheet_Change code go?

In the code module of the specific worksheet — double-click the sheet (e.g. Sheet1) under "Microsoft Excel Objects" in the Project Explorer and put Private Sub Worksheet_Change(ByVal Target As Range) there. It does not work from a standard Module. For all sheets at once, use Workbook_SheetChange in ThisWorkbook.

My VBA events stopped working — how do I fix it?

An earlier handler probably set Application.EnableEvents = False and errored before restoring it, leaving events globally off. Type Application.EnableEvents = True in the Immediate window (Ctrl+G) and press Enter, or restart Excel. Prevent it by always re-enabling events in an error handler.

Tested in

Tested in: Excel 365 (Windows 11), VBA 7.1 — last verified 2026-08-02.

Related guides: VBA Worksheet_SelectionChange · VBA Workbook_Open · VBA On Error · VBA Range · VBA For Loop