Skip to footer content
USING IRONPDF

A Developer's Guide to Building a Blazor PDF Viewer

Integrating a robust PDF viewer into a Blazor application can seem challenging, but it's a common requirement for business applications that need to generate invoices, reports, or other official documents. This guide provides a practical, peer-to-peer walkthrough on how to build a fully functional PDF viewer in a server-side Blazor application.

We'll move straight into the code, focusing on how to render PDFs from various sources like URLs and even directly from your Razor components. You'll also learn how to customize the PDF output and explore different ways to deliver the final document to your users, giving you a solid foundation for any PDF-related task in your Blazor projects.

Why Choose a Library for PDF Rendering in Blazor?

While you could try to build PDF rendering from scratch, a dedicated library like IronPDF handles the complexities of the PDF specification and browser rendering quirks for you. Its powerful Chrome rendering engine ensures that what you see in a modern web browser is exactly what you get in your PDF. This pixel-perfect conversion of HTML, CSS, and JavaScript saves immense development time and effort.

IronPDF is built for .NET developers, providing an intuitive API that feels right at home in a C# project. This makes tasks like HTML to PDF conversion, PDF customization, and document manipulation straightforward. While other libraries like Aspose and SyncFusion exist, IronPDF's focus on ease of use and rendering accuracy makes it a strong choice for Blazor applications.

How Do You Render a PDF from a URL in Blazor?

One of the most common needs is to convert an existing web page into a PDF. This is incredibly useful for archiving web content or generating a PDF version of a live report. With IronPDF, this process is accomplished with just a couple of lines of C#.

First, ensure you have IronPDF installed in your project. You can add it via the NuGet Package Manager by searching for IronPdf or by using the Package Manager Console:

Install-Package IronPdf

Next, in your Razor component (.razor file), you can create a method to handle the PDF generation. This method will use the ChromePdfRenderer to fetch a URL and convert its content into a PDF document.

Code Example: URL to PDF

private async Task ViewCustomizedPdf()
{
    var renderer = new ChromePdfRenderer();

    // Create a set of rendering options to customize the PDF output.
    var options = new ChromePdfRenderOptions
    {
        // Set a custom paper size, useful for invoices or specific document types.
        PaperSize = PdfPaperSize.A4,

        // Adjust margins to control the layout of the content.
        MarginTop = 20,  // in millimeters
        MarginBottom = 20,

        // Add a text header to every page.
        TextHeader = new TextHeaderFooter
        {
            CenterText = "Report Generated from Blazor Application",
            Font = PdfFont.Helvetica,
            FontSize = 12
        },

        // Add a footer with dynamic content like page numbers and date.
        // The {page} and {total-pages} fields are automatically populated.
        TextFooter = new TextHeaderFooter
        {
            LeftText = "{date}",
            RightText = "Page {page} of {total-pages}",
            Font = PdfFont.Courier,
            FontSize = 10
        }
    };

    // Apply the custom options during the rendering process.
    var pdf = await renderer.RenderUrlAsPdfAsync("https://getbootstrap.com/docs/5.3/examples/dashboard/", options);

    pdfDataUri = $"data:application/pdf;base64,{Convert.ToBase64String(pdf.BinaryData)}";
}
private async Task ViewCustomizedPdf()
{
    var renderer = new ChromePdfRenderer();

    // Create a set of rendering options to customize the PDF output.
    var options = new ChromePdfRenderOptions
    {
        // Set a custom paper size, useful for invoices or specific document types.
        PaperSize = PdfPaperSize.A4,

        // Adjust margins to control the layout of the content.
        MarginTop = 20,  // in millimeters
        MarginBottom = 20,

        // Add a text header to every page.
        TextHeader = new TextHeaderFooter
        {
            CenterText = "Report Generated from Blazor Application",
            Font = PdfFont.Helvetica,
            FontSize = 12
        },

        // Add a footer with dynamic content like page numbers and date.
        // The {page} and {total-pages} fields are automatically populated.
        TextFooter = new TextHeaderFooter
        {
            LeftText = "{date}",
            RightText = "Page {page} of {total-pages}",
            Font = PdfFont.Courier,
            FontSize = 10
        }
    };

    // Apply the custom options during the rendering process.
    var pdf = await renderer.RenderUrlAsPdfAsync("https://getbootstrap.com/docs/5.3/examples/dashboard/", options);

    pdfDataUri = $"data:application/pdf;base64,{Convert.ToBase64String(pdf.BinaryData)}";
}
Private Async Function ViewCustomizedPdf() As Task
	Dim renderer = New ChromePdfRenderer()

	' Create a set of rendering options to customize the PDF output.
	Dim options = New ChromePdfRenderOptions With {
		.PaperSize = PdfPaperSize.A4,
		.MarginTop = 20,
		.MarginBottom = 20,
		.TextHeader = New TextHeaderFooter With {
			.CenterText = "Report Generated from Blazor Application",
			.Font = PdfFont.Helvetica,
			.FontSize = 12
		},
		.TextFooter = New TextHeaderFooter With {
			.LeftText = "{date}",
			.RightText = "Page {page} of {total-pages}",
			.Font = PdfFont.Courier,
			.FontSize = 10
		}
	}

	' Apply the custom options during the rendering process.
	Dim pdf = Await renderer.RenderUrlAsPdfAsync("https://getbootstrap.com/docs/5.3/examples/dashboard/", options)

	pdfDataUri = $"data:application/pdf;base64,{Convert.ToBase64String(pdf.BinaryData)}"
