Skip to footer content
USING IRONXL

How to Export GridView to Excel XLSX in C# Using IronXL

Exporting Data from an ASP.NET GridView to an Excel File

You can export an ASP.NET GridView to an Excel XLSX file in C# by installing IronXL, creating a WorkBook, iterating over GridView rows, writing each cell value to the corresponding worksheet position, and saving the workbook to disk or the HTTP response stream. This approach requires no Microsoft Office installation on the server and works reliably across .NET Framework, .NET Core, and .NET 5 through .NET 10 web applications.

Exporting GridView data to Excel is a standard requirement in enterprise ASP.NET Web Forms projects. Users need to take tabular data out of the browser and into spreadsheets for reporting, offline analysis, and archiving. The challenge is bridging the gap between the server-side GridView control and a properly structured .xlsx file -- without introducing Office Interop dependencies that break in web server environments.

This guide walks you through three progressively richer export patterns: a minimal row-by-row export, a header-plus-data export with column auto-sizing, and a DataTable-driven export that bypasses the rendered HTML entirely. Each example uses C# top-level-statement-compatible code and the IronXL API.

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

Open the Package Manager Console in Visual Studio and run:

Install-Package IronXL
dotnet add package IronXL
Install-Package IronXL
dotnet add package IronXL
SHELL

Alternatively, right-click the project in Solution Explorer, choose Manage NuGet Packages, search for IronXL, and click Install. NuGet resolves all transitive dependencies automatically.

After the package is installed, add the following using directives to your code-behind file:

using System;
using System.Data;
using System.Web.UI;
using IronXL;
using System;
using System.Data;
using System.Web.UI;
using IronXL;
$vbLabelText   $csharpLabel

IronXL supports .NET Framework 4.6.2 and later, as well as .NET Core 3.1 and .NET 5 through .NET 10. No Office installation or COM registration is required on the server. Full API documentation is available in the IronXL object reference.

What NuGet Package ID Should You Use?

The correct package ID is IronXL (not IronXL.Excel, which is a legacy alias). After installation the assembly IronXL.dll is referenced automatically. You can verify the version installed by checking the Packages node in Solution Explorer or running dotnet list package from the project directory.

Does IronXL Work in ASP.NET Core Web Applications?

Yes. The same API works in both ASP.NET Web Forms and ASP.NET Core (MVC or Razor Pages) projects. In Core projects, replace Server.MapPath with IWebHostEnvironment.WebRootPath or Path.Combine(Directory.GetCurrentDirectory(), "Exports") when building server-side file paths.

How Do You Export a GridView to an Excel File?

The simplest export pattern iterates over every rendered row in the GridView and copies each cell's text to the corresponding Excel worksheet cell. In the ASPX markup, add an <asp:GridView ID="gvEmployees" runat="server" AutoGenerateColumns="true" /> control and an <asp:Button ID="btnExport" runat="server" Text="Export to Excel" OnClick="btnExport_Click" /> button. The runat="server" attribute makes both controls accessible in the code-behind.

The code-behind binds sample data on first load and exports on button click:

using System;
using System.Data;
using System.Web.UI;
using IronXL;

public partial class GridViewExport : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            BindGridView();
        }
    }

    private void BindGridView()
    {
        DataTable dt = new DataTable();
        dt.Columns.Add("EmployeeID", typeof(int));
        dt.Columns.Add("Name", typeof(string));
        dt.Columns.Add("Department", typeof(string));
        dt.Columns.Add("Salary", typeof(decimal));

        dt.Rows.Add(1, "John Smith", "Engineering", 75000);
        dt.Rows.Add(2, "Sarah Johnson", "Marketing", 65000);
        dt.Rows.Add(3, "Michael Chen", "Finance", 70000);

        ViewState["EmployeeData"] = dt;
        gvEmployees.DataSource = dt;
        gvEmployees.DataBind();
    }

    protected void btnExport_Click(object sender, EventArgs e)
    {
        WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
        WorkSheet worksheet = workbook.CreateWorkSheet("Employees");

        // Write header row
        for (int j = 0; j < gvEmployees.HeaderRow.Cells.Count; j++)
        {
            worksheet.SetCellValue(0, j, gvEmployees.HeaderRow.Cells[j].Text);
        }

        // Write data rows
        for (int i = 0; i < gvEmployees.Rows.Count; i++)
        {
            for (int j = 0; j < gvEmployees.Rows[i].Cells.Count; j++)
            {
                worksheet.SetCellValue(i + 1, j, gvEmployees.Rows[i].Cells[j].Text);
            }
        }

        string filePath = Server.MapPath("~/Exports/EmployeeData.xlsx");
        workbook.SaveAs(filePath);
    }
}
using System;
using System.Data;
using System.Web.UI;
using IronXL;

