Blazor Server apps can render Razor components directly to PDF. No HTML string manipulation, no third-party Chromium processes—just your existing components converted to downloadable documents.
using IronPdf;
using IronPdf.Extensions.Blazor;
// Install via NuGet: Install-Package IronPdf.Extensions.Blazor
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderRazorComponentToPdf<InvoiceComponent>(
new Dictionary<string, object> { ["InvoiceId"] = 1234 });
pdf.SaveAs("invoice.pdf");
Pass parameters to your Razor component just like you would in Blazor. The PDF renders with your styles, layouts, and data binding intact.
Why Convert Razor Components to PDF?
You already have Razor components displaying invoices, reports, and receipts in your Blazor app. Converting them to PDF means:
Code reuse - Same component renders in browser AND as PDF
Consistent styling - Your Bootstrap or Tailwind CSS carries over
Dynamic data - Parameters and cascading values work normally
Maintainability - One component to update, two outputs
The alternative—building HTML strings manually or maintaining separate templates—creates tech debt fast.
What NuGet Packages Do I Need?
Blazor Server PDF generation requires two packages:
Install-Package IronPdf
Install-Package IronPdf.Extensions.Blazor
The extensions package provides RenderRazorComponentToPdf and related methods. Without it, you're limited to rendering raw HTML strings.
How Do I Create a Printable Razor Component?
Design your component with print in mind. Here's a simple invoice:
@* InvoiceComponent.razor *@
<div class="invoice">
<h1>Invoice #@InvoiceId</h1>
<div class="customer">
<strong>@CustomerName</strong>
<p>@CustomerAddress</p>
</div>
<table>
<thead>
<tr>
<th>Item</th>
<th>Qty</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@foreach (var item in LineItems)
{
<tr>
<td>@item.Name</td>
<td>@item.Quantity</td>
<td>@item.Price.ToString("C")</td>
</tr>
}
</tbody>
</table>
<div class="total">
<strong>Total: @Total.ToString("C")</strong>
</div>
</div>
@code {
[Parameter] public int InvoiceId { get; set; }
[Parameter] public string CustomerName { get; set; }
[Parameter] public string CustomerAddress { get; set; }
[Parameter] public List<LineItem> LineItems { get; set; }
[Parameter] public decimal Total { get; set; }
}
This component works in your Blazor app AND converts cleanly to PDF. The key: avoid JavaScript-dependent interactions in components meant for PDF output.
How Do I Render the Component to PDF?
In your Blazor page or service:
using IronPdf;
using IronPdf.Extensions.Blazor;
// Install via NuGet: Install-Package IronPdf.Extensions.Blazor
public class PdfService
{
public byte[] GenerateInvoicePdf(Invoice invoice)
{
var renderer = new ChromePdfRenderer();
var parameters = new Dictionary<string, object>
{
["InvoiceId"] = invoice.Id,
["CustomerName"] = invoice.CustomerName,
["CustomerAddress"] = invoice.CustomerAddress,
["LineItems"] = invoice.Items,
["Total"] = invoice.Total
};
var pdf = renderer.RenderRazorComponentToPdf<InvoiceComponent>(parameters);
return pdf.BinaryData;
}
}
The Dictionary<string, object> maps to your component's [Parameter] properties. Type matching happens automatically.
How Do I Add a Download Button?
Blazor Server needs JavaScript interop to trigger downloads. Here's the complete pattern:
JavaScript helper (wwwroot/js/download.js):
window.downloadFile = (filename, contentType, content) => {
const blob = new Blob([Uint8Array.from(atob(content), c => c.charCodeAt(0))],
{ type: contentType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
};
Blazor component:
@inject IJSRuntime JS
@inject PdfService PdfService
<button @onclick="DownloadInvoice">Download PDF</button>
@code {
[Parameter] public Invoice Invoice { get; set; }
private async Task DownloadInvoice()
{
var pdfBytes = PdfService.GenerateInvoicePdf(Invoice);
var base64 = Convert.ToBase64String(pdfBytes);
await JS.InvokeVoidAsync("downloadFile",
$"invoice-{Invoice.Id}.pdf",
"application/pdf",
base64);
}
}
The PDF generates server-side, converts to Base64, and the JavaScript creates a client-side download.
How Do I Apply CSS Styling?
Your component needs its CSS accessible during rendering. Options:
Option 1: Inline styles in the component
<style>
.invoice { font-family: Arial, sans-serif; padding: 20px; }
.invoice h1 { color: #333; }
.invoice table { width: 100%; border-collapse: collapse; }
.invoice th, .invoice td { border: 1px solid #ddd; padding: 8px; }
</style>
Option 2: Reference external CSS
using IronPdf;
using IronPdf.Extensions.Blazor;
// Install via NuGet: Install-Package IronPdf.Extensions.Blazor
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
// Add custom CSS
renderer.RenderingOptions.CustomCssUrl = "https://yourapp.com/css/print.css";
The Print media type uses your @media print CSS rules if defined.
What About Page Breaks for Multi-Page PDFs?
CSS handles page breaks:
/* Force page break before an element */
.page-break {
page-break-before: always;
}
/* Keep element together (don't split across pages) */
.keep-together {
page-break-inside: avoid;
}
/* Start each invoice item on a new page */
.invoice-item {
page-break-after: always;
}
Use these classes in your Razor component:
@foreach (var section in Report.Sections)
{
<div class="section @(section.IsNewPage ? "page-break" : "")">
<h2>@section.Title</h2>
<p>@section.Content</p>
</div>
}
Can I Add Headers and Footers?
Yes, through rendering options:
using IronPdf;
using IronPdf.Extensions.Blazor;
// Install via NuGet: Install-Package IronPdf.Extensions.Blazor
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader = new TextHeaderFooter
{
CenterText = "Company Name",
FontSize = 12
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter
{
LeftText = "{date}",
RightText = "Page {page} of {total-pages}",
FontSize = 10
};
var pdf = renderer.RenderRazorComponentToPdf<ReportComponent>(parameters);
The placeholders {page}, {total-pages}, and {date} are replaced automatically.
How Do I Handle Large Reports?
For reports with hundreds of pages, consider:
using IronPdf;
using IronPdf.Extensions.Blazor;
// Install via NuGet: Install-Package IronPdf.Extensions.Blazor
var renderer = new ChromePdfRenderer();
// Render asynchronously to avoid blocking
var pdf = await Task.Run(() =>
renderer.RenderRazorComponentToPdf<LargeReportComponent>(parameters));
// Stream directly instead of loading entire PDF into memory
return File(pdf.Stream, "application/pdf", "report.pdf");
For very large datasets, consider paginating within your component rather than rendering everything at once.
What Are Common Pitfalls?
JavaScript-dependent content won't render. Blazor components that rely on JS interop for content (charts via Chart.js, maps, etc.) render blank. Use server-side chart libraries or pre-render chart images.
Cascading parameters need explicit handling. Pass them through the parameters dictionary, not through Blazor's cascading value system.
Authentication context is unavailable. The PDF renderer doesn't have access to the user's auth state. Pass any user-specific data as parameters.
Complete Working Example
Here's a minimal end-to-end implementation:
using IronPdf;
using IronPdf.Extensions.Blazor;
// Install via NuGet: Install-Package IronPdf.Extensions.Blazor
// Program.cs - Register service
builder.Services.AddScoped<PdfService>();
// PdfService.cs
public class PdfService
{
public byte[] GeneratePdf<TComponent>(Dictionary<string, object> parameters)
where TComponent : IComponent
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
var pdf = renderer.RenderRazorComponentToPdf<TComponent>(parameters);
return pdf.BinaryData;
}
}
Razor components are first-class citizens for PDF generation in Blazor Server. Your existing UI components become printable documents with minimal additional code.
For the complete API reference, see the IronPDF Blazor Server documentation.
Written by Jacob Mellor, CTO at Iron Software. Jacob created IronPDF and leads a team of 50+ engineers building .NET document processing libraries.
Top comments (0)