End Function
$vbLabelText   $csharpLabel

How it Works

  1. We create an instance of ChromePdfRenderer, which is the core class for HTML-to-PDF conversion.
  2. The RenderUrlAsPdfAsync method asynchronously fetches the HTML, CSS, and JavaScript from the specified URL and renders it into a PdfDocument object.
  3. The pdf.BinaryData property returns the raw byte array of the generated PDF.
  4. We convert this byte array into a Base64 string and format it as a Data URI.
  5. This Data URI is then bound to the src attribute of an <iframe>, prompting the browser's native PDF viewer to display the document.

How Can You Customize the PDF Generation?

Standard PDF conversion is great, but real-world applications often require more control. You might need to add headers and footers with page numbers, adjust margins, or change the paper size. IronPDF exposes these options through the ChromePdfRenderOptions class.

By configuring these options, you can tailor the PDF output to match specific requirements, such as formatting for professional reports or invoices.

Code Example: Custom Headers and Footers

Let's expand on the previous example to include a custom header and footer. The header will display the document title, and the footer will show the current date and page number.

private async Task ViewCustomizedPdf()
{
    var renderer = new ChromePdfRenderer();

    // Create a set of rendering options to customize the PDF output.
    var options = new ChromePdfRenderOptions
    {
        // Set a custom paper size, useful for invoices or specific document types.
        PaperSize = PdfPaperSize.A4,

        // Adjust margins to control the layout of the content.
        MarginTop = 20,  // in millimeters
        MarginBottom = 20,

        // Add a text header to every page.
        TextHeader = new TextHeaderFooter
        {
            CenterText = "Report Generated from Blazor Application",
            Font = PdfFont.Helvetica,
            FontSize = 12
        },

        // Add a footer with dynamic content like page numbers and date.
        // The {page} and {total-pages} fields are automatically populated.
        TextFooter = new TextHeaderFooter
        {
            LeftText = "{date}",
            RightText = "Page {page} of {total-pages}",
            Font = PdfFont.Courier,
            FontSize = 10
        }
    };

    // Apply the custom options during the rendering process.
    var pdf = await renderer.RenderUrlAsPdfAsync("https://getbootstrap.com/docs/5.3/examples/dashboard/", options);

    pdfDataUri = $"data:application/pdf;base64,{Convert.ToBase64String(pdf.BinaryData)}";
}
private async Task ViewCustomizedPdf()
{
    var renderer = new ChromePdfRenderer();

    // Create a set of rendering options to customize the PDF output.
    var options = new ChromePdfRenderOptions
    {
        // Set a custom paper size, useful for invoices or specific document types.
        PaperSize = PdfPaperSize.A4,

        // Adjust margins to control the layout of the content.
        MarginTop = 20,  // in millimeters
        MarginBottom = 20,

        // Add a text header to every page.
        TextHeader = new TextHeaderFooter
        {
            CenterText = "Report Generated from Blazor Application",
            Font = PdfFont.Helvetica,
            FontSize = 12
        },

        // Add a footer with dynamic content like page numbers and date.
        // The {page} and {total-pages} fields are automatically populated.
        TextFooter = new TextHeaderFooter
        {
            LeftText = "{date}",
            RightText = "Page {page} of {total-pages}",
            Font = PdfFont.Courier,
            FontSize = 10
        }
    };

    // Apply the custom options during the rendering process.
    var pdf = await renderer.RenderUrlAsPdfAsync("https://getbootstrap.com/docs/5.3/examples/dashboard/", options);

    pdfDataUri = $"data:application/pdf;base64,{Convert.ToBase64String(pdf.BinaryData)}";
}
Private Async Function ViewCustomizedPdf() As Task
	Dim renderer = New ChromePdfRenderer()

	' Create a set of rendering options to customize the PDF output.
	Dim options = New ChromePdfRenderOptions With {
		.PaperSize = PdfPaperSize.A4,
		.MarginTop = 20,
		.MarginBottom = 20,
		.TextHeader = New TextHeaderFooter With {
			.CenterText = "Report Generated from Blazor Application",
			.Font = PdfFont.Helvetica,
			.FontSize = 12
		},
		.TextFooter = New TextHeaderFooter With {
			.LeftText = "{date}",
			.RightText = "Page {page} of {total-pages}",
			.Font = PdfFont.Courier,
			.FontSize = 10
		}
	}

	' Apply the custom options during the rendering process.
	Dim pdf = Await renderer.RenderUrlAsPdfAsync("https://getbootstrap.com/docs/5.3/examples/dashboard/", options)

	pdfDataUri = $"data:application/pdf;base64,{Convert.ToBase64String(pdf.BinaryData)}"
