Skip to footer content
COMPARE TO OTHER COMPONENTS

ASP.NET Export to Excel: IronXL vs ClosedXML vs EPPlus Compared

Exporting data to Excel is a standard requirement in ASP.NET Core web applications. Whether you are generating sales reports, enabling users to download GridView contents, or producing CSV files from database queries, the approach you choose determines whether the resulting file opens cleanly in Microsoft Excel or triggers format warnings. This article compares the most common Excel export methods available to C# developers -- traditional HTML-based streaming, and modern library solutions including IronXL, ClosedXML, and EPPlus -- so you can select the right tool for your project.

Start your free trial to see how IronXL handles Excel file generation in ASP.NET Core applications.

What Are the Common Methods to Export Data to Excel in ASP.NET Core?

ASP.NET Core developers have several paths available when adding Excel export functionality. Each approach differs in the quality of the output file, the effort required to implement it, and the licensing implications for commercial projects.

Traditional MIME type streaming is the oldest technique. The server sets the response Content-Type header to application/vnd.ms-excel and writes an HTML table to the output stream. The browser interprets this as an Excel download, but the file contains HTML markup rather than genuine spreadsheet data. Microsoft Excel detects the mismatch and displays a format warning before opening the file. This method cannot support formulas, typed columns, or proper cell formatting.

Library-based solutions add a NuGet package that constructs genuine XLSX files using the Open XML format defined by Microsoft. Options include IronXL, ClosedXML, and EPPlus. All three produce valid Excel files that open without warnings, support cell-level formatting, and work with .NET Core. The official .NET documentation for file downloads covers the underlying ASP.NET Core mechanisms used by all these approaches.

The table below summarises key differences:

Excel export library comparison: feature matrix
Feature MIME Type / HTML ClosedXML EPPlus IronXL
Genuine XLSX output No Yes Yes Yes
CSV file support Manual Limited Limited Native
Opens without Excel warning No Yes Yes Yes
Formula support No Yes Yes Yes
JSON and XML export No No No Yes
Commercial license N/A MIT Polyform Commercial
.NET Core support Yes Yes Yes Yes

How Do You Install IronXL in an ASP.NET Core Project?

Before writing any export code, add IronXL to your project through the NuGet Package Manager or the .NET CLI. IronXL has no dependency on Microsoft Office or COM interop, so installation is straightforward on any operating system supported by .NET.

dotnet add package IronXL.Excel
dotnet add package IronXL.Excel
SHELL

Alternatively, use the Package Manager Console in Visual Studio:

Install-Package IronXL.Excel
Install-Package IronXL.Excel
SHELL

Once the package is installed, add using IronXL; to any controller or service class that needs to generate Excel files. No additional configuration is required for basic export scenarios. For licensing and deployment options, visit the IronXL licensing page.

How Does the Traditional GridView Export Approach Work?

In legacy WebForms and some older MVC patterns, developers exported GridView data by rendering it as HTML and streaming it to the browser with a misleading Content-Type header. The application calls Response.AddHeader to set the filename and writes the HTML output directly.

// Traditional approach -- exports HTML disguised as Excel
public void ExportToExcel(object sender, EventArgs e)
{
    Response.Clear();
    Response.Buffer = true;
    Response.ContentType = "application/vnd.ms-excel";
    Response.AddHeader("content-disposition", "attachment;filename=Report.xls");
    StringWriter stringWriter = new StringWriter();
    HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter);
    // Render grid content as HTML
    DataGrid1.RenderControl(htmlTextWriter);
    Response.Write(stringWriter.ToString());
    Response.End();
}

public override void VerifyRenderingInServerForm(Control control)
{
    // Required to prevent server form rendering errors
}
// Traditional approach -- exports HTML disguised as Excel
public void ExportToExcel(object sender, EventArgs e)
{
    Response.Clear();
    Response.Buffer = true;
    Response.ContentType = "application/vnd.ms-excel";
    Response.AddHeader("content-disposition", "attachment;filename=Report.xls");
    StringWriter stringWriter = new StringWriter();
    HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter);
    // Render grid content as HTML
    DataGrid1.RenderControl(htmlTextWriter);
    Response.Write(stringWriter.ToString());
    Response.End();
}

public override void VerifyRenderingInServerForm(Control control)
{
    // Required to prevent server form rendering errors
}
$vbLabelText   $csharpLabel

Legacy Output

ASP.NET Export to Excel: Best Tool Comparison for C# Developers: Image 1 - Legacy Excel export output

This method requires overriding VerifyRenderingInServerForm to bypass server-side validation. The generated file contains HTML rather than genuine spreadsheet data, so Microsoft Excel displays a format warning when the user opens it. The output cannot support worksheet formulas, typed data columns, or cell-level formatting. For any new ASP.NET Core development, this pattern should be avoided in favour of a proper Excel library.