public partial class GridViewExport : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            BindGridView();
        }
    }

    private void BindGridView()
    {
        DataTable dt = new DataTable();
        dt.Columns.Add("EmployeeID", typeof(int));
        dt.Columns.Add("Name", typeof(string));
        dt.Columns.Add("Department", typeof(string));
        dt.Columns.Add("Salary", typeof(decimal));

        dt.Rows.Add(1, "John Smith", "Engineering", 75000);
        dt.Rows.Add(2, "Sarah Johnson", "Marketing", 65000);
        dt.Rows.Add(3, "Michael Chen", "Finance", 70000);

        ViewState["EmployeeData"] = dt;
        gvEmployees.DataSource = dt;
        gvEmployees.DataBind();
    }

    protected void btnExport_Click(object sender, EventArgs e)
    {
        WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
        WorkSheet worksheet = workbook.CreateWorkSheet("Employees");

        // Write header row
        for (int j = 0; j < gvEmployees.HeaderRow.Cells.Count; j++)
        {
            worksheet.SetCellValue(0, j, gvEmployees.HeaderRow.Cells[j].Text);
        }

        // Write data rows
        for (int i = 0; i < gvEmployees.Rows.Count; i++)
        {
            for (int j = 0; j < gvEmployees.Rows[i].Cells.Count; j++)
            {
                worksheet.SetCellValue(i + 1, j, gvEmployees.Rows[i].Cells[j].Text);
            }
        }

        string filePath = Server.MapPath("~/Exports/EmployeeData.xlsx");
        workbook.SaveAs(filePath);
    }
}
$vbLabelText   $csharpLabel

The WorkBook.Create(ExcelFileFormat.XLSX) call initialises an in-memory workbook targeting the modern Open XML format. CreateWorkSheet("Employees") adds the first sheet. The outer loop (i) advances through data rows; the inner loop (j) advances through columns. Row index i + 1 is used for data cells so that the header at row 0 is not overwritten.

Why Use IronXL Instead of Excel Interop?

Excel Interop requires a licensed copy of Microsoft Excel installed on the server and creates COM objects that must be explicitly released to avoid memory leaks. IronXL reads and writes .xlsx files directly as Open XML packages -- no COM, no Office, no Marshal.ReleaseComObject boilerplate. This makes it reliable in IIS, Azure App Service, Docker, and other server environments where Office is unavailable.

What Is the Correct MIME Type for XLSX Downloads?

When you want the browser to prompt a file-save dialog instead of serving the bytes inline, set the response content type to application/vnd.openxmlformats-officedocument.spreadsheetml.sheet and add a Content-Disposition: attachment header before writing the workbook bytes to the response stream.

How Do You Add Column Headers and Auto-Size Columns?

The basic example writes headers extracted from HeaderRow.Cells, but a production export should also auto-size each column so that data is readable without manual adjustments in Excel:

protected void btnExport_Click(object sender, EventArgs e)
{
    WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
    WorkSheet worksheet = workbook.CreateWorkSheet("Employees");

    // Write column headers
    int colCount = gvEmployees.HeaderRow.Cells.Count;
    for (int i = 0; i < colCount; i++)
    {
        string header = gvEmployees.HeaderRow.Cells[i].Text;
        worksheet.SetCellValue(0, i, header);
    }

    // Write data rows
    for (int i = 0; i < gvEmployees.Rows.Count; i++)
    {
        for (int j = 0; j < gvEmployees.Rows[i].Cells.Count; j++)
        {
            worksheet.SetCellValue(i + 1, j, gvEmployees.Rows[i].Cells[j].Text);
        }
    }

    // Auto-size each column for readability
    for (int col = 0; col < colCount; col++)
    {
        worksheet.AutoSizeColumn(col);
    }

    string filePath = Server.MapPath("~/Exports/EmployeeReport.xlsx");
    workbook.SaveAs(filePath);
}
protected void btnExport_Click(object sender, EventArgs e)
{
    WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
    WorkSheet worksheet = workbook.CreateWorkSheet("Employees");

    // Write column headers
    int colCount = gvEmployees.HeaderRow.Cells.Count;
    for (int i = 0; i < colCount; i++)
    {
        string header = gvEmployees.HeaderRow.Cells[i].Text;
        worksheet.SetCellValue(0, i, header);
    }

    // Write data rows
    for (int i = 0; i < gvEmployees.Rows.Count; i++)
    {
        for (int j = 0; j < gvEmployees.Rows[i].Cells.Count; j++)
        {
            worksheet.SetCellValue(i + 1, j, gvEmployees.Rows[i].Cells[j].Text);
        }
    }

    // Auto-size each column for readability
    for (int col = 0; col < colCount; col++)
    {
        worksheet.AutoSizeColumn(col);
    }

    string filePath = Server.MapPath("~/Exports/EmployeeReport.xlsx");
    workbook.SaveAs(filePath);
}
$vbLabelText   $csharpLabel