End Function
$vbLabelText   $csharpLabel

Key Customization Features

  • Page Layout: Control properties like PaperSize, Orientation, and margins (MarginTop, MarginBottom, etc.) for precise document layout.
  • Headers and Footers: The TextHeader and TextFooter properties allow you to add text and dynamic fields. You can also use HtmlHeader and HtmlFooter for more complex, HTML-based content. For more details, see the Header and Footer guide.
  • Rendering Delay: Use RenderDelay to wait for JavaScript animations or async data loading on a page to complete before generating the PDF.
  • CSS Media Type: Set CssMediaType to PdfCssMediaType.Print to apply print-specific CSS styles, just like a browser's print function.

What's the Best Way to Serve a PDF to the User?

Displaying the PDF in an <iframe> is convenient for a quick preview, but it's not always the best user experience. Sometimes, you want the user to download the file directly. This can be achieved in Blazor by streaming the file from a controller endpoint or a minimal API.

Here's how you can create a method that generates a PDF and returns it as a downloadable file.

Code Example: Triggering a File Download

// This method would typically be in a Blazor @page, a controller, or a minimal API endpoint.
// For this example, let's assume it's a method that can be called to get a FileResult.

public async Task&lt;IResult&gt; DownloadPdf()
{
    var renderer = new ChromePdfRenderer();
    var pdf = await renderer.RenderHtmlAsPdfAsync("&lt;h1&gt;PDF Report&lt;/h1&gt;&lt;p&gt;This is a downloadable PDF generated from a Blazor app.&lt;/p&gt;");

    // Return the PDF as a file stream.
    // This prompts the browser's "Save As" dialog.
    return Results.File(pdf.BinaryData, "application/pdf", "MyReport.pdf");
}
// This method would typically be in a Blazor @page, a controller, or a minimal API endpoint.
// For this example, let's assume it's a method that can be called to get a FileResult.

public async Task&lt;IResult&gt; DownloadPdf()
{
    var renderer = new ChromePdfRenderer();
    var pdf = await renderer.RenderHtmlAsPdfAsync("&lt;h1&gt;PDF Report&lt;/h1&gt;&lt;p&gt;This is a downloadable PDF generated from a Blazor app.&lt;/p&gt;");

    // Return the PDF as a file stream.
    // This prompts the browser's "Save As" dialog.
    return Results.File(pdf.BinaryData, "application/pdf", "MyReport.pdf");
}
' This method would typically be in a Blazor @page, a controller, or a minimal API endpoint.
' For this example, let's assume it's a method that can be called to get a FileResult.

