How to Create and Convert PDF Documents in ASP.NET Core
Learn to create and convert PDF documents in ASP.NET Core using QuestPDF and HTML-to-PDF tools, with simple code, diagrams, and clear advice.
A receipt from the sweet shop
Think about your favourite sweet shop. You buy a box of laddoos, and the shopkeeper hands you a printed bill. That bill always looks the same no matter which printer or phone you use to view it. The amount, the date, the shop name — they never move around or change size.
A PDF (Portable Document Format) is exactly like that printed bill. It is a fixed paper. Once you make it, it looks the same on every computer, phone, and printer in the world. That is why offices use PDFs for invoices, report cards, tickets, and certificates.
In this guide, you will learn two things in ASP.NET Core:
- How to create a brand-new PDF from scratch using C# code.
- How to convert existing HTML or a web page into a PDF.
We will keep the code small and friendly. By the end, you will be able to send a real PDF back to a browser.
Two ways to make a PDF
There are two main roads to a PDF in .NET. Picture them like two ways to make that sweet-shop bill.
- Road 1 — Build it yourself. You take a blank page and place each piece of text and each line by hand. This is the code-first way. A popular library for this is QuestPDF.
- Road 2 — Print a web page. You already have an HTML page that looks nice. You ask a tool to "print" that page into a PDF. This is the HTML-to-PDF way. Tools like IronPDF, Syncfusion, and NReco do this.
Both roads end in the same place: a stream of bytes that you send back to the user. Let us walk each road.
Road 1: Create a PDF with QuestPDF
QuestPDF lets you design a document using plain C#. You describe the page in a clear, readable way, almost like building blocks. It is fast and works well on servers.
Step 1: Install the package
Open a terminal in your project folder and run this command.
// In a terminal, not in C#:
// dotnet add package QuestPDFIf you prefer the NuGet UI in Visual Studio, search for QuestPDF and click install.
Step 2: Set the license once
QuestPDF asks you to declare which license you are using. This is one line at startup. For learning, small companies, open-source, or charity work, the free Community license is fine.
using QuestPDF.Infrastructure;
// In Program.cs, before the app runs:
QuestPDF.Settings.License = LicenseType.Community;A quick word on the license, because it matters. QuestPDF is free under its Community MIT License if your company earns less than $1M USD in yearly gross revenue, or if you use it for learning, open-source, or charity. Companies above that limit need a paid Professional or Enterprise license. Always check the official license page before shipping a paid product.
Step 3: Describe the document
Now the fun part. We build a small invoice. Notice how the code reads almost like a sentence: a page, with some margin, a header, some content, and a footer.
using QuestPDF.Fluent;
using QuestPDF.Helpers;
public static byte[] BuildInvoicePdf(string customer, decimal total)
{
return Document.Create(doc =>
{
doc.Page(page =>
{
page.Margin(40);
page.Size(PageSizes.A4);
page.Header()
.Text("Sweet Shop Invoice")
.FontSize(22).Bold();
page.Content().PaddingVertical(20).Column(col =>
{
col.Item().Text($"Customer: {customer}");
col.Item().Text($"Date: {DateTime.Now:dd MMM yyyy}");
col.Item().PaddingTop(10)
.Text($"Total: ₹{total}").FontSize(16).Bold();
});
page.Footer()
.AlignCenter()
.Text("Thank you for shopping with us!");
});
}).GeneratePdf(); // returns a byte[]
}The method returns a byte[] — the raw PDF. That byte array is all we need to send the file to the user.
Step 4: Send it from a controller
Here is a tiny controller action. It builds the PDF, then hands it back with the right content type so the browser knows it is a PDF.
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("invoices")]
public class InvoiceController : ControllerBase
{
[HttpGet("{customer}")]
public IActionResult Get(string customer)
{
byte[] pdf = BuildInvoicePdf(customer, total: 250m);
return File(pdf, "application/pdf", "invoice.pdf");
}
}When someone visits /invoices/Asha, ASP.NET Core sends back invoice.pdf. The browser shows it or downloads it. That is a complete, working PDF feature.
QuestPDF request flow
Steps
Request
User hits /invoices/Asha
Build doc
Describe page in C#
Generate bytes
GeneratePdf() runs
Return file
File(bytes, application/pdf)
How QuestPDF lays out the page
QuestPDF measures your content and decides where each part goes. If your content is too tall for one page, it moves the extra onto a new page automatically. You do not have to count pixels.
Road 2: Convert HTML to a PDF
Sometimes you already have a nice HTML page. Maybe it is a Razor view with your company colours and a logo. Rebuilding all of that in C# would be a waste. Instead, you can convert the HTML into a PDF.
The idea is simple: a tool takes your HTML and CSS, renders it like a browser would, and then saves that picture as a PDF.
A simple HTML-to-PDF example
Many tools share a similar shape: give it HTML, get back bytes. Here is the pattern with a popular library, IronPDF. The exact class names differ between tools, but the steps are the same.
using IronPdf;
public static byte[] BuildReportFromHtml(string title, string body)
{
var html = $@"
<html>
<head><style>
body {{ font-family: sans-serif; }}
h1 {{ color: #1a4f8b; }}
</style></head>
<body>
<h1>{title}</h1>
<p>{body}</p>
</body>
</html>";
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData; // byte[]
}Then you return it from a controller the same way as before, using File(bytes, "application/pdf", "report.pdf"). The big win here is that your CSS — colours, fonts, spacing — carries over, so the PDF matches your web page.
A note on HTML-to-PDF tools
Most strong HTML-to-PDF libraries are paid for commercial use. They are powerful and save time, but check the price and license before you commit. Here is a quick comparison to help you choose.
| Tool | Style | Cost | Good for |
|---|---|---|---|
| QuestPDF | Code-first | Free under limit | Precise invoices and reports |
| IronPDF | HTML to PDF | Paid | Reusing HTML and CSS pages |
| Syncfusion | HTML to PDF | Free community / paid | Rich documents, many features |
| NReco.PdfGenerator | HTML to PDF | Free (wkhtmltopdf) | Simple jobs, older engine |
Creating vs converting: which one?
Both roads make a PDF, but they suit different jobs. Use this table as a quick guide.
| Question | Create (QuestPDF) | Convert (HTML to PDF) |
|---|---|---|
| Do you need exact control? | Yes, full control | Less, depends on CSS |
| Do you already have HTML? | Not used | Reuses it directly |
| Speed and memory on servers | Very good | Heavier, uses a browser engine |
| Cost for a small team | Often free | Often paid |
| Learning curve | Learn the C# API | Just write HTML |
A simple rule: if you are starting fresh and want clean, fast, server-friendly documents, pick QuestPDF. If you already have polished HTML pages and want them to look identical in PDF, pick an HTML-to-PDF tool.
Choosing your approach
Steps
Have HTML?
Do you own a nice HTML page
Yes path
Use HTML to PDF tool
No path
Build with QuestPDF
Decide
Check cost and control
Good habits for PDFs on a server
A few simple tips will save you trouble later.
- Do the work off the request thread for big files. Generating a 200-page PDF can take time. For heavy jobs, build the PDF in a background task or a queue, then let the user download it when it is ready.
- Stream large files. Instead of holding the whole PDF in memory, you can write it straight to the response stream. This keeps memory low when many users ask at once.
- Set the right headers. The content type must be
application/pdf. To force a download, you can pass a file name; to show it inline, set the disposition to inline. - Watch fonts. A Linux server may not have the fonts your Windows machine has. QuestPDF bundles a safe default font, but if you use special fonts, ship the font files with your app.
- Cache repeated PDFs. If the same report is requested many times and the data has not changed, store the bytes and reuse them.
Here is how streaming looks. We write the PDF straight into the HTTP response so we never keep the whole file in memory.
[HttpGet("stream/{customer}")]
public IActionResult Stream(string customer)
{
Response.ContentType = "application/pdf";
var document = Document.Create(doc =>
{
doc.Page(page =>
{
page.Margin(40);
page.Content().Text($"Streaming invoice for {customer}");
});
});
document.GeneratePdf(Response.Body); // writes directly to the stream
return new EmptyResult();
}Adding a table to your PDF
Most real documents have a table — a list of items with prices, or rows of marks on a report card. QuestPDF makes this easy. You define the columns once, add a header row, then loop over your data and add one row per item. The layout engine lines everything up for you and moves long tables onto new pages by itself.
Here is a small order table. Imagine the customer bought three kinds of sweets, and we want each on its own row with a price.
using QuestPDF.Fluent;
using QuestPDF.Helpers;
public static byte[] BuildOrderPdf((string Name, decimal Price)[] items)
{
return Document.Create(doc =>
{
doc.Page(page =>
{
page.Margin(40);
page.Size(PageSizes.A4);
page.Header().Text("Order Summary").FontSize(20).Bold();
page.Content().PaddingTop(15).Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn(3); // wide column for the name
columns.RelativeColumn(1); // narrow column for the price
});
table.Header(header =>
{
header.Cell().Text("Item").Bold();
header.Cell().AlignRight().Text("Price").Bold();
});
foreach (var item in items)
{
table.Cell().Text(item.Name);
table.Cell().AlignRight().Text($"₹{item.Price}");
}
});
});
}).GeneratePdf();
}Read the code slowly and you can almost say it out loud: "two columns, a header row, then one row per item." That readability is the main reason people enjoy QuestPDF. When the table grows past one page, the header row even repeats on the next page so the reader never loses track of which column is which.
A common mistake to avoid
New developers often forget to set the QuestPDF license, then see an error at runtime. Set it once at startup in Program.cs. Do not set it inside every method — that is repeated work and easy to forget.
Another common slip is building huge PDFs on the web request itself and timing out. If a job is large, move it to the background. Your users get a smoother experience, and your server stays calm.
Quick recap
- A PDF is like a printed bill: it looks the same everywhere, which is why it is perfect for invoices, reports, and certificates.
- There are two roads: create a PDF from scratch in code, or convert existing HTML into a PDF.
- QuestPDF is a clean, fast, code-first library. It is free under the Community MIT License for companies under $1M USD yearly revenue, and for learning, open-source, and charity use. Set the license once at startup.
- To send a PDF, generate a
byte[]and returnFile(bytes, "application/pdf", "name.pdf"). For big files, stream toResponse.Body. - HTML-to-PDF tools (IronPDF, Syncfusion, NReco) reuse your HTML and CSS, but most are paid for commercial use, so check the license first.
- For large documents, do the work in the background and mind your fonts on Linux servers.
References and further reading
- QuestPDF — Official site and documentation
- QuestPDF License and Pricing
- QuestPDF on GitHub
- QuestPDF on NuGet
- Convert HTML to PDF in ASP.NET Core — IronPDF tutorial
- Convert HTML to PDF in ASP.NET Core — Syncfusion docs
- NReco.PdfGenerator — free HTML to PDF
Related Posts
Global Error Handling in ASP.NET Core: From Middleware to Modern Handlers
Learn global error handling in ASP.NET Core step by step: from try-catch middleware to IExceptionHandler and Problem Details, with simple diagrams and clear code.
Building Async APIs in ASP.NET Core the Right Way
Learn to build fast, safe async APIs in ASP.NET Core: async/await, CancellationToken, avoiding .Result deadlocks, and thread pool tips.
How to Easily Create PDF Documents in ASP.NET Core
A simple, friendly guide to creating PDF documents in ASP.NET Core with QuestPDF, with clear code, diagrams, tables, and tips for invoices and reports.
The 3 C# PDF Libraries Every Developer Must Know
A friendly guide to QuestPDF, PDFsharp, and iText for C#. Learn what each does, their licensing, code examples, and how to pick the right one.
Top 15 Mistakes Developers Make When Creating Web APIs
A warm, beginner-friendly tour of the 15 most common Web API mistakes in ASP.NET Core, with simple fixes, diagrams, tables, and clear C# examples.
How to Increase the Performance of Web APIs in .NET
A friendly, step-by-step guide to making your ASP.NET Core Web APIs fast: async, caching, query tuning, compression, and pooling in .NET 10.