AutoSizeColumn(col) instructs IronXL to calculate the optimal column width based on cell content length, mirroring the Format > AutoFit Column Width action in the Excel desktop application. Calling it in a loop after writing all data is more efficient than calling it after each row because the final content is known at that point.

For additional styling options -- bold headers, background colours, number formats -- explore the IronXL cell styling tutorial. You can also merge cells to create report titles spanning multiple columns.

How Do You Apply Bold Formatting to Header Cells?

Use the IStyle object returned by worksheet["A1"].Style (or a range). Set Font.Bold = true before or after writing values -- the style is decoupled from the cell content:

// Bold the entire header row (columns A through D)
var headerRange = worksheet["A1:D1"];
headerRange.Style.Font.Bold = true;
headerRange.Style.BackgroundColor = "#4472C4";
headerRange.Style.Font.FontColor = "#FFFFFF";
// Bold the entire header row (columns A through D)
var headerRange = worksheet["A1:D1"];
headerRange.Style.Font.Bold = true;
headerRange.Style.BackgroundColor = "#4472C4";
headerRange.Style.Font.FontColor = "#FFFFFF";
$vbLabelText   $csharpLabel

This pattern applies uniformly to ranges of any size. For a full list of style properties, see the IronXL styling API reference.

How Do You Export Data Directly from a DataTable?

Iterating over rendered GridView rows ties your export to the control's current visual state, which can be affected by paging, sorting, and column visibility settings. Exporting from the underlying DataTable stored in ViewState produces a deterministic result regardless of how the grid is configured:

protected void btnExport_Click(object sender, EventArgs e)
{
    DataTable sourceData = ViewState["EmployeeData"] as DataTable;
    if (sourceData == null)
    {
        Response.Write("No data available to export.");
        return;
    }

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

    // Column headers from DataTable schema
    for (int i = 0; i < sourceData.Columns.Count; i++)
    {
        worksheet.SetCellValue(0, i, sourceData.Columns[i].ColumnName);
    }

    // Data rows from DataRow objects
    for (int i = 0; i < sourceData.Rows.Count; i++)
    {
        for (int j = 0; j < sourceData.Columns.Count; j++)
        {
            worksheet.SetCellValue(i + 1, j, sourceData.Rows[i][j].ToString());
        }
    }

    string filePath = Server.MapPath("~/Exports/DataExport.xlsx");
    workbook.SaveAs(filePath);
}
protected void btnExport_Click(object sender, EventArgs e)
{
    DataTable sourceData = ViewState["EmployeeData"] as DataTable;
    if (sourceData == null)
    {
        Response.Write("No data available to export.");
        return;
    }

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

    // Column headers from DataTable schema
    for (int i = 0; i < sourceData.Columns.Count; i++)
    {
        worksheet.SetCellValue(0, i, sourceData.Columns[i].ColumnName);
    }

    // Data rows from DataRow objects
    for (int i = 0; i < sourceData.Rows.Count; i++)
    {
        for (int j = 0; j < sourceData.Columns.Count; j++)
        {
            worksheet.SetCellValue(i + 1, j, sourceData.Rows[i][j].ToString());
        }
    }

    string filePath = Server.MapPath("~/Exports/DataExport.xlsx");
    workbook.SaveAs(filePath);
}
$vbLabelText   $csharpLabel

Column names come from DataTable.Columns[i].ColumnName, which reflects the original data schema rather than any display-name overrides applied in the GridView template. Cell values are retrieved from DataRow objects using the column index, casting each value to string to satisfy SetCellValue.

