
What You’ll Learn
VBA loops and conditions in Excel are what separate a macro that runs once on a fixed dataset from one that actually does the thinking for you — across any amount of data, every single time.
Here is the situation a lot of people find themselves in. You recorded a macro. It works. But it only works on the exact 50 rows you had open when you recorded it. Add more data next week, and suddenly half the rows get ignored. Change the layout slightly, and the whole thing falls apart. That is the wall loops and conditions break through.
This guide covers every major loop type in VBA, how to write conditional logic that actually holds up, and how to debug it all when something goes sideways. Because something will go sideways — that is not pessimism, that is just VBA.
Think of a loop as a to-do list that repeats. You hand VBA a task — say, “check this cell” — and you tell it to do that task on every row in your dataset. The loop is the mechanism that keeps repeating the task until the work is done.
A condition is the decision point inside that repetition. Not just “check this cell,” but “check this cell, and if the value is overdue, flag it red.” Loop handles the repetition. Condition handles the logic. You almost always need both at the same time, and that is exactly how this lesson treats them.
You have probably seen this more than once. The macro runs, no errors, and then you look at the output and something is off. Rows got skipped. Wrong cells got highlighted.
Nine times out of ten, the culprit is a hardcoded range. If your loop is written to process rows 2 through 50, it will always process exactly rows 2 through 50 — regardless of whether your data has grown to 300 rows or shrunk to 12.
The fix is dynamic range detection — calculating the actual last row of data at runtime instead of baking a number into your code. Every example in this lesson uses it.
The For Next loop is where almost everyone starts. It runs a block of code a set number of times using a counter variable you define.
Dim i As Long
For i = 1 To 10
Debug.Print i
Next iThe loop runs 10 times. i starts at 1, increases by 1 each time, and stops after hitting 10. One important note: always declare your counter as Long, not Integer. Integer tops out at 32,767. If you are looping through a large spreadsheet and hit that ceiling, you get an overflow error. Long handles numbers in the billions — just use Long by default.
By default, For Next counts up by 1. The Step keyword changes that:
For i = 10 To 1 Step -1
Debug.Print i
Next i
' Skip to even rows only
For i = 2 To 100 Step 2
Debug.Print i
Next iCounting backwards is not just trivia — it is critical when deleting rows inside a loop. If you delete rows counting forward, you shift the row numbers beneath you and skip rows entirely. Step -1 prevents that completely.
For Next is perfect when you know exactly how many iterations you need. But when you are working with a range of cells and just want to touch every cell without tracking the row number, For Each is cleaner, less error-prone, and reads more like plain English.
For Each does not count. It moves through every item in a collection — every cell in a range, every sheet in a workbook — one at a time. You are not tracking row numbers. You are just saying: give me each item, one by one.
Dim cell As Range
For Each cell In Range("A2:A100")
Debug.Print cell.Value
Next cellHere is the pattern you will use constantly once you know it:
Dim LastRow As Long
Dim cell As Range
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
For Each cell In Range("A2:A" & LastRow)
Debug.Print cell.Value
Next cellThat LastRow line starts at the absolute bottom of column A and jumps upward to the last non-empty cell. Your loop now adjusts automatically whether you have 20 rows or 20,000.
💡 PRO TIP
Calculate LastRow before the loop starts, not inside it. Recalculating on every single iteration is wasted processing. Calculate it once, store it in a variable, then use that variable throughout the loop.
Dim LastRow As Long
Dim cell As Range
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
For Each cell In Range("B2:B" & LastRow)
If cell.Value > 100 Then
cell.Interior.Color = RGB(255, 0, 0)
End If
Next cellThe Do While loop has no counter and no fixed end point. It runs for as long as a condition stays true, and stops when that condition becomes false.
Advertisement-X
' Top condition — checks before running the body
Dim i As Long
i = 1
Do While i <= 10
Debug.Print i
i = i + 1
Loop
' Bottom condition — runs at least once before checking
Dim i As Long
i = 1
Do
Debug.Print i
i = i + 1
Loop While i <= 10If i starts at 11, the top version never runs. The bottom version runs once before checking. Use the bottom form only when you genuinely need that guaranteed first execution.
Do Until ActiveCell.Value = ""
ActiveCell.Offset(1, 0).Activate
LoopDo Until runs until a condition becomes true, rather than while it is true. You will see this pattern constantly in report-building macros that need to find where dynamic data ends.
Trust me on this — an infinite loop is one of the most unpleasant things to experience in Excel. The application freezes, the spinning cursor mocks you, and eventually you force-quit hoping your file was saved. The safest defense is a counter failsafe:
Dim i As Long
Dim safety As Long
safety = 0
Do While i <= 10
i = i + 1
safety = safety + 1
If safety > 10000 Then Exit Do
Loop⚠️ COMMON MISTAKE
Writing a Do While loop without changing the condition variable inside the loop body. If nothing inside the loop ever pushes the condition toward false, you have written an infinite loop. Before running any Do While, trace the logic manually: what specific line of code will eventually make the condition false?
If condition Then
' code if true
Else
' code if false
End If
' Single-line form
If cell.Value > 100 Then cell.Interior.Color = RGB(255, 0, 0)If score >= 90 Then
grade = "A"
ElseIf score >= 80 Then
grade = "B"
ElseIf score >= 70 Then
grade = "C"
Else
grade = "F"
End IfReadable up to four or five branches. Past that, it becomes hard to follow and harder to maintain. That is when Select Case earns its place.
Select Case status
Case "Paid"
cell.Interior.Color = RGB(0, 255, 0)
Case "Overdue"
cell.Interior.Color = RGB(255, 0, 0)
Case "Pending"
cell.Interior.Color = RGB(255, 255, 0)
Case Else
cell.Interior.Color = xlNone
End SelectSame logic as the ElseIf chain, but far easier to scan and extend. Reach for Select Case any time you have three or more specific value comparisons on the same variable.
For Each cell In Range("A2:A" & LastRow)
If cell.Value <> "" Then
If cell.Value > 100 Then
cell.Interior.Color = RGB(255, 0, 0)
Else
cell.Interior.Color = RGB(0, 255, 0)
End If
End If
Next cellEvery opening keyword gets its closing keyword. Every level gets its own indent. If nesting goes four or five levels deep, extract the inner logic into a separate Sub and call it from inside the loop. Flat code is always easier to debug than deeply nested code.
Column A has due dates. Column B has payment status. Flag any row where the date has passed and the payment is not marked "Paid."
Dim LastRow As Long
Dim i As Long
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To LastRow
If Cells(i, 1).Value < Date And Cells(i, 2).Value <> "Paid" Then
Rows(i).Interior.Color = RGB(255, 200, 200)
Cells(i, 3).Value = "OVERDUE"
End If
Next iA CRM export arrives with blank rows, "N/A" placeholders, and inconsistent spacing. One loop handles all of it:
Dim LastRow As Long
Dim cell As Range
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
For Each cell In Range("A2:A" & LastRow)
If Trim(cell.Value) = "" Or cell.Value = "N/A" Then
cell.EntireRow.Delete
Else
cell.Value = Trim(cell.Value)
End If
Next cell⚠️ COMMON MISTAKE
Deleting rows inside a For Each loop can cause VBA to skip the row immediately after each deletion. For any row-deletion operation, switch to For Next counting backwards with Step -1. The For Each version above is for illustration — in production, replace the loop type.
Dim i As Long
i = 2
Do While Cells(i, 1).Value <> ""
i = i + 1
Loop
Cells(i, 1).Value = "TOTAL"
Cells(i, 2).Value = Application.WorksheetFunction.Sum(Range("B2:B" & i - 1))Imagine searching through 50,000 rows for the first cell containing "TARGET." You find it on row 3. Without an exit mechanism, your loop checks the remaining 49,997 rows for no reason. Exit For and Exit Do stop the loop the instant you say so. On large datasets, this can turn a two-minute macro into a two-second one.
Dim cell As Range
Dim foundCell As Range
For Each cell In Range("A2:A" & LastRow)
If cell.Value = "TARGET" Then
Set foundCell = cell
Exit For
End If
Next cell
If Not foundCell Is Nothing Then
MsgBox "Found at row " & foundCell.Row
End IfDim i As Long
Dim counter As Long
i = 2
counter = 0
Do While Cells(i, 1).Value <> ""
i = i + 1
counter = counter + 1
If counter > 100000 Then
MsgBox "Safety limit reached — check your data"
Exit Do
End If
Loop💡 PRO TIP
Make the safety counter a reflex. Put it in every Do While loop during development. Once you have verified the termination logic is solid, you can leave it in anyway — it costs almost nothing in performance and protects you from the rare edge case you did not anticipate.
Open your macro in the VBA editor and press F8. One line executes. Press F8 again. Another line executes. The yellow arrow shows you exactly where you are in the code at all times.
This is the most valuable debugging technique in VBA, full stop. When a loop produces wrong output, step through the first three to five iterations manually and watch your variables change in real time. You will almost always spot the problem within a few keystrokes.
Add Debug.Print inside the loop to write values to the Immediate Window (Ctrl + G opens it):
For Each cell In Range("A2:A" & LastRow)
Debug.Print "Row: " & cell.Row & " Value: " & cell.Value
Next cellRun the macro and check the Immediate Window. Every cell value scrolls past as the loop processes it. Far faster than inserting a MsgBox that requires you to click OK thousands of times.
In the VBA editor, go to Debug, then Add Watch. Enter your loop variable name and set the watch type to break when the value changes. Execution pauses every time that variable updates, letting you inspect the full state of your procedure at each step.
Almost always a hardcoded range or a LastRow calculation pointing at the wrong column. Verify that the column used for LastRow detection is the one that consistently has data throughout your dataset.
For i = 2 To 10 processes both row 2 and row 10 — the range is inclusive on both ends. If your loop is processing one too few or one too many rows, trace the bounds carefully. When using LastRow dynamically, add a Debug.Print of LastRow before the loop the first time you run new code.
If the loop body never runs, the condition was already false before the loop started. Print the value of your condition variable immediately before the Do While line to confirm what it actually contains versus what you expected.
| Loop / Statement | Best For | Watch Out For |
|---|---|---|
| For Next | Known iterations, row deletion (Step -1) | Use Long not Integer for large row counts |
| For Each | Ranges, sheets, arrays without row tracking | Do not delete rows inside For Each |
| Do While | Unknown iteration count, walking to empty cell | Always ensure condition can become false |
| Do Until | Same as Do While, reads more naturally as "until" | Same infinite loop risk as Do While |
| If Then Else | Two to four outcome branches | More than four branches — switch to Select Case |
| Select Case | Three or more specific values on one variable | Does not work for compound multi-variable conditions |
🧪 TRY IT YOURSELF
Open a blank workbook and add 20 numbers between 1 and 200 in column A, starting from row 2. Mix in values above 150, between 100 and 150, and below 100.
Write a single macro that does all of the following inside one loop:
Cells(Rows.Count, 1).End(xlUp).Row, not a hardcoded numberThis exercise uses For Next with a compound ElseIf block and a counter variable that increments inside the loop. If you are unsure where to start, the invoice flagging example above has almost the same structure.
How to Loop Through Cells in Excel VBA — Step by Step
LastRow = Cells(Rows.Count, 1).End(xlUp).RowRange("A2:A" & LastRow)Next cellIf anything in this lesson around counter variables or data types felt unfamiliar — particularly why Long matters over Integer, or how variables are declared — visit Lesson 32: Introduction to VBA in Excel to reinforce that foundation before going further.
When you are ready to go further, Lesson 34: Advanced Pivot Tables in Excel applies structured data thinking to one of Excel's most powerful reporting tools — a natural next step once you are comfortable automating with loops and conditions.
📚 Further Reading
The official Microsoft documentation on every VBA loop variant, including edge cases and less common loop forms beyond what this lesson covers.
A detailed walkthrough of If Then Else with real spreadsheet scenarios, covering edge cases not addressed in this lesson.
Advertisement-X