Conversion Workflow

Convert XLSM to XLSX Without Losing Macros

Sponsored
By MacroKit · Updated April 2026 · 10 min read

Here's the uncomfortable truth: you cannot convert XLSM to XLSX and keep macros intact. XLSX is structurally incapable of storing VBA code — it's a format limitation, not a software bug.

But there is a safe workflow. You can preserve all VBA code as exported files, create an XLSX copy for sharing, and keep the original XLSM as your working master. This guide walks you through the full process — manually and via script for bulk operations.

If you used CloudConvert, Zamzar, or any online converter: Your macros are already gone. Online converters don't warn you. They silently drop the vbaProject.bin binary. If you haven't overwritten the original XLSM, you can still recover. See the recovery guide.

Why XLSM → XLSX Always Strips Macros

XLSM stores VBA code in a binary file called vbaProject.bin inside the ZIP archive. XLSX's format specification explicitly prohibits this binary — there's no slot for it. When any tool converts XLSM to XLSX, it must choose:

The only way to "keep" your macros is to archive them separately before the conversion.

The Safe 5-Step Workflow

Step 1 of 5

Audit What Macros Exist

Before touching the file, know what you're working with. Open the VBA Editor (Alt+F11 in Excel) and look at the Project Explorer panel on the left.

Count and name every item under your workbook's project:

  • Modules (Module1, Module2, etc.) — standard VBA code
  • Class Modules (clsInvoice, etc.) — object-oriented VBA
  • Forms (UserForm1, etc.) — dialog boxes
  • ThisWorkbook / Sheet objects — event handlers

Alternatively, use the free Macro Inspector to get a full module list without opening Excel.

Step 2 of 5

Export All VBA Modules

In the VBA Editor Project Explorer, right-click each module/class/form → Export File.

Save to a folder named after the workbook. Use these extensions:

Module typeExtensionWhat it contains
Standard module.basAll Sub and Function procedures
Class module.clsClass definition and methods
UserForm.frmForm layout (+ .frx binary for controls)
ThisWorkbook / Sheet.clsWorkbook and sheet event handlers

Also copy any macros from Sheet code modules (right-click the sheet tab → View Code). These are often missed.

Step 3 of 5

Create the XLSX Copy

With the XLSM open in Excel:

  1. File → Save As
  2. Change "Save as type" to Excel Workbook (*.xlsx)
  3. Save to a different location or add _sharing to the filename
  4. Click Yes when Excel warns that macros will be removed
Never click Save over your XLSM. Always use Save As to a new filename. One misclick and the master workbook is gone.
Step 4 of 5

Archive the Original XLSM

Move the original XLSM (not the new XLSX) to a dated archive folder:

archive/
  2026-04-15_report-automation-v3.xlsm
  modules/
    Module1.bas
    DataProcessor.cls
    InputForm.frm
    InputForm.frx

Zip it. Add a README noting what the macros do and when they were last verified. This is your recovery kit if the XLSX version needs macros re-enabled later.

Step 5 of 5

Verify the XLSX Output

Open the new XLSX and verify:

  • All worksheets are present with correct data
  • Formulas that relied on VBA-defined functions now show #NAME? — this is expected
  • Formatting, tables, and charts transferred correctly
  • The file size is smaller than the XLSM (expected — vbaProject.bin removed)

Batch Conversion: Multiple XLSM Files at Once

If you have dozens of XLSM files to convert, doing this manually for each is impractical. The Python script below automates the export + conversion:

"""
batch_xlsm_to_xlsx.py — Safe XLSM → XLSX converter with module archiving
Requires: openpyxl, zipfile (standard library)
Usage: python batch_xlsm_to_xlsx.py /path/to/xlsm/files
"""
import os, sys, shutil, zipfile
from pathlib import Path

def extract_vba_project(xlsm_path, archive_dir):
    """Extract vbaProject.bin from XLSM for archiving."""
    archive_dir.mkdir(parents=True, exist_ok=True)
    with zipfile.ZipFile(xlsm_path, 'r') as z:
        entries = z.namelist()
        vba = [e for e in entries if 'vbaProject' in e]
        for entry in vba:
            z.extract(entry, archive_dir)
            print(f"  Archived: {entry}")
    return len(vba)

def convert_xlsm_to_xlsx(xlsm_path, output_dir, archive_base):
    """Convert a single XLSM to XLSX with VBA archiving."""
    p = Path(xlsm_path)
    out_name = p.stem + '.xlsx'
    out_path = Path(output_dir) / out_name
    archive_dir = Path(archive_base) / p.stem

    # Archive VBA binary
    vba_count = extract_vba_project(p, archive_dir)
    print(f"  Archived {vba_count} VBA component(s)")

    # Copy XLSM as ZIP, rebuild without vbaProject.bin
    with zipfile.ZipFile(p, 'r') as src_zip:
        with zipfile.ZipFile(out_path, 'w', zipfile.ZIP_DEFLATED) as dst_zip:
            for item in src_zip.infolist():
                if 'vbaProject' not in item.filename:
                    dst_zip.writestr(item, src_zip.read(item.filename))

    # Fix Content_Types to remove vba reference
    # (simplified — for production, use openpyxl to re-save properly)
    print(f"  Converted: {out_name}")
    return out_path

if __name__ == '__main__':
    src_dir = sys.argv[1] if len(sys.argv) > 1 else '.'
    output_dir = Path(src_dir) / 'xlsx_output'
    archive_dir = Path(src_dir) / 'vba_archive'
    output_dir.mkdir(exist_ok=True)

    files = list(Path(src_dir).glob('*.xlsm'))
    print(f"Found {len(files)} XLSM files to convert")

    for f in files:
        print(f"Processing: {f.name}")
        convert_xlsm_to_xlsx(f, output_dir, archive_dir)

    print(f"\nDone. XLSX files in: {output_dir}")
    print(f"VBA archives in: {archive_dir}")
Note: The script above archives the vbaProject.bin binary. To get readable VBA source code from this binary, you need to import it back into Excel's VBA editor. The MacroKit workflow guide includes a more complete script using xlrd and oletools to extract human-readable module source from the binary.

Re-importing Macros Later

If you later need the macros back in a file, here's how to re-import from your .bas / .cls exports:

  1. Open the target workbook
  2. Save as XLSM (it must be XLSM to hold macros)
  3. Open VBA Editor (Alt+F11)
  4. File → Import File → select your .bas / .cls files
  5. For UserForms: import the .frm file (the .frx must be in the same folder)
  6. Test all macros — some may need worksheet or workbook references updated

Common Mistakes

MistakeConsequencePrevention
Using Save (not Save As) when convertingOverwrites the XLSM masterAlways Save As to a new filename/location
Skipping sheet event handlersWorksheet_Change, Workbook_Open events lostExport ThisWorkbook and each sheet module explicitly
Forgetting .frx files with .frmUserForms won't import correctlyExport both .frm and .frx; they're always paired
Sending XLSX to users who expect automationVBA-driven formulas return #NAME?Document which features require the macro version

Need to convert dozens of XLSM files?

MacroKit includes a complete batch conversion workflow with automated VBA export, conversion scripts, and a post-conversion validation checklist. One purchase covers every file, forever.

Get MacroKit — $9

Related Guides