Public Task As async
Private IResult And gt
'INSTANT VB WARNING: The following constructor is declared outside of its associated class:
'ORIGINAL LINE: DownloadPdf()
Private Sub New()
	Dim renderer = New ChromePdfRenderer()
	Dim pdf = Await renderer.RenderHtmlAsPdfAsync("&lt;h1&gt;PDF Report&lt;/h1&gt;&lt;p&gt;This is a downloadable PDF generated from a Blazor app.&lt;/p&gt;")

	' Return the PDF as a file stream.
	' This prompts the browser's "Save As" dialog.
	Return Results.File(pdf.BinaryData, "application/pdf", "MyReport.pdf")
End Sub
$vbLabelText   $csharpLabel

This approach is often cleaner, as it doesn't embed the PDF within your application's UI and gives the user a portable file they can save and share. You can learn more about saving and exporting options from our documentation on saving PDFs.

How Do You Generate a PDF from a Razor Component?

Perhaps the most powerful feature for Blazor developers is the ability to generate a PDF directly from a Razor component. This allows you to create dynamic, data-driven documents using the same component model you already know. You can design an invoice, a ticket, or a report as a component, pass data to it, render it to an HTML string, and then convert that string to a PDF.

This requires a helper service to render the component to HTML outside of the normal page rendering context.

1. Create the Razor Component for the PDF

First, create your component. This could be an invoice template, for example.

Invoice.razor

@* This component is designed specifically for PDF rendering. *@
<h3>Invoice #@InvoiceNumber</h3>
<p>Date: @DateTime.Now.ToShortDateString()</p>

<table class="table">
    <thead>
        <tr><th>Item</th><th>Price</th></tr>
    </thead>
    <tbody>
        @foreach (var item in LineItems)
        {
            <tr><td>@item.Key</td><td>@item.Value.ToString("C")</td></tr>
        }
    </tbody>
</table>

@code {
    [Parameter]
    public int InvoiceNumber { get; set; }

    [Parameter]
    public Dictionary<string, decimal> LineItems { get; set; } = new();
}
@* This component is designed specifically for PDF rendering. *@
<h3>Invoice #@InvoiceNumber</h3>
<p>Date: @DateTime.Now.ToShortDateString()</p>

<table class="table">
    <thead>
        <tr><th>Item</th><th>Price</th></tr>
    </thead>
    <tbody>
        @foreach (var item in LineItems)
        {
            <tr><td>@item.Key</td><td>@item.Value.ToString("C")</td></tr>
        }
    </tbody>
</table>

@code {
    [Parameter]
    public int InvoiceNumber { get; set; }

    [Parameter]
    public Dictionary<string, decimal> LineItems { get; set; } = new();
}
Friend * This component is designed specifically for PDF rendering. * (Of h3) Invoice #InvoiceNumber</h3> (Of p) [Date]
	Inherits DateTime.Now.ToShortDateString()</p> <table class="table">(Of thead) (Of tr)(Of th) Item</th>(Of th) Price</th></tr> </thead> (Of tbody) foreach(var item in LineItems)

			(Of tr)(Of td) item.Key</td>(Of td) item.Value.ToString("C")</td></tr>
End Class
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'	</tbody> </table> @code
'	{
'	[Parameter] public int InvoiceNumber
'	{
'		get;
'		set;
'	}
'
'	[Parameter] public Dictionary<string, decimal> LineItems
'	{
'		get;
'		set;
'	} = New();
'}
$vbLabelText   $csharpLabel

2. Render the Component to an HTML String and Convert to PDF

Next, use this component in your main page to generate the PDF.

