Guide

Getting Started With Python Excel AutomationDeep dive

openpyxl vs xlsxwriter vs pandas.ExcelWriter

Understand how pandas.ExcelWriter wraps the openpyxl and xlsxwriter engines, when each one wins, and write the same DataFrame three ways with a clear decision table.

The three names look like competing libraries, but they sit at different layers. pandas.ExcelWriter is not an engine at all — it's a thin wrapper that delegates to one. The real engines are openpyxl and xlsxwriter, and they have opposite strengths: openpyxl can read and write existing files and preserves styles, while xlsxwriter is write-only but faster and richer at formatting and charts. This guide, part of Writing DataFrames to Excel with Pandas, clears up the relationship, gives you a decision table, and writes the same DataFrame three ways.

Prerequisites

Install pandas and both engines:

Bash
pip install pandas openpyxl xlsxwriter

How the pieces relate

df.to_excel(path) and pd.ExcelWriter(path) both produce a file by handing your data to an engine. When you don't name one, pandas picks a default (openpyxl for .xlsx). You choose explicitly with engine=:

  • openpyxl — read + write. Opens an existing .xlsx, edits cells in place, appends sheets, and keeps the styles already in the file. This is the only engine that can modify a workbook you already have.
  • xlsxwriter — write only. It builds a brand-new file fast and exposes the richest formatting, conditional formats, and native charts. It cannot open or read an existing workbook.
  • pandas.ExcelWriter — the wrapper. It exposes mode="a" (append) and a book/sheets handle so you can reach the underlying engine object for formatting.

Decision table

Needopenpyxlxlsxwriterplain to_excel
Read an existing fileYesNon/a (write-only)
Edit a file in placeYesNoNo
Append a sheet (mode="a")YesNoUses openpyxl
Native chartsLimitedYes (strong)Whatever engine you pass
Conditional formattingLimitedYesVia engine
Raw write speed on large filesGoodFastestMatches the engine
Preserve styles in source fileYesn/aOnly via openpyxl
Quick one-off dumpOverkillOverkillBest fit

Write 1: plain to_excel for a quick dump

No engine argument, no formatting — the fastest way to get a DataFrame onto disk:

Python
import pandas as pd

df = pd.DataFrame({
    "Region": ["North", "South", "West"],
    "Revenue": [12000, 9800, 15400],
})
df.to_excel("dump.xlsx", index=False)   # defaults to openpyxl
print("wrote dump.xlsx")

Write 2: xlsxwriter with a cell format

Reach the workbook and worksheet objects through the ExcelWriter, then add a currency format. xlsxwriter shines here because formatting is first-class:

Python
import pandas as pd

df = pd.DataFrame({
    "Region": ["North", "South", "West"],
    "Revenue": [12000, 9800, 15400],
})

with pd.ExcelWriter("styled.xlsx", engine="xlsxwriter") as writer:
    df.to_excel(writer, sheet_name="Report", index=False)
    workbook = writer.book
    worksheet = writer.sheets["Report"]
    money = workbook.add_format({"num_format": "$#,##0", "bold": True})
    worksheet.set_column("B:B", 14, money)   # format the Revenue column

print("wrote styled.xlsx")

Write 3: openpyxl direct, to edit an existing file

This is where openpyxl is the only option: load a file that already exists, change a cell, and save. xlsxwriter physically cannot do this because it never reads:

Python
import pandas as pd
from openpyxl import load_workbook

# Start from a file written earlier
pd.DataFrame({"Region": ["North"], "Revenue": [12000]}).to_excel(
    "ledger.xlsx", index=False
)

wb = load_workbook("ledger.xlsx")   # read the existing workbook
ws = wb.active
ws["B2"].value = 13500              # edit a cell in place
ws["C1"] = "Adjusted"               # add a column header
wb.save("ledger.xlsx")

print("updated:", load_workbook("ledger.xlsx").active["B2"].value)

Appending a sheet needs openpyxl

mode="a" opens the existing file to add to it, so it requires the read-capable engine. Passing engine="xlsxwriter" with mode="a" raises a ValueError:

Python
import pandas as pd

pd.DataFrame({"Region": ["North"], "Revenue": [12000]}).to_excel(
    "book.xlsx", sheet_name="Jan", index=False
)

with pd.ExcelWriter("book.xlsx", engine="openpyxl", mode="a") as writer:
    pd.DataFrame({"Region": ["South"], "Revenue": [9800]}).to_excel(
        writer, sheet_name="Feb", index=False
    )

print(pd.ExcelFile("book.xlsx").sheet_names)

Common pitfalls

SymptomCauseFix
ValueError: Append mode is not supported with xlsxwriterxlsxwriter can't open existing filesUse engine="openpyxl" for mode="a"
FileNotFoundError/empty file when "editing"Tried to load an existing file with xlsxwriterUse load_workbook (openpyxl); xlsxwriter only creates new files
Styles vanish after a pandas round-tripReading to a DataFrame keeps values, not formattingEdit with openpyxl directly instead of read_excel then to_excel
Chart or conditional format ignoredTried to add it through the wrong engineBuild new styled reports with xlsxwriter

The styling-loss trap is the one that bites teams: pd.read_excel() returns only the data, so a read_excel → to_excel cycle silently drops every fill, border, and number format the source had. To keep formatting, never pass through a DataFrame — open the file with openpyxl and modify cells in place.

Performance and scale

For large write-only jobs, xlsxwriter is the fastest and supports constant_memory mode to stream rows without holding the whole sheet in RAM. openpyxl is competitive and adds a write_only=True mode for the same reason, but it pays a cost to preserve existing content. For a quick dump under a few thousand rows, the engine choice is irrelevant — use plain to_excel.

Frequently asked questions

Is pandas.ExcelWriter an engine? No. It's a context-manager wrapper that delegates to openpyxl or xlsxwriter. You pick the real engine with engine=.

Which engine does to_excel use by default? For .xlsx, pandas defaults to openpyxl when it's installed. Name engine="xlsxwriter" explicitly when you want xlsxwriter's formatting.

Can xlsxwriter edit an existing workbook? Never. It is write-only and creates a fresh file each run. Any in-place edit or append must go through openpyxl.

How do I add a chart to a pandas export? Write with engine="xlsxwriter", grab writer.book and writer.sheets[...], then build the chart with xlsxwriter's chart API.

Conclusion

Think in layers: pandas.ExcelWriter is the wrapper, openpyxl and xlsxwriter are the engines. Reach for openpyxl when you must read, edit in place, or append to a file that already exists; reach for xlsxwriter to build new, heavily styled reports and charts from scratch; and use plain to_excel for a quick dump where formatting doesn't matter. The single rule that prevents most surprises: a pandas round-trip keeps data but loses styling, so edit existing files with openpyxl directly.

Where to go next

Return to Writing DataFrames to Excel with Pandas for the full export workflow. To drop the index column cleanly on any of these engines, see Write a Pandas DataFrame to Excel Without the Index. For the openpyxl append pattern in depth, read Append Data to an Existing Excel Sheet with openpyxl. To take styling further, explore Formatting and Charting Excel Reports with Python.