How Does IronXL Simplify Excel File Generation in ASP.NET Core?

IronXL provides an API for creating genuine XLSX files without requiring Microsoft Office or COM interop. The library constructs workbook objects entirely in managed code, so it runs on Linux, macOS, and Windows without additional dependencies.

The following example creates a workbook, populates a worksheet with sales data, applies bold formatting to the header row, and streams the file to the browser:

using IronXL;
using Microsoft.AspNetCore.Mvc;

public class ExportController : Controller
{
    [HttpPost]
    public IActionResult ExportReport()
    {
        // Create workbook and worksheet
        WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
        WorkSheet worksheet = workbook.CreateWorkSheet("Sales Data");

        // Add header row
        worksheet["A1"].Value = "Product";
        worksheet["B1"].Value = "Quantity";
        worksheet["C1"].Value = "Revenue";

        // Populate data rows
        worksheet["A2"].Value = "Widget A";
        worksheet["B2"].Value = 150;
        worksheet["C2"].Value = 4500.00;
        worksheet["A3"].Value = "Widget B";
        worksheet["B3"].Value = 230;
        worksheet["C3"].Value = 6900.00;

        // Apply bold formatting to headers
        var headerRange = worksheet["A1:C1"];
        headerRange.Style.Font.Bold = true;

        // Stream file to browser
        byte[] fileBytes = workbook.ToByteArray();
        string filename = $"SalesReport_{DateTime.Now:yyyyMMdd}.xlsx";
        return File(fileBytes,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            filename);
    }
}
using IronXL;
using Microsoft.AspNetCore.Mvc;

public class ExportController : Controller
{
    [HttpPost]
    public IActionResult ExportReport()
    {
        // Create workbook and worksheet
        WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
        WorkSheet worksheet = workbook.CreateWorkSheet("Sales Data");

        // Add header row
        worksheet["A1"].Value = "Product";
        worksheet["B1"].Value = "Quantity";
        worksheet["C1"].Value = "Revenue";

        // Populate data rows
        worksheet["A2"].Value = "Widget A";
        worksheet["B2"].Value = 150;
        worksheet["C2"].Value = 4500.00;
        worksheet["A3"].Value = "Widget B";
        worksheet["B3"].Value = 230;
        worksheet["C3"].Value = 6900.00;

        // Apply bold formatting to headers
        var headerRange = worksheet["A1:C1"];
        headerRange.Style.Font.Bold = true;

        // Stream file to browser
        byte[] fileBytes = workbook.ToByteArray();
        string filename = $"SalesReport_{DateTime.Now:yyyyMMdd}.xlsx";
        return File(fileBytes,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            filename);
    }
}
$vbLabelText   $csharpLabel

IronXL Output

ASP.NET Export to Excel: Best Tool Comparison for C# Developers: Image 2 - IronXL export to Excel output

WorkBook.Create constructs a new Excel document in memory. CreateWorkSheet adds a named tab that users see at the bottom of the Excel window. Cell values are assigned using Excel-style range notation ("A1", "B2"), which most C# developers find readable and maintainable.

The ToByteArray() method converts the finished workbook to a byte array, which the ASP.NET Core File() response method streams directly to the user's browser with the correct MIME type and content disposition headers. The downloaded file opens in Excel without any format warnings.

IronXL also supports exporting to CSV format through the SaveAsCsv method:

// Export as CSV instead of XLSX
workbook.SaveAsCsv("output.csv");
// Export as CSV instead of XLSX
workbook.SaveAsCsv("output.csv");
$vbLabelText   $csharpLabel

For scenarios where you need to export worksheet data as JSON or XML, IronXL provides SaveAsJson and SaveAsXml methods -- capabilities not available in ClosedXML or EPPlus. You can find additional patterns in the IronXL code examples and API reference.

How Does IronXL Handle DataTable and Database Integration?

Many ASP.NET Core applications fetch data from SQL Server or another relational database before exporting it. IronXL provides first-class support for loading a DataTable directly into a worksheet, eliminating the need to iterate rows manually.

The example below queries a database using ADO.NET and populates an Excel worksheet from the resulting DataTable:

using IronXL;
using System.Data;
using System.Data.SqlClient;
using Microsoft.AspNetCore.Mvc;

public class ReportController : Controller
{
    private readonly string _connectionString;

    public ReportController(IConfiguration config)
    {
        _connectionString = config.GetConnectionString("DefaultConnection");
    }