private async Task GeneratePdfFromComponent()
{
    // This is a simplified example. In a real app, you'd use a service
    // to render the component to an HTML string.
    // For the sake of this tutorial, we'll build the HTML manually
    // to simulate rendering a component.
    var invoiceData = new Dictionary&lt;string, decimal&gt;
    {
        { "IronPDF License", 799m },
        { "Support Package", 299m }
    };

    var html = $@"
        &lt;h1&gt;Invoice #123&lt;/h1&gt;
        &lt;p&gt;Date: {DateTime.Now.ToShortDateString()}&lt;/p&gt;
        &lt;table border='1' width='100%'&gt;
            &lt;thead&gt;
                &lt;tr&gt;&lt;th&gt;Item&lt;/th&gt;&lt;th&gt;Price&lt;/th&gt;&lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;";

    foreach (var item in invoiceData)
    {
        html += $"&lt;tr&gt;&lt;td&gt;{item.Key}&lt;/td&gt;&lt;td&gt;${item.Value}&lt;/td&gt;&lt;/tr&gt;";
    }

    html += "&lt;/tbody&gt;&lt;/table&gt;";

    var renderer = new ChromePdfRenderer();
    var pdf = await renderer.RenderHtmlAsPdfAsync(html);

    // Now you can either display it in an iframe or save it.
    await pdf.SaveAsAsync("GeneratedInvoice.pdf");
    pdfDataUri = $"data:application/pdf;base64,{Convert.ToBase64String(pdf.BinaryData)}";
}
private async Task GeneratePdfFromComponent()
{
    // This is a simplified example. In a real app, you'd use a service
    // to render the component to an HTML string.
    // For the sake of this tutorial, we'll build the HTML manually
    // to simulate rendering a component.
    var invoiceData = new Dictionary&lt;string, decimal&gt;
    {
        { "IronPDF License", 799m },
        { "Support Package", 299m }
    };

    var html = $@"
        &lt;h1&gt;Invoice #123&lt;/h1&gt;
        &lt;p&gt;Date: {DateTime.Now.ToShortDateString()}&lt;/p&gt;
        &lt;table border='1' width='100%'&gt;
            &lt;thead&gt;
                &lt;tr&gt;&lt;th&gt;Item&lt;/th&gt;&lt;th&gt;Price&lt;/th&gt;&lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;";

    foreach (var item in invoiceData)
    {
        html += $"&lt;tr&gt;&lt;td&gt;{item.Key}&lt;/td&gt;&lt;td&gt;${item.Value}&lt;/td&gt;&lt;/tr&gt;";
    }

    html += "&lt;/tbody&gt;&lt;/table&gt;";

    var renderer = new ChromePdfRenderer();
    var pdf = await renderer.RenderHtmlAsPdfAsync(html);

    // Now you can either display it in an iframe or save it.
    await pdf.SaveAsAsync("GeneratedInvoice.pdf");
    pdfDataUri = $"data:application/pdf;base64,{Convert.ToBase64String(pdf.BinaryData)}";
}
Private Async Function GeneratePdfFromComponent() As Task
	' This is a simplified example. In a real app, you'd use a service
	' to render the component to an HTML string.
	' For the sake of this tutorial, we'll build the HTML manually
	' to simulate rendering a component.
	Dim invoiceData = New Dictionary And lt
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'	string, decimal&gt
'	{
'		{
'			"IronPDF License", 799m
'		}
'	   ,
'		{
'			"Support Package", 299m }
'	};
'
'	var html = string.Format("
'        &lt;h1&gt;Invoice #123&lt;/h1&gt;
'        &lt;p&gt;Date: {0}&lt;/p&gt;
'        &lt;table border='1' width='100%'&gt;
'            &lt;thead&gt;
'                &lt;tr&gt;&lt;th&gt;Item&lt;/th&gt;&lt;th&gt;Price&lt;/th&gt;&lt;/tr&gt;
'            &lt;/thead&gt;
'            &lt;tbody&gt;", DateTime.Now.ToShortDateString());
'
'	foreach (var item in invoiceData)
'	{
'		html += string.Format("&lt;tr&gt;&lt;td&gt;{0}&lt;/td&gt;&lt;td&gt;${1}&lt;/td&gt;&lt;/tr&gt;", item.Key, item.Value);
'	}
'
'	html += "&lt;/tbody&gt;&lt;/table&gt;";
'
'	var renderer = New ChromePdfRenderer();
'	var pdf = await renderer.RenderHtmlAsPdfAsync(html);
'
'	' Now you can either display it in an iframe or save it.
'	await pdf.SaveAsAsync("GeneratedInvoice.pdf");
'	pdfDataUri = string.Format("data:application/pdf;base64,{0}", Convert.ToBase64String(pdf.BinaryData));
'}
$vbLabelText   $csharpLabel