This pattern is particularly valuable when the GridView uses paging -- iterating gvEmployees.Rows only returns the rows on the currently visible page, whereas reading from the full DataTable exports every record.

What Happens with Large DataTable Exports?

IronXL streams workbook data efficiently for large Excel datasets. For exports exceeding 100,000 rows, consider batching SetCellValue calls inside a single worksheet operation rather than setting individual cells in a nested loop. You can also write the workbook to a MemoryStream and flush it to the Response stream incrementally to avoid holding the entire file in server RAM.

How Do You Handle Errors During Export?

Production export handlers should wrap the IronXL operations in a try/catch block and return a descriptive message to the user when something goes wrong. Common failure modes include missing ~/Exports/ directory, file permission errors, and ViewState being null after a session timeout:

protected void btnExport_Click(object sender, EventArgs e)
{
    try
    {
        DataTable sourceData = ViewState["EmployeeData"] as DataTable;
        if (sourceData == null)
            throw new InvalidOperationException("Session data has expired. Please reload the page.");

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

        for (int i = 0; i < sourceData.Columns.Count; i++)
            worksheet.SetCellValue(0, i, sourceData.Columns[i].ColumnName);

        for (int i = 0; i < sourceData.Rows.Count; i++)
            for (int j = 0; j < sourceData.Columns.Count; j++)
                worksheet.SetCellValue(i + 1, j, sourceData.Rows[i][j].ToString());

        string filePath = Server.MapPath("~/Exports/SafeExport.xlsx");
        workbook.SaveAs(filePath);

        lblStatus.Text = "Export successful. File saved to server.";
    }
    catch (Exception ex)
    {
        lblStatus.Text = $"Export failed: {ex.Message}";
    }
}
protected void btnExport_Click(object sender, EventArgs e)
{
    try
    {
        DataTable sourceData = ViewState["EmployeeData"] as DataTable;
        if (sourceData == null)
            throw new InvalidOperationException("Session data has expired. Please reload the page.");

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

        for (int i = 0; i < sourceData.Columns.Count; i++)
            worksheet.SetCellValue(0, i, sourceData.Columns[i].ColumnName);

        for (int i = 0; i < sourceData.Rows.Count; i++)
            for (int j = 0; j < sourceData.Columns.Count; j++)
                worksheet.SetCellValue(i + 1, j, sourceData.Rows[i][j].ToString());

        string filePath = Server.MapPath("~/Exports/SafeExport.xlsx");
        workbook.SaveAs(filePath);

        lblStatus.Text = "Export successful. File saved to server.";
    }
    catch (Exception ex)
    {
        lblStatus.Text = $"Export failed: {ex.Message}";
    }
}
$vbLabelText   $csharpLabel

For web applications that deliver the file as a direct browser download rather than saving to disk, use Response.BinaryWrite or write to Response.OutputStream after setting the content-type and disposition headers. Ensure the ~/Exports/ directory exists and the IIS application pool identity has write permission to it.

How Do You Send the XLSX File as a Browser Download?

Replace workbook.SaveAs(filePath) with a stream-based response:

using System.IO;

// ... inside btnExport_Click after populating the worksheet ...

byte[] fileBytes;
using (MemoryStream ms = new MemoryStream())
{
    workbook.SaveAs(ms);
    fileBytes = ms.ToArray();
}

Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", "attachment; filename=EmployeeData.xlsx");
Response.BinaryWrite(fileBytes);
Response.End();
using System.IO;

// ... inside btnExport_Click after populating the worksheet ...

byte[] fileBytes;
using (MemoryStream ms = new MemoryStream())
{
    workbook.SaveAs(ms);
    fileBytes = ms.ToArray();
}

Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", "attachment; filename=EmployeeData.xlsx");
Response.BinaryWrite(fileBytes);
Response.End();
$vbLabelText   $csharpLabel

This pattern avoids writing a temporary file to disk. The MemoryStream is allocated on the server, serialised to byte[], and sent directly to the client. The Response.End() call flushes the response and prevents additional page markup from being appended after the binary data.

How Do You Export Multiple Sheets or Advanced Workbooks?

A single WorkBook can contain multiple sheets, which is useful when you need to export several GridViews -- or the same dataset at different granularities -- into one file. Call workbook.CreateWorkSheet(name) once for each tab:

WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);

WorkSheet summary = workbook.CreateWorkSheet("Summary");
WorkSheet detail  = workbook.CreateWorkSheet("Detail");

