CSV-to-XLSX in .NET is straightforward: parse the CSV into rows, write those rows into a workbook. ClosedXML has the simplest API for most use cases. EPPlus trades simplicity for performance on large datasets and offers more control over formatting, formulas, and charts. The ChangeThisFile API handles the whole pipeline for you — useful when you need XLSX without adding a spreadsheet library to your project.
Method 1: ClosedXML (simple API, MIT license)
ClosedXML wraps OpenXML SDK with a clean .NET-friendly API. MIT license, no Excel required, runs on any OS.
dotnet add package ClosedXML
dotnet add package CsvHelper # for robust CSV parsing
using ClosedXML.Excel;
using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;
public static class CsvToXlsx
{
/// <summary>Converts a CSV file to XLSX. First row becomes the header row.</summary>
public static void Convert(
string csvPath,
string xlsxPath,
bool hasHeader = true)
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = hasHeader,
MissingFieldFound = null
};
using var reader = new StreamReader(csvPath);
using var csv = new CsvReader(reader, config);
using var records = csv.GetRecords<dynamic>();
using var workbook = new XLWorkbook();
var sheet = workbook.Worksheets.Add("Data");
int row = 1;
bool headerWritten = !hasHeader;
foreach (IDictionary<string, object> record in records)
{
if (!headerWritten)
{
int col = 1;
foreach (var key in record.Keys)
sheet.Cell(row, col++).Value = key;
// Bold the header row
sheet.Row(row).Style.Font.SetBold(true);
row++;
headerWritten = true;
}
int c = 1;
foreach (var val in record.Values)
sheet.Cell(row, c++).Value = val?.ToString() ?? "";
row++;
}
sheet.Columns().AdjustToContents();
workbook.SaveAs(xlsxPath);
}
}
// Usage
CsvToXlsx.Convert("data.csv", "data.xlsx");
CsvHelper handles edge cases automatically: quoted fields with commas, embedded newlines, escaped quotes. Don't parse CSVs with string.Split(',') — it breaks on quoted fields.
Method 2: EPPlus (faster for large files, rich formatting)
EPPlus loads CSVs via its built-in LoadFromText method and handles 100K+ row spreadsheets efficiently with a low memory footprint.
dotnet add package EPPlus
using OfficeOpenXml;
public static class CsvToXlsxEpPlus
{
public static void Convert(
string csvPath,
string xlsxPath,
bool hasHeader = true,
char delimiter = ',')
{
// EPPlus requires a license context (LicenseContext.NonCommercial for open source)
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using var package = new ExcelPackage();
var sheet = package.Workbook.Worksheets.Add("Data");
var format = new ExcelTextFormat
{
Delimiter = delimiter,
TextQualifier = '"',
EOL = "\n"
};
var csvText = File.ReadAllText(csvPath);
sheet.Cells["A1"].LoadFromText(csvText, format, TableStyles.Medium2, hasHeader);
// Auto-fit columns
sheet.Cells[sheet.Dimension.Address].AutoFitColumns();
package.SaveAs(xlsxPath);
}
}
EPPlus 5+ requires a license context — LicenseContext.NonCommercial for open-source projects, LicenseContext.Commercial (paid) for commercial applications. Set it once before using the library.
TableStyles.Medium2 applies an Excel table style with banded rows and filter dropdowns. Pass TableStyles.None for a plain worksheet without styling.
Method 3: ChangeThisFile API (HttpClient, no spreadsheet library)
POST the CSV, receive XLSX. No parsing code to write. Free tier: 1,000 conversions/month.
# Verify with curl first
curl -X POST https://changethisfile.com/v1/convert \
-H "Authorization: Bearer ctf_sk_your_key" \
-F "file=@data.csv" \
-F "target=xlsx" \
--output data.xlsx
using System.Net.Http;
using System.Net.Http.Headers;
public class SpreadsheetConvertService
{
private readonly HttpClient _http;
private const string ApiKey = "ctf_sk_your_key_here";
public SpreadsheetConvertService(IHttpClientFactory factory)
=> _http = factory.CreateClient("ctf");
public async Task ConvertCsvToXlsxAsync(
string csvPath,
string xlsxPath,
CancellationToken ct = default)
{
await using var fileStream = File.OpenRead(csvPath);
using var form = new MultipartFormDataContent();
var fileContent = new StreamContent(fileStream);
fileContent.Headers.ContentType =
new MediaTypeHeaderValue("text/csv");
form.Add(fileContent, "file", Path.GetFileName(csvPath));
form.Add(new StringContent("xlsx"), "target");
using var request = new HttpRequestMessage(HttpMethod.Post, "/v1/convert")
{
Content = form,
Headers = { Authorization =
new AuthenticationHeaderValue("Bearer", ApiKey) }
};
using var response = await _http.SendAsync(request, ct);
response.EnsureSuccessStatusCode();
await using var outStream = File.Create(xlsxPath);
await response.Content.CopyToAsync(outStream, ct);
}
}
When to use each
| Approach | Best for | Tradeoff |
|---|---|---|
| ClosedXML | Simple conversions, MIT license, readable API | Slower than EPPlus on very large files (>100K rows) |
| EPPlus | Large files, auto-styled tables, rich formatting | Commercial license required for commercial use |
| ChangeThisFile API | No spreadsheet library in project, free tier for low volume | Network call; 25MB file limit on free tier |
Production tips
- Always use CsvHelper for CSV parsing. Manual string splitting breaks on quoted fields containing commas. CsvHelper handles RFC 4180 edge cases correctly.
- Stream large CSVs — don't ReadAllText. For files over 10MB, stream row by row (ClosedXML approach above) instead of loading into memory all at once.
- Use IHttpClientFactory for the API. Register a named client in
Program.cswith base address and timeout. InjectIHttpClientFactoryinto the service. - Set column types for better Excel UX. If you know a column is numeric, write a double/decimal instead of a string — Excel will treat it as a number, enabling SUM formulas and sorting. CsvHelper's TypeConverter can parse numbers automatically.
- AdjustToContents is expensive on wide sheets. ClosedXML's
Columns().AdjustToContents()scans all cell values to compute widths. On 100+ column sheets, cap the max width:sheet.Column(i).AdjustToContents(1, 50).
ClosedXML + CsvHelper handles most CSV-to-XLSX needs with a clean API and MIT license. EPPlus is the better choice when performance on large files matters or you want Excel table styling out of the box. The ChangeThisFile API removes both library dependencies entirely for low-volume use cases. Free tier: 1,000 conversions/month.