PdfPecker.Razor
1.23.0
dotnet add package PdfPecker.Razor --version 1.23.0
NuGet\Install-Package PdfPecker.Razor -Version 1.23.0
<PackageReference Include="PdfPecker.Razor" Version="1.23.0" />
<PackageVersion Include="PdfPecker.Razor" Version="1.23.0" />
<PackageReference Include="PdfPecker.Razor" />
paket add PdfPecker.Razor --version 1.23.0
#r "nuget: PdfPecker.Razor, 1.23.0"
#:package PdfPecker.Razor@1.23.0
#addin nuget:?package=PdfPecker.Razor&version=1.23.0
#tool nuget:?package=PdfPecker.Razor&version=1.23.0
PdfPecker.Razor
Razor components (.razor files) to PDF with PdfPecker. Write a document
template as a Razor component, with typed parameters, @foreach, child components, layouts and dependency injection,
and get a PDF from it in one call. The component is rendered to HTML with Microsoft's HtmlRenderer, the static
renderer ASP.NET Core uses for server-side rendering, and PdfPecker converts the HTML. There is no browser, no
ASP.NET Core host and no JavaScript, so it works the same in a console app, a background worker or a web app.
dotnet add package PdfPecker.Razor
PdfPecker.Razor is released together with PdfPecker and always has the same version. It depends on the PdfPecker
release of that version and on Microsoft.AspNetCore.Components.Web of the app's .NET version (8.0, 9.0 or 10.0), which
holds HtmlRenderer.
The project that holds the .razor files uses the Razor SDK, so they compile to C# classes:
<Project Sdk="Microsoft.NET.Sdk.Razor">
An invoice template, Invoice.razor:
@using System.Globalization
<!DOCTYPE html>
<html>
<head>
<style>
@@page { size: A4; margin: 20mm; }
td.amount { text-align: right; }
</style>
</head>
<body>
<h1>Invoice @Data.Number</h1>
<table>
@foreach (var line in Data.Lines)
{
<tr><td>@line.Description</td><td class="amount">@line.Amount.ToString("N2", CultureInfo.InvariantCulture)</td></tr>
}
</table>
</body>
</html>
@code {
[Parameter, EditorRequired] public InvoiceModel Data { get; set; } = default!;
}
and the code that turns it into a PDF:
using PdfPecker;
using PdfPecker.Razor;
var parameters = new Dictionary<string, object?> { [nameof(Invoice.Data)] = invoice };
byte[] pdf = await RazorToPdf.ConvertAsync<Invoice>(parameters);
// Every PdfPecker option works: page size, fonts, PDF/A, signatures, e-invoices
byte[] archived = await RazorToPdf.ConvertAsync<Invoice>(parameters, new PdfOptions { Conformance = PdfConformance.PdfA2b });
// Straight to an ASP.NET Core response, with the request's services for components that @inject them
app.MapGet("/invoice/{id}", async (int id, HttpContext context, InvoiceStore store) =>
{
var parameters = new Dictionary<string, object?> { ["Data"] = await store.LoadAsync(id) };
context.Response.ContentType = "application/pdf";
await RazorToPdf.ConvertAsync<Invoice>(context.Response.Body, parameters, services: context.RequestServices,
cancellationToken: context.RequestAborted);
});
// The HTML the component renders to, to open in a browser while working on the template
string html = await RazorToPdf.RenderHtmlAsync<Invoice>(parameters);
A complete example, with a layout component and a child component for the line table, is in samples/PdfPecker.Razor.Sample.
What works
- Typed templates. Parameters are C# properties, so a misspelt field or a wrong type is a compile error, and the
editor completes them. A parameter name that has no
[Parameter]property fails withInvalidOperationException. - Child components and layouts. Split a document into components (address block, line table, totals) and pass
data down with parameters or
CascadingValue. A layout is a component withChildContent, or aLayoutComponentBaseused throughLayoutView. - Dependency injection.
@injectand constructor injection take services from theIServiceProvidergiven asservices, such as an ASP.NET Core app'sRequestServices. AnILoggerFactoryamong them receives the renderer's log. Withoutservicesthere are none, and a component that injects one fails with a message that names it. - Asynchronous data. Work in
OnInitializedAsyncandOnParametersSetAsyncis awaited before the HTML is taken. - Everything PdfPecker does. The HTML goes to
HtmlToPdf, so the CSS,@pagerules, running headers and footers, page numbers, fonts andPdfOptions(PDF/A, PDF/UA, signing, encryption, e-invoices) are all available. Relative image paths resolve againstPdfOptions.BaseDirectory, and other resources load throughPdfOptions.ResourceResolver. - Same output.
ConvertAsyncgives byte for byte the PDF thatHtmlToPdf.ConvertAsyncgives for the HTML ofRenderHtmlAsync, so a fixedCreationDatemakes the output reproducible.
Safety
Razor encodes every value it writes into the markup, so names, addresses and other caller data cannot become HTML. Only
a MarkupString inserts HTML as it is, so use it only for markup you trust. PdfPecker runs no scripts and fetches
nothing over the network, as always.
Each call renders in an HtmlRenderer of its own, which is disposed with the component when the call returns, so
nothing is kept between conversions and parallel calls are safe. An exception that the component throws reaches the
caller unchanged. The cancellation token stops the PDF conversion; the component's own rendering is not interrupted, so
pass the token to it as a parameter when it does slow work of its own.
Components render once and statically, as in ASP.NET Core's static server-side rendering: event handlers, timers and
JavaScript interop do not run, and services that only an ASP.NET Core host provides, such as NavigationManager, are
available only when they are in services.
Trimming and Native AOT
PdfPecker.Razor is trimmable and marked AOT-compatible, and CI publishes the sample with Native AOT and checks the PDF it
writes. Microsoft's Microsoft.AspNetCore.Components itself reports trim warnings from its own code, folded into one
IL2104 warning when you publish trimmed or with Native AOT; Blazor WebAssembly turns those warnings off for the same
reason. The component types are kept all the same, because each one reaches the renderer through an annotated
parameter. The sample's project file shows how to ignore only that assembly's warning, so a warning from anywhere else
still fails the publish.
@layout and LayoutView set a layout's type by reflection, which the trim analyzer warns about in your own
project. A layout written as a component with ChildContent, as in the sample, avoids that.
Performance
Rendering the sample's invoice component with 20 lines takes about 0.03 ms, against about 0.5 ms for PdfPecker's conversion of the HTML, measured on a 4-core cloud VM on .NET 10. The package adds about 1.4 MB of Microsoft assemblies to an app that does not already have ASP.NET Core; an ASP.NET Core app usually takes them from its shared framework.
Source
PdfPecker.Razor lives in the PdfPecker repository under
src/PdfPecker.Razor, with its tests in tests/PdfPecker.Razor.Tests. Issues and pull requests go there.
License
MIT, like PdfPecker.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.AspNetCore.Components.Web (>= 10.0.14)
- PdfPecker (>= 1.23.0)
-
net8.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.33)
- PdfPecker (>= 1.23.0)
-
net9.0
- Microsoft.AspNetCore.Components.Web (>= 9.0.22)
- PdfPecker (>= 1.23.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.23.0 | 83 | 11/4/2026 |
What changed in each release: https://pdfpecker.net/docs/changelog/