// Populate summary sheet ...
// Populate detail sheet ...

workbook.SaveAs(Server.MapPath("~/Exports/FullReport.xlsx"));
WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);

WorkSheet summary = workbook.CreateWorkSheet("Summary");
WorkSheet detail  = workbook.CreateWorkSheet("Detail");

// Populate summary sheet ...
// Populate detail sheet ...

workbook.SaveAs(Server.MapPath("~/Exports/FullReport.xlsx"));
$vbLabelText   $csharpLabel

IronXL also supports reading existing Excel files, so you can load a pre-built template with branding and formatting, inject data into specific cells, and save the result -- preserving charts, images, and styles already in the template.

For even richer output, IronXL provides Excel formula support, conditional formatting, chart creation, and image embedding. These capabilities are documented in the IronXL tutorials section.

How Does IronXL Compare to EPPlus and ClosedXML?

Feature IronXL EPPlus ClosedXML
Office installation required No No No
XLSX read and write Yes Yes Yes
XLS (legacy) support Yes No No
CSV / TSV export Yes No Partial
Chart creation API Yes Yes Limited
Licence model Perpetual + SaaS Polyform non-commercial MIT
.NET 10 support Yes Yes Yes

EPPlus switched to a commercial licence in version 5, making IronXL a natural alternative for teams already investing in a commercial .NET library ecosystem. ClosedXML remains MIT-licensed but has limited chart support. The right choice depends on your project's licensing constraints, the Excel features you need, and whether legacy .xls format support is a requirement.

What Excel Formats Does IronXL Support?

IronXL reads and writes .xlsx (Office Open XML), .xls (legacy BIFF8), .csv, and .tsv formats. You can convert between Excel file formats with a single API call by changing the ExcelFileFormat enum value passed to WorkBook.Create or by loading an existing file and saving it in a different format.

What Are Your Next Steps?

You now have three production-ready patterns for exporting ASP.NET GridView data to Excel XLSX files using IronXL:

  • Row iteration export -- the quickest path to a working export from a bound GridView
  • Header and formatting export -- adds column auto-sizing and bold headers for presentable reports
  • DataTable-driven export -- bypasses the rendered control entirely for paged or filtered grids

The next logical steps are:

  1. Add a browser-download response using MemoryStream and Response.BinaryWrite so users receive the file immediately without a server-side save path.
  2. Apply cell styling -- bold headers, background colour fills, and number formats -- using the IronXL formatting API.
  3. Explore multi-sheet workbooks to combine summary and detail data in a single file delivered to stakeholders.
  4. Read Excel files back into DataTable objects using IronXL's read API for round-trip import/export workflows.
  5. Start a free trial at ironsoftware.com/csharp/excel/ to test all features in your project with a full-capability trial licence.

For teams building document generation pipelines, IronPDF integrates with IronXL to export worksheets directly to PDF. The Iron Suite bundles IronXL with IronPDF, IronOCR, IronBarcode, and IronZIP at a reduced combined price.

Frequently Asked Questions

What is the primary purpose of using IronXL for exporting GridView to Excel?

IronXL is primarily used to facilitate the export of data from an ASP.NET GridView to Excel formats like XLSX, ensuring high performance and ease of integration in C# applications.

Can IronXL handle large datasets when exporting from GridView?

Yes, IronXL is optimized to efficiently handle large datasets during the export process from GridView to Excel, maintaining speed and performance.

Is it possible to customize the Excel output when using IronXL?

With IronXL, you can customize various aspects of the Excel output, such as formatting, styling, and including additional data or formulas in the exported file.

How does IronXL compare to other libraries for exporting GridView to Excel?

IronXL offers a more straightforward and flexible approach compared to some other libraries, providing support for modern Excel formats and direct integration with C# applications.

Does IronXL support exporting to formats other than XLSX?

Yes, IronXL supports exporting to various Excel formats, including XLS, CSV, and TSV, providing versatility for different project requirements.

What are the prerequisites for using IronXL in a project?

To use IronXL, you need a .NET environment and can install IronXL via NuGet in your C# project.

Can IronXL export GridView data asynchronously?

IronXL supports asynchronous operations, allowing developers to export GridView data to Excel files without blocking the main application thread.

How can I get started with using IronXL for exporting GridView to Excel?

To get started, refer to IronXL's documentation and examples, which provide step-by-step guidance on setting up and executing data exports from GridView to Excel.

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