    [HttpGet]
    public IActionResult DownloadReport()
    {
        DataTable table = new DataTable();

        using (SqlConnection connection = new SqlConnection(_connectionString))
        {
            string query = "SELECT OrderId, CustomerName, Total, OrderDate FROM Orders";
            using SqlDataAdapter adapter = new SqlDataAdapter(query, connection);
            adapter.Fill(table);
        }

        WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
        WorkSheet worksheet = workbook.CreateWorkSheet("Orders");

        // Write column headers from DataTable schema
        for (int col = 0; col < table.Columns.Count; col++)
        {
            worksheet[0, col].Value = table.Columns[col].ColumnName;
        }

        // Write data rows
        for (int row = 0; row < table.Rows.Count; row++)
        {
            for (int col = 0; col < table.Columns.Count; col++)
            {
                worksheet[row + 1, col].Value = table.Rows[row][col].ToString();
            }
        }

        byte[] fileBytes = workbook.ToByteArray();
        return File(fileBytes,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "Orders.xlsx");
    }
}
using IronXL;
using System.Data;
using System.Data.SqlClient;
using Microsoft.AspNetCore.Mvc;

public class ReportController : Controller
{
    private readonly string _connectionString;

    public ReportController(IConfiguration config)
    {
        _connectionString = config.GetConnectionString("DefaultConnection");
    }

    [HttpGet]
    public IActionResult DownloadReport()
    {
        DataTable table = new DataTable();

        using (SqlConnection connection = new SqlConnection(_connectionString))
        {
            string query = "SELECT OrderId, CustomerName, Total, OrderDate FROM Orders";
            using SqlDataAdapter adapter = new SqlDataAdapter(query, connection);
            adapter.Fill(table);
        }

        WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
        WorkSheet worksheet = workbook.CreateWorkSheet("Orders");

        // Write column headers from DataTable schema
        for (int col = 0; col < table.Columns.Count; col++)
        {
            worksheet[0, col].Value = table.Columns[col].ColumnName;
        }

        // Write data rows
        for (int row = 0; row < table.Rows.Count; row++)
        {
            for (int col = 0; col < table.Columns.Count; col++)
            {
                worksheet[row + 1, col].Value = table.Rows[row][col].ToString();
            }
        }

        byte[] fileBytes = workbook.ToByteArray();
        return File(fileBytes,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "Orders.xlsx");
    }
}
$vbLabelText   $csharpLabel

This pattern keeps the controller lean and testable. The database query and workbook construction are clearly separated, making it straightforward to swap out the data source or add additional worksheets for summary data. For guidance on reading existing Excel files into a DataTable, see the IronXL how-to guide on reading Excel files.

How Do ClosedXML and EPPlus Compare?

ClosedXML wraps Microsoft's Open XML SDK with an accessible API. Install it from NuGet:

Install-Package ClosedXML
Install-Package ClosedXML
SHELL

The following example shows a typical ClosedXML export action:

using ClosedXML.Excel;
using Microsoft.AspNetCore.Mvc;

public class ExportController : Controller
{
    [HttpGet]
    public IActionResult ExportWithClosedXML()
    {
        using var workbook = new XLWorkbook();
        var worksheet = workbook.AddWorksheet("Data");
        worksheet.Cell(1, 1).Value = "Name";
        worksheet.Cell(1, 2).Value = "Amount";
        worksheet.Cell(2, 1).Value = "Alpha";
        worksheet.Cell(2, 2).Value = 1200;

        using var stream = new MemoryStream();
        workbook.SaveAs(stream);
        return File(stream.ToArray(),
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "export.xlsx");
    }
}
using ClosedXML.Excel;
using Microsoft.AspNetCore.Mvc;

public class ExportController : Controller
{
    [HttpGet]
    public IActionResult ExportWithClosedXML()
    {
        using var workbook = new XLWorkbook();
        var worksheet = workbook.AddWorksheet("Data");
        worksheet.Cell(1, 1).Value = "Name";
        worksheet.Cell(1, 2).Value = "Amount";
        worksheet.Cell(2, 1).Value = "Alpha";
        worksheet.Cell(2, 2).Value = 1200;

        using var stream = new MemoryStream();
        workbook.SaveAs(stream);
        return File(stream.ToArray(),
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "export.xlsx");
    }
}
$vbLabelText   $csharpLabel

ClosedXML Output

ASP.NET Export to Excel: Best Tool Comparison for C# Developers: Image 3 - ClosedXML export to Excel output

ClosedXML uses integer-based cell addressing (Cell(row, col)) rather than the range-string notation used by IronXL. Both approaches produce valid XLSX files. ClosedXML is released under the MIT licence, making it suitable for open-source projects without commercial licensing concerns. The ClosedXML GitHub repository provides issue tracking and community-contributed examples.