This approach unlocks the full power of Blazor's component model for PDF generation, enabling you to create complex, reusable, and easily maintainable document templates.

Viewing a PDF Generated in a Blazor app in the browser. This PDF has been created by IronPDF and sent to the browser as a base64 string. A PDF generated by IronPDF from a sample Blazor component, demonstrating how web content is accurately rendered and displayed in the browser.

What Other PDF Features Can Be Used in Blazor?

Beyond viewing and generating, a full-featured PDF library can handle many other tasks within your Blazor app. With IronPDF, you can also:

These capabilities allow you to build comprehensive document workflows directly within your .NET applications.

Conclusion

Integrating a PDF viewer and generator into a server-side Blazor application is a straightforward process with a library like IronPDF. We've seen how to move beyond basic HTML to PDF conversion and leverage the power of ChromePdfRenderOptions for customization, serve files for a better user experience, and even generate dynamic documents directly from your Razor components.

By using these techniques, you can add powerful document generation features to your Blazor projects, saving time and creating professional, pixel-perfect PDFs with ease.

Start using IronPDF in your project today with a free trial.

First Step:
green arrow pointer

Please noteAspose and SyncFusion are registered trademarks of their respective owners. This site is not affiliated with, endorsed by, or sponsored by Aspose or SyncFusion. All product names, logos, and brands are the property of their respective owners. Comparisons are provided for informational purposes only and are based on publicly available information at the time of writing.

Frequently Asked Questions

How can I integrate a PDF viewer in a server-side Blazor application?

You can integrate a PDF viewer in a server-side Blazor application using the IronPDF library. This involves generating the PDF and then displaying it using an iframe with a Base64 Data URI. For instance, convert the PDF binary data to a Base64 string and set it as the src of the iframe.

Can I render PDFs from external URLs in Blazor?

Yes, you can render PDFs from external URLs in Blazor using IronPDF. Use the RenderUrlAsPdf method to fetch the HTML content from the URL and convert it into a PDF document.

How do I customize PDF output with headers and footers in my Blazor app?

To customize PDF output with headers and footers in a Blazor app, use the ChromePdfRenderOptions class. You can set properties like TextHeader.CenterText and TextFooter.RightText to add custom text, including dynamic fields like {page} and {total-pages}.

What are the methods for delivering a PDF to a user in a Blazor application?

In a Blazor application, you can deliver a PDF by embedding it within an iframe using a Base64 Data URI for inline viewing, or by implementing a file download endpoint using FileResult to allow users to save the PDF to their device.

How can I generate a PDF from HTML in a Blazor application?

In a Blazor application, you can generate a PDF from HTML using IronPDF's RenderHtmlAsPdfAsync method. First, ensure the HTML content is properly formatted, then pass it to this method to create a PDF document.

Is it possible for a Blazor application to support complex JavaScript when rendering a PDF?

Yes, IronPDF supports complex JavaScript while rendering a PDF in a Blazor application. You can adjust the RenderDelay in ChromePdfRenderOptions to ensure all JavaScript executes before capturing the page as a PDF.

How do I set the paper size and margins when generating a PDF in C#?

To set the paper size and margins when generating a PDF in C#, use the ChromePdfRenderOptions class. Specify the PaperSize and adjust the MarginTop, MarginBottom, MarginLeft, and MarginRight properties as needed.

Jacob Mellor, Chief Technology Officer @ Team Iron
Chief Technology Officer

Jacob Mellor is Chief Technology Officer at Iron Software and a visionary engineer pioneering C# PDF technology. As the original developer behind Iron Software's core codebase, he has shaped the company's product architecture since its inception, transforming it alongside CEO Cameron Rimington into a 50+ person company serving NASA, Tesla, ...Read More