Expert guide

Excel VBA: Loop Through All Files in a Folder

Open, read, change or combine every workbook in a folder, safely and quickly

Short answer: get the first file with Dir(folder & "*.xlsx"), loop while the result is not empty, open each workbook, do the work, close it, then call Dir() for the next name. Skip ~$ lock files, turn off ScreenUpdating and automatic calculation for speed, and restore settings in an error handler. For subfolders use FileSystemObject recursion. If you only need to combine data, Power Query may be simpler. We build these as part of Excel development services.

Branch reports, monthly exports, supplier price files, timesheets: many teams receive dozens of workbooks with the same layout and need something done to each one. Opening them by hand takes an afternoon. A VBA folder loop does it in a minute, and the pattern is the same whether you are copying data out, fixing a formula in each file, or saving PDFs.

The Basic Dir Loop

Option Explicit

Sub ProcessFolder()
    Dim folder As String, f As String, wb As Workbook

    folder = "C:\Reports\Branches\"
    If Right$(folder, 1) <> "\" Then folder = folder & "\"

    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.Calculation = xlCalculationManual
    On Error GoTo CleanUp

    f = Dir(folder & "*.xls*")
    Do While Len(f) > 0
        If Left$(f, 2) <> "~$" Then                      ' skip lock files
            Set wb = Workbooks.Open(folder & f, ReadOnly:=True, UpdateLinks:=0)
            ' --- do the work here ---
            Debug.Print f, wb.Worksheets(1).Range("B2").Value
            wb.Close SaveChanges:=False
        End If
        f = Dir()                                        ' next file
    Loop

CleanUp:
    Application.Calculation = xlCalculationAutomatic
    Application.EnableEvents = True
    Application.ScreenUpdating = True
    If Err.Number <> 0 Then MsgBox "Stopped at " & f & ": " & Err.Description
End Sub

Opening read-only with UpdateLinks:=0 avoids prompts about links and protects the source files. Change ReadOnly to False and SaveChanges to True only when the macro is meant to modify the files.

Let the User Pick the Folder

Function PickFolder() As String
    With Application.FileDialog(msoFileDialogFolderPicker)
        .Title = "Choose the folder to process"
        If .Show = -1 Then PickFolder = .SelectedItems(1) & "\"
    End With
End Function

Combine Data From Every File Into One Sheet

Sub CombineBranchFiles()
    Dim folder As String, f As String, wb As Workbook
    Dim src As Worksheet, dest As Worksheet, data As Variant
    Dim lastRow As Long, nextRow As Long

    folder = PickFolder(): If Len(folder) = 0 Then Exit Sub
    Set dest = ThisWorkbook.Worksheets("Combined")
    dest.Cells.ClearContents
    dest.Range("A1:F1").Value = Array("Source", "Date", "Customer", "Product", "Qty", "Amount")
    nextRow = 2

    Application.ScreenUpdating = False
    f = Dir(folder & "*.xlsx")
    Do While Len(f) > 0
        If Left$(f, 2) <> "~$" Then
            Set wb = Workbooks.Open(folder & f, ReadOnly:=True)
            Set src = wb.Worksheets("Sales")
            lastRow = src.Cells(src.Rows.Count, "A").End(xlUp).Row
            If lastRow >= 2 Then
                data = src.Range("A2:E" & lastRow).Value          ' read once into an array
                dest.Cells(nextRow, 2).Resize(UBound(data), 5).Value = data
                dest.Cells(nextRow, 1).Resize(UBound(data), 1).Value = f
                nextRow = nextRow + UBound(data)
            End If
            wb.Close False
        End If
        f = Dir()
    Loop
    Application.ScreenUpdating = True
    MsgBox nextRow - 2 & " rows combined.", vbInformation
End Sub

Reading the whole range into an array and writing it in one operation is dramatically faster than copying cell by cell. Adding the source file name to each row makes it easy to trace any figure back to its file.

Include Subfolders With FileSystemObject

Sub ProcessTree(ByVal folderPath As String)
    Dim fso As Object, fld As Object, sub_ As Object, fil As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set fld = fso.GetFolder(folderPath)

    For Each fil In fld.Files
        If LCase$(fso.GetExtensionName(fil.Name)) Like "xls*" _
           And Left$(fil.Name, 2) <> "~$" Then
            ProcessOneFile fil.Path                     ' your per-file routine
        End If
    Next fil

    For Each sub_ In fld.SubFolders
        ProcessTree sub_.Path                           ' recurse
    Next sub_
End Sub

Common Tasks Inside the Loop

TaskKey line
Save each file as PDFwb.ExportAsFixedFormat xlTypePDF, folder & "PDF\" & name & ".pdf"
Fix a formula in every filewb.Worksheets("Summary").Range("D10").Formula = "=SUM(D2:D9)"
Refresh and savewb.RefreshAll: wb.Save
Convert .xls to .xlsxwb.SaveAs newPath, FileFormat:=xlOpenXMLWorkbook
Collect a single value per fileWrite file name and value to the next row of a summary sheet

Pitfalls

  • Nested Dir calls reset the outer loop. Collect names first if you need Dir inside the loop.

  • Files open in another user's Excel can be opened read-only only; check wb.ReadOnly before saving.

  • Sheet names differ between files: test with a function that checks the sheet exists.

  • Workbooks with Workbook_Open macros run them when opened; EnableEvents = False prevents that.

  • Very large folders: log progress to the status bar and a log sheet so you know where it stopped.

VBA Loop or Power Query?

If all you need is to stack data from many files into one table that refreshes, Power Query's From Folder connector is often the better tool, needs no macros, and handles new files automatically. The trade-offs are compared in Power Query vs VBA. For multi-source consolidation built properly once, see Excel data consolidation.

FAQ

VBA folder loop FAQ

Dir, subfolders, speed and safety

Use Dir(folder & "*.xls*") to get the first file name, then call Dir() with no arguments in a loop until it returns an empty string. For each file, open it with Workbooks.Open, read or change what you need, then close it with or without saving.

Dir does not recurse. Use the FileSystemObject (Scripting.FileSystemObject) and a recursive procedure that processes each folder's Files collection and then calls itself for each folder in SubFolders.

Calling Dir with a new path inside the loop resets the sequence, so a nested Dir call breaks the outer loop. Collect file names into an array or collection first, then process them. Also skip temporary files that start with ~$, which appear when someone has a workbook open.

Turn off ScreenUpdating, set Calculation to manual, disable events while opening files, open workbooks read-only when you only need data, and read ranges into arrays rather than cell by cell. Restore the settings in an error handler.

If the goal is to combine data from many files into one table, Power Query's From Folder connector is usually simpler and refreshable without macros. Use VBA when you need to change the files themselves, save copies, print, email, or interact with other applications.