EPPlus offers similar functionality under its Polyform Non-Commercial licence for non-commercial use, with a commercial licence required for production deployments. Consult the EPPlus licence overview before using it in a commercial product. EPPlus and ClosedXML both produce valid Excel files but neither offers native CSV, JSON, or XML export in the way IronXL does.

The table below compares specific capabilities relevant to production ASP.NET Core applications:

Detailed capability comparison for production ASP.NET Core projects
Capability IronXL ClosedXML EPPlus
CSV, JSON, and XML export Native Not available Not available
DataTable integration Yes Yes Yes
Technical support Included with licence Community only Paid tier
Microsoft Office dependency None None None
Cross-platform (.NET on Linux) Yes Yes Yes
Licence type Commercial MIT Polyform / Commercial

How Do You Choose the Right Library for Your Project?

Selecting the correct Excel export library depends on three factors: the output formats required, the licensing constraints of your project, and whether you need professional support.

Choose IronXL when your application needs multi-format output (XLSX, CSV, JSON, XML), when you are building a commercial product that requires a guaranteed support channel, or when you need advanced Excel features such as formula evaluation, cell styling, or chart generation. IronXL's documentation covers every API surface in detail, and the support team responds to licence holders directly. Review the IronXL blog for additional patterns and tutorials.

Choose ClosedXML when your project is open-source or non-commercial and requires only XLSX output. The MIT licence imposes no restrictions on redistribution, and the API is well-documented through community resources.

Choose EPPlus when an existing codebase already uses EPPlus and migrating to another library would cost more than purchasing a commercial licence.

For teams evaluating IronXL against its alternatives, the IronXL trial licence allows full-featured testing before committing to a purchase. Additional code examples for reading and writing Excel files are available on the IronXL examples page.

What Are Your Next Steps?

Now that you have reviewed the available Excel export approaches for ASP.NET Core, you can take the following steps to move forward:

  • Install IronXL using dotnet add package IronXL.Excel and run through the getting started guide to create your first workbook.
  • Compare code patterns by reviewing the IronXL examples page for scenarios that match your use case, such as database export, multi-sheet workbooks, or styled reports.
  • Evaluate licensing by visiting the IronXL licensing page to understand the options for development, staging, and production deployments.
  • Explore additional formats by testing the SaveAsCsv, SaveAsJson, and SaveAsXml methods if your application needs to export data in multiple formats from the same codebase.
  • Migrate from legacy code by identifying any existing Response.ContentType = "application/vnd.ms-excel" patterns in your solution and replacing them with IronXL workbook creation, eliminating format warnings for your users.

For production deployments, purchase a licence to unlock professional support and ensure compliance with the IronXL licence terms.

Get stated with IronXL now.
green arrow pointer

Frequently Asked Questions

What are the key features of IronXL for exporting to Excel in ASP.NET Core?

IronXL generates genuine XLSX, CSV, JSON, and XML files without requiring Microsoft Office. It provides an intuitive API for workbook and worksheet management, cell-level styling, formula evaluation, and DataTable integration, all within standard .NET managed code.

How does IronXL compare to ClosedXML for ASP.NET Core projects?

IronXL supports multiple export formats (XLSX, CSV, JSON, XML) and includes professional support with a commercial licence. ClosedXML produces valid XLSX files under the MIT licence, making it well-suited for open-source projects that need only spreadsheet output.

Is IronXL suitable for creating Excel reports from databases in ASP.NET?

Yes. IronXL works directly with DataTable objects and ADO.NET query results, making it straightforward to populate worksheets from SQL Server or other relational databases and stream the resulting file to the browser.

What are the advantages of using IronXL over EPPlus?

IronXL supports CSV, JSON, and XML export natively, includes professional support with every commercial licence, and does not impose a non-commercial restriction. EPPlus requires a separate commercial licence for production use and does not offer native multi-format export.

Can IronXL handle large datasets efficiently?

IronXL is designed for server-side workloads and handles large datasets without requiring Microsoft Excel or COM interop. For very large exports, streaming the byte array directly through the ASP.NET Core File() response avoids buffering the entire file in memory.

Does IronXL require Microsoft Office to be installed for exporting to Excel?

No. IronXL operates entirely in managed .NET code and has no dependency on Microsoft Office, COM interop, or Office automation. It runs on Windows, Linux, and macOS wherever .NET is supported.

Jordi Bardia
Software Engineer
Jordi is most proficient in Python, C# and C++, when he isn’t leveraging his skills at Iron Software; he’s game programming. Sharing responsibilities for product testing, product development and research, Jordi adds immense value to continual product improvement. The varied experience keeps him challenged and engaged, and he ...
Read More

Iron Support Team

We're online 24 hours, 5 days a week.
Chat
Email
Call Me