Generating PDFs in Blazor requires understanding the client-server split. Blazor Server runs C# on the server, making PDF generation straightforward. Blazor WebAssembly needs an API endpoint since PDF libraries can't run in the browser.
// Blazor Server component
@page "/generate-pdf"
@using IronPdf
// Install via NuGet: Install-Package IronPdf
<button @onclick="GeneratePdf">Download Report</button>
@code {
private async Task GeneratePdf()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Report</h1><p>Generated from Blazor</p>");
// Download to client
await DownloadFile("report.pdf", pdf.BinaryData);
}
}
Server-side Blazor has direct access to PDF libraries. WebAssembly needs a different approach.
What's the Difference Between Blazor Server and WebAssembly?
| Feature | Blazor Server | Blazor WebAssembly |
|---|---|---|
| Where code runs | Server | Browser |
| PDF library access | Direct | Via API only |
| Startup time | Fast | Slower (download .NET runtime) |
| Server dependency | Required | Optional |
| Scalability | Limited by server | Better |
For PDF generation:
- Blazor Server: Call IronPDF directly in components
- Blazor WebAssembly: Call an API endpoint that generates the PDF
How Do I Generate PDFs in Blazor Server?
Direct PDF generation in server-side code:
// Pages/Reports.razor
@page "/reports"
@using IronPdf
@inject IJSRuntime JS
// Install via NuGet: Install-Package IronPdf
<h3>Report Generator</h3>
<div class="form-group">
<label>Report Title:</label>
<input @bind="reportTitle" class="form-control" />
</div>
<button class="btn btn-primary" @onclick="GenerateReport">
Generate PDF Report
</button>
@code {
private string reportTitle = "Monthly Report";
private async Task GenerateReport()
{
var html = $@"
<html>
<head>
<style>
body {{ font-family: Arial; padding: 40px; }}
h1 {{ color: #333; }}
.date {{ color: #666; }}
</style>
</head>
<body>
<h1>{reportTitle}</h1>
<p class='date'>Generated: {DateTime.Now:MMMM dd, yyyy}</p>
<p>Report content goes here...</p>
</body>
</html>";
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
await JS.InvokeVoidAsync("downloadFile", "report.pdf", pdf.BinaryData);
}
}
Add JavaScript interop for downloads:
// wwwroot/js/download.js
window.downloadFile = (filename, byteArray) => {
const blob = new Blob([new Uint8Array(byteArray)], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
};
How Do I Generate PDFs in Blazor WebAssembly?
WebAssembly can't run IronPDF directly. Use an API:
API Controller:
// Server/Controllers/PdfController.cs
using IronPdf;
using Microsoft.AspNetCore.Mvc;
// Install via NuGet: Install-Package IronPdf
[ApiController]
[Route("api/[controller]")]
public class PdfController : ControllerBase
{
[HttpPost("generate")]
public IActionResult Generate([FromBody] PdfRequest request)
{
var renderer = new ChromePdfRenderer();
var html = $@"
<html>
<body>
<h1>{request.Title}</h1>
<p>{request.Content}</p>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
return File(pdf.BinaryData, "application/pdf", "document.pdf");
}
}
public class PdfRequest
{
public string Title { get; set; }
public string Content { get; set; }
}
WebAssembly Component:
// Client/Pages/Reports.razor
@page "/reports"
@inject HttpClient Http
@inject IJSRuntime JS
<button @onclick="GeneratePdf">Download PDF</button>
@code {
private async Task GeneratePdf()
{
var request = new { Title = "Report", Content = "Generated from WASM" };
var response = await Http.PostAsJsonAsync("api/pdf/generate", request);
if (response.IsSuccessStatusCode)
{
var bytes = await response.Content.ReadAsByteArrayAsync();
await JS.InvokeVoidAsync("downloadFile", "report.pdf", bytes);
}
}
}
The browser calls the API, the server generates the PDF, and returns it as bytes.
How Do I Render Razor Components to PDF?
Convert your existing Blazor components to PDF:
// Services/RazorPdfService.cs
using IronPdf;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
// Install via NuGet: Install-Package IronPdf
public class RazorPdfService
{
private readonly HtmlRenderer _htmlRenderer;
public RazorPdfService(IServiceProvider serviceProvider)
{
_htmlRenderer = new HtmlRenderer(serviceProvider, serviceProvider.GetRequiredService<ILoggerFactory>());
}
public async Task<byte[]> RenderComponentToPdf<TComponent>(Dictionary<string, object> parameters = null)
where TComponent : IComponent
{
var html = await _htmlRenderer.Dispatcher.InvokeAsync(async () =>
{
var output = await _htmlRenderer.RenderComponentAsync<TComponent>(
ParameterView.FromDictionary(parameters ?? new Dictionary<string, object>()));
return output.ToHtmlString();
});
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
}
Register the service:
// Program.cs
builder.Services.AddScoped<RazorPdfService>();
How Do I Create Invoice PDFs?
Complete invoice generation example:
// Pages/Invoices.razor
@page "/invoice/{InvoiceId:int}"
@using IronPdf
@inject IJSRuntime JS
@inject InvoiceService InvoiceService
// Install via NuGet: Install-Package IronPdf
<h3>Invoice #@InvoiceId</h3>
<button @onclick="DownloadInvoice">Download PDF</button>
@code {
[Parameter]
public int InvoiceId { get; set; }
private async Task DownloadInvoice()
{
var invoice = await InvoiceService.GetInvoice(InvoiceId);
var lineItems = string.Join("",
invoice.Items.Select(i => $@"
<tr>
<td>{i.Description}</td>
<td>{i.Quantity}</td>
<td>${i.UnitPrice:F2}</td>
<td>${i.Total:F2}</td>
</tr>"));
var html = $@"
<html>
<head>
<style>
body {{ font-family: Arial; padding: 40px; }}
table {{ width: 100%; border-collapse: collapse; }}
th, td {{ border: 1px solid #ddd; padding: 10px; }}
th {{ background: #f5f5f5; }}
.total {{ font-size: 1.2em; font-weight: bold; }}
</style>
</head>
<body>
<h1>Invoice #{invoice.Number}</h1>
<p>Date: {invoice.Date:MMMM dd, yyyy}</p>
<p>Customer: {invoice.CustomerName}</p>
<table>
<tr>
<th>Description</th>
<th>Qty</th>
<th>Price</th>
<th>Total</th>
</tr>
{lineItems}
</table>
<p class='total'>Total: ${invoice.Total:F2}</p>
</body>
</html>";
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
var pdf = renderer.RenderHtmlAsPdf(html);
await JS.InvokeVoidAsync("downloadFile", $"invoice-{InvoiceId}.pdf", pdf.BinaryData);
}
}
How Do I Add Headers and Footers?
Consistent branding across PDF pages:
@code {
private async Task GenerateReportWithBranding()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
HtmlFragment = @"
<div style='text-align:center; font-size:10px;'>
Company Name | Confidential
</div>",
DrawDividerLine = true
};
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
HtmlFragment = @"
<div style='text-align:center; font-size:10px;'>
Page {page} of {total-pages}
</div>"
};
var pdf = renderer.RenderHtmlAsPdf(reportHtml);
await JS.InvokeVoidAsync("downloadFile", "report.pdf", pdf.BinaryData);
}
}
How Do I Handle Large PDFs?
Async generation for long-running operations:
@page "/reports/large"
@using IronPdf
@inject IJSRuntime JS
// Install via NuGet: Install-Package IronPdf
<button @onclick="GenerateLargeReport" disabled="@isGenerating">
@(isGenerating ? "Generating..." : "Generate Large Report")
</button>
@if (isGenerating)
{
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
}
@code {
private bool isGenerating = false;
private async Task GenerateLargeReport()
{
isGenerating = true;
StateHasChanged();
try
{
// Generate on background thread
var pdfBytes = await Task.Run(() =>
{
var renderer = new ChromePdfRenderer();
// Generate large content
var html = GenerateLargeHtml();
var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
});
await JS.InvokeVoidAsync("downloadFile", "large-report.pdf", pdfBytes);
}
finally
{
isGenerating = false;
StateHasChanged();
}
}
}
Use Task.Run to avoid blocking the UI thread.
How Do I Use Dependency Injection?
Register IronPDF services properly:
// Program.cs
builder.Services.AddScoped<IPdfService, PdfService>();
// Services/PdfService.cs
using IronPdf;
// Install via NuGet: Install-Package IronPdf
public interface IPdfService
{
byte[] GenerateFromHtml(string html);
byte[] GenerateFromUrl(string url);
}
public class PdfService : IPdfService
{
private readonly ChromePdfRenderer _renderer;
public PdfService()
{
_renderer = new ChromePdfRenderer();
_renderer.RenderingOptions.MarginTop = 20;
_renderer.RenderingOptions.MarginBottom = 20;
}
public byte[] GenerateFromHtml(string html)
{
var pdf = _renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
public byte[] GenerateFromUrl(string url)
{
var pdf = _renderer.RenderUrlAsPdf(url);
return pdf.BinaryData;
}
}
Inject and use:
@inject IPdfService PdfService
@code {
private async Task Generate()
{
var bytes = PdfService.GenerateFromHtml("<h1>Hello</h1>");
await JS.InvokeVoidAsync("downloadFile", "doc.pdf", bytes);
}
}
How Do I Preview Before Download?
Display PDF in browser before downloading:
@page "/preview"
@using IronPdf
@inject IJSRuntime JS
// Install via NuGet: Install-Package IronPdf
<button @onclick="PreviewPdf">Preview PDF</button>
@if (!string.IsNullOrEmpty(pdfDataUrl))
{
<iframe src="@pdfDataUrl" style="width:100%; height:600px;"></iframe>
<button @onclick="Download">Download</button>
}
@code {
private string pdfDataUrl;
private byte[] pdfBytes;
private async Task PreviewPdf()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Preview</h1>");
pdfBytes = pdf.BinaryData;
// Create data URL for preview
var base64 = Convert.ToBase64String(pdfBytes);
pdfDataUrl = $"data:application/pdf;base64,{base64}";
}
private async Task Download()
{
await JS.InvokeVoidAsync("downloadFile", "document.pdf", pdfBytes);
}
}
Quick Reference
| Scenario | Approach |
|---|---|
| Blazor Server | Direct IronPDF calls in components |
| Blazor WebAssembly | API endpoint that returns PDF bytes |
| File download | JS interop with blob URL |
| Preview | Embed base64 in iframe |
| Large files | Use Task.Run for async |
Blazor Server makes PDF generation simple—just call the library. WebAssembly needs an API layer but keeps your server doing the heavy lifting.
For more Blazor integration examples, see the IronPDF Blazor 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)