An Excel VBA progress bar shows how much of a long-running macro has finished. For a visual bar, create a UserForm with a track and a fill label, then update the fill as your code processes items; for a simpler display, use Excel’s status bar with Application.StatusBar.
What an Excel VBA progress bar does
A progress indicator gives users feedback while a macro runs. Without one, Excel may appear to have stopped responding even when VBA is still working. A bar can show an approximate completion percentage, while nearby text can say what the macro is processing, such as “Processing item 75 of 500.”
VBA does not provide a ready-made progress-bar control for ordinary worksheets. The common visual approach is to build a small UserForm and change the width of a label inside a track. The status bar is a built-in alternative that needs no form design. Neither approach makes the macro itself run faster; it communicates progress and, when used with care, gives Excel opportunities to refresh its interface.
Choose a UserForm or the status bar
Choose the display that fits the task rather than adding a visual form automatically. A UserForm is prominent and can include a message or a Cancel button, but it requires a little setup and screen space. The status bar is unobtrusive and quick to implement, though users can miss it and it offers less room for detail.
- Use a UserForm when the task takes long enough that users need a visible, dedicated indicator, or when you want to show descriptive text.
- Use the status bar for a lightweight indicator during a batch operation when a percentage or short message is enough.
- Skip frequent updates for very short macros. Updating the interface too often can add overhead without giving users useful information.
Progress is meaningful only when the macro can estimate the total work. A loop over a known number of rows is straightforward. A task that discovers more work as it runs may need an indeterminate message such as “Working…” rather than a percentage that repeatedly changes its denominator.
Build the visual bar with a UserForm
In Excel for Microsoft 365 on Windows, press Alt+F11 to open the Visual Basic Editor. Choose Insert > UserForm, then set the form’s (Name) property to frmProgress. Add a Frame named fraTrack, a Label named lblFill inside the frame, and a separate Label named lblText for the percentage and message. Set the fill label’s full width in the designer, place it at the left edge of the track, and give it a contrasting BackColor. The example records that designed width, then reduces it to zero.
Double-click the form and put this code in the form’s code module. The percentage is bounded between zero and one, so an accidental value outside the expected range cannot make the fill exceed the track. The text label displays both the percentage and the supplied message.
Option Explicit
Private mBarWidth As Single
Private Sub UserForm_Initialize()
' Keep the designed width as the bar's maximum width.
mBarWidth = lblFill.Width
lblFill.Width = 0
lblText.Caption = "Starting..."
End Sub
Public Sub UpdateProgress(ByVal completed As Long, _
ByVal total As Long, _
Optional ByVal message As String = "")
Dim fraction As Double
If total <= 0 Then
fraction = 0
Else
fraction = CDbl(completed) / CDbl(total)
End If
If fraction < 0 Then fraction = 0
If fraction > 1 Then fraction = 1
lblFill.Width = mBarWidth * fraction
lblText.Caption = Format$(fraction, "0%") & " " & message
End Sub
Call the form from a macro
This example processes the nonblank range of rows identified by column A on a worksheet named Data. It writes uppercase versions of those values to column B. The work is deliberately simple; replace the example operation with the task your macro actually needs to perform. Before running any macro, save a copy of the workbook: VBA changes cannot be undone with Ctrl+Z.
Option Explicit
Public Sub ProcessRowsWithProgress()
Dim ws As Worksheet
Dim progress As frmProgress
Dim lastRow As Long
Dim rowNum As Long
Dim total As Long
Dim errorText As String
On Error GoTo CleanFail
Set ws = ThisWorkbook.Worksheets("Data")
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
If lastRow < 2 Then
MsgBox "There are no data rows to process.", vbInformation
Exit Sub
End If
total = lastRow - 1
Set progress = New frmProgress
progress.Show vbModeless
progress.UpdateProgress 0, total, "Starting..."
DoEvents
For rowNum = 2 To lastRow
' Example task: copy column A to column B in uppercase.
ws.Cells(rowNum, "B").Value = _
UCase$(CStr(ws.Cells(rowNum, "A").Value))
If (rowNum - 1) Mod 25 = 0 Or rowNum = lastRow Then
progress.UpdateProgress rowNum - 1, total, _
"Processing item " & (rowNum - 1) & " of " & total
DoEvents
End If
Next rowNum
progress.UpdateProgress total, total, "Complete"
DoEvents
Unload progress
Set progress = Nothing
MsgBox "Processing is complete.", vbInformation
Exit Sub
CleanFail:
errorText = Err.Description
On Error Resume Next
If Not progress Is Nothing Then Unload progress
On Error GoTo 0
MsgBox "The macro stopped: " & errorText, vbExclamation
End Sub
Put the main procedure in a standard module, not in the UserForm’s code module. The form must exist under the name used in the declaration. The sample assumes the first row is a header and that column A identifies the last row to process. If your data has a different layout, adjust the worksheet name, columns, and starting row together.
Update at sensible intervals
The sample refreshes after every 25 items and on the final item. This is a practical compromise for many row-based tasks: updating on every iteration can slow a macro, particularly when there are thousands of rows, while updating only at the end defeats the purpose of a progress indicator. The right interval depends on how expensive each unit of work is. For a slow operation per item, updating every item may be reasonable; for a fast loop, update every several dozen or several hundred items.
DoEvents lets Excel process pending interface events, which can allow the modeless form to repaint. It also lets other events be handled while the procedure is running, so it should not be inserted indiscriminately into code that must not be interrupted or re-entered. Keep the work between updates bounded, and test the macro with realistic data. A progress display is an estimate of completed units, not a guarantee of remaining time: some rows or operations may take much longer than others.
Use the status bar instead
To avoid building a form, write a message to Excel’s status bar. The following standalone example uses the same worksheet and row assumptions. It stores the existing status-bar value and restores it on both normal completion and error. As with the form example, save a copy of the workbook before running it.
Option Explicit
Public Sub ProcessRowsWithStatusBar()
Dim ws As Worksheet
Dim lastRow As Long
Dim rowNum As Long
Dim total As Long
Dim previousStatus As Variant
Dim errorText As String
previousStatus = Application.StatusBar
On Error GoTo CleanFail
Set ws = ThisWorkbook.Worksheets("Data")
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
If lastRow < 2 Then GoTo CleanUp
total = lastRow - 1
For rowNum = 2 To lastRow
' Example task: copy column A to column B in uppercase.
ws.Cells(rowNum, "B").Value = _
UCase$(CStr(ws.Cells(rowNum, "A").Value))
If (rowNum - 1) Mod 25 = 0 Or rowNum = lastRow Then
Application.StatusBar = "Processing " & _
Format$((rowNum - 1) / total, "0%") & _
" - item " & (rowNum - 1) & " of " & total
DoEvents
End If
Next rowNum
CleanUp:
Application.StatusBar = previousStatus
Exit Sub
CleanFail:
errorText = Err.Description
Application.StatusBar = previousStatus
MsgBox "The macro stopped: " & errorText, vbExclamation
End Sub
Setting Application.StatusBar to a string temporarily replaces Excel’s normal status-bar message. Restoring the value saved at the start avoids leaving a stale progress message behind. This example exits quietly if there are no data rows; change that behavior if you prefer to display a completion or no-data message.
Common problems and fixes
- The bar does not move: Check that
UpdateProgressis called inside the loop, that the form and controls have the exact names used in the code, and that updates are not so infrequent that the macro finishes between them. - The form appears frozen: A modeless form still needs Excel to process interface events. Call
DoEventsat appropriate intervals, and avoid a long, uninterrupted block of work between calls. - The percentage is wrong: Confirm that
totalcounts the same units ascompleted. In the example, the header is excluded, so the first data row is item 1, not worksheet row 2. - The bar jumps or finishes early: A fixed denominator works only if the total is known. If the macro skips rows, counts multiple phases, or adds work dynamically, calculate progress using the actual completed units and a consistent total.
- The status bar stays changed: Make sure the procedure restores
Application.StatusBaron every exit path, including error handling. Avoid using an abruptEndstatement, which can bypass normal cleanup.
Safety, compatibility, and alternatives
VBA macros run in desktop Excel; Excel for the web cannot run VBA macros. The code here targets Excel for Microsoft 365 on Windows. Excel for Mac also supports VBA and UserForms, but the Visual Basic Editor shortcuts and interface can differ; use the Developer tab or Excel’s macro tools to open the editor if the Windows Alt+F11 shortcut does not apply. Form layout and control rendering can also vary across systems, so check the form on the computers where it will be used.
Only enable macros in workbooks you trust. A progress bar does not make a macro safer, and code that edits cells can overwrite data. Save a separate copy before testing or running it, especially if the procedure changes or deletes workbook content. For a process that needs no custom VBA, Excel’s built-in calculation indicators or a worksheet-based status message may be enough; for more complex automation, consider logging completed stages to a worksheet so users can inspect what happened after the macro finishes.
Frequently Asked Questions
Can I make a progress bar without a UserForm?
Yes. Set Application.StatusBar to a short message or percentage and restore its previous value when the macro exits. This is simpler, but it is less prominent and offers less design flexibility than a UserForm.
Why does Excel say “Not Responding” while my macro runs?
VBA usually runs on Excel’s main interface thread. A long stretch of work without yielding can prevent the window from repainting. Periodic, carefully placed DoEvents calls can help the interface update, but they do not make the computation run in parallel.
Can users cancel a macro from the progress bar?
Yes, but cancellation needs deliberate design. You can add a Cancel button to the UserForm, set a public cancellation flag in its click event, and have the main procedure check that flag between work units. Do not assume that closing the form automatically stops the running procedure safely.
Can a progress bar show an accurate time remaining?
Not reliably in every macro. A time estimate can be calculated from elapsed time and the fraction completed, but it assumes the remaining work will take roughly as long per unit as the work already done. Uneven tasks make the estimate fluctuate or mislead.
Will this VBA progress bar work in Excel for the web?
No. Excel for the web does not run VBA macros. A workbook that depends on this code needs to be opened in desktop Excel; web-based automation requires a different approach, such as Office Scripts where supported.
Related
- Circular References in Excel: How to Find and Trace Them
- Circular References in Excel: How to Find and Trace Them
- How to Turn On Circular Reference Iteration in Excel
Try it: the Column Letter to Number: aA is 27, XFD is 16,384. Converts both ways.
More in VBA & macros
- VBA ArrayList: How to Create and Use It in Excel VBAIn VBA, an ArrayList is a resizable, zero-based list that you can create on Windows with CreateObject("System.Collections.ArrayList"). It…
- Excel Macro Error? Common Causes and FixesAn Excel macro error usually means VBA is blocked, the code cannot find an expected workbook, worksheet, or…
- Set Image Height in Excel VBA Using InchesTo set an image to a height measured in inches, assign its VBA Height property the result of…
- Print GMT Time Using Excel VBATo print the current GMT time with Excel VBA on Windows, call the Windows GetSystemTime API and send…
- Excel VBA New Line: Add Line Breaks in Cells and TextIn Excel VBA, join text with vbCrLf or vbNewLine to put it on separate lines in a message…
- Excel UserForm Combo Box: Add, Populate, and Use ItAn Excel UserForm combo box is a VBA control that lets someone choose an item from a drop-down…
Sources: Function behaviour and menu paths: Microsoft Support documentation for Excel. Method & sources
Spotted an error in this guide? Let us know.