Skip to main content

Command Palette

Search for a command to run...

Building a Modern PDF Viewer in .NET MAUI with C#

Published
13 min read

Ever tried to stuff a PDF viewer into your .NET MAUI app and felt like you were hacking together duct tape and hope? You’re not alone. Native solutions are clunky, web-based viewers feel like a compromise, and building a PDF renderer from scratch is a dark rabbit hole. But the good news? With IronPDF and its new MAUI Viewer, you can finally treat PDF viewing as a first-class, cross-platform citizen.

Let’s roll up our sleeves and build a robust, user-friendly PDF viewer into your MAUI app – the kind you’d actually want to ship to users (or use yourself).


Why PDF Viewing in .NET MAUI Is Harder Than It Looks

Here’s the thing: PDF is a beast of a standard. Whether you’re building for Windows, macOS, iOS, or Android, you hit the same hurdles: - Native viewers (like Adobe Reader) are external, ugly, and disconnected from your app’s UI. - Web-based solutions need a browser control, which feels like a hack and often kills offline scenarios. - Most cross-platform .NET PDF libraries either don’t do viewing, or need you to write platform-specific wrappers. - DIY parsing? Don’t even think about it unless you love pain.

.NET MAUI promises “one codebase, all platforms.” But when it comes to PDFs, out-of-the-box support just isn’t there. And if you’re building document-heavy apps—think CRMs, contract reviews, or help systems—PDF viewing is table stakes.

That’s where IronPDF’s MAUI Viewer comes in.


What Makes IronPDF’s Viewer Different?

  • True native control: No browser, no JavaScript, no web view hacks.

  • Chromium-based rendering: Your PDFs look exactly like they do in Chrome—fonts, colors, layouts, the works.

  • All-in-one: Add a viewer to a page, bind it to a PDF source, get instant navigation, zoom, search, and more.

  • Cross-platform ready: Designed for MAUI, not shoehorned in from a Windows Forms or WPF world.

  • Dev-friendly: Use it in XAML or C#. Zero platform-check boilerplate, no interop headaches.

I’ve shipped real apps (think: document management systems and custom report viewers) using this control. It’s hands down the least painful PDF viewer I’ve dropped into a .NET UI.

Sound good? Let’s get building.


Getting Started: Setting Up IronPDF Viewer in MAUI

First, you’ll need the IronPDF Viewer NuGet package. You can grab it directly from the NuGet UI or with the CLI:

dotnet add package IronPdf.Viewer.Maui
// Or in Visual Studio: Install-Package IronPdf.Viewer.Maui

Pro tip: If you’re also doing PDF creation or manipulation, check out IronPDF’s full library for all the bells and whistles—merging, signing, stamping, etc.

Now, let’s wire up the viewer in your MAUI app.

1. Initialize the Viewer Once in MauiProgram.cs

Here’s how you register the viewer globally, so it’s ready everywhere:

using IronPdf.Viewer.Maui;
// Install via NuGet: Install-Package IronPdf.Viewer.Maui

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureIronPdfView(); // Registers the PDF viewer globally
        return builder.Build();
    }
}

What’s happening here? - ConfigureIronPdfView() sets up everything under the hood: dependency injection, rendering, and platform specifics. - Do this once. All pages in your app can now drop in the viewer control.

Want to add a license key? (You’ll need this for production to remove [[[watermark](https://ironpdf.com/java/blog/using-ironpdf-for-java/java-watermark-pdf-tutorial/)](https://ironpdf.com/java/how-to/custom-watermark/)](https://ironpdf.com/java/blog/using-ironpdf-for-java/java-watermark-pdf-tutorial/)s—more on that below.) Just pass it in:

builder.ConfigureIronPdfView("YOUR-LICENSE-KEY-HERE");

Adding a PDF Viewer to Your MAUI Pages

You’ve got options: XAML for markup fans, C# for the code-first crowd. Both work beautifully.

XAML: The Declarative Way

If your PDF source is static (think help files, terms, embedded docs), XAML is clean and tidy:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewer="clr-namespace:IronPdf.Viewer.Maui;assembly=IronPdf.Viewer.Maui"
             x:Class="MyApp.PdfViewerPage">

    <viewer:PdfViewer x:Name="pdfViewer"
                      Source="documents/manual.pdf"
                      Margin="10"
                      VerticalOptions="FillAndExpand"
                      HorizontalOptions="FillAndExpand"/>
</ContentPage>

Nice touch: The control behaves like any other MAUI control—so you can stack, grid, or style it to your heart’s content.

C#: The Dynamic, Code-Driven Approach

For scenarios where the PDF source changes at runtime (user picks a doc, downloads from server, etc.), build the viewer in C#:

using IronPdf.Viewer.Maui;
using Microsoft.Maui.Controls;

public class PdfViewerPage : ContentPage
{
    public PdfViewerPage(string pdfPath)
    {
        var pdfViewer = new PdfViewer
        {
            Source = pdfPath, // Or set later as needed
            Margin = new Thickness(10)
        };

        Content = pdfViewer;
    }
}

Mix and match: You can also bind the Source property to a ViewModel, making it easy to swap PDFs based on app logic.


Real-World: Loading PDFs from Everywhere

“Okay, but what if my PDFs aren’t just files on disk?”

That’s where IronPDF Viewer really shines. The Source property is flexible—it’ll accept: - File paths (local or bundled) - Embedded resources - Byte arrays (e.g., from a database or API) - Streams (for downloads, cloud blobs, etc.)

Let’s see some practical scenarios.

Loading a PDF from a Local File

Easy as:

pdfViewer.Source = "documents/manual.pdf";

On Windows: This path is relative to your app’s install folder.
On mobile: Use MAUI’s file APIs to get the right location.

Loading from a Byte Array (e.g., Database)

Suppose you fetched a PDF as a byte[] from SQL or a REST API:

// Load PDF bytes from your data source
byte[] pdfBytes = await LoadPdfFromDatabaseAsync(documentId);

// Show in viewer
pdfViewer.Source = pdfBytes;

Loading from a Stream (e.g., HTTP Download, Azure Blob)

Let’s say you need to display a PDF from a server or blob storage—no need to save it to disk:

using System.Net.Http;

using (var httpClient = new HttpClient())
using (var stream = await httpClient.GetStreamAsync("https://example.com/docs/contract.pdf"))
{
    pdfViewer.Source = stream;
}

Heads up: Keep the stream open until the viewer has loaded the PDF. Disposing too early = sad panda.

From Embedded Resources (Best for Offline Bundled Docs)

If you want to ship a manual or help file inside your app:

var assembly = typeof(PdfViewerPage).Assembly;
using (Stream resourceStream = assembly.GetManifestResourceStream("MyApp.Resources.manual.pdf"))
{
    pdfViewer.Source = resourceStream;
}

Pro tip: Use dotPeek or similar to check your resource names if you get a “resource not found” error.

Bonus: Loading from a Remote API with Auth

Often, PDFs are behind authentication. Here’s how you could load with a bearer token:

var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = 
    new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);

using (var stream = await httpClient.GetStreamAsync("https://api.yoursite.com/user/report.pdf"))
{
    pdfViewer.Source = stream;
}

Making the Viewer Feel Native: Layout and Gestures

What’s great about IronPDF’s MAUI Viewer is it acts just like any other MAUI control: - Drop it in a Grid, StackLayout, or custom container. - Set margins, paddings, or size constraints. - On desktops, mouse wheel and keyboard navigation work out-of-the-box. - On touch devices, you get pinch-to-zoom and swipe gestures for free.

Example: Embedding in a Grid with Other Controls

<Grid RowDefinitions="Auto,*">
    <StackLayout Orientation="Horizontal" Grid.Row="0">
        <Button Text="Previous" Clicked="OnPreviousPage" />
        <Button Text="Next" Clicked="OnNextPage" />
        <Label Text="{Binding CurrentPageInfo}" VerticalOptions="Center"/>
    </StackLayout>
    <viewer:PdfViewer x:Name="pdfViewer" Grid.Row="1"/>
</Grid>

And in your code-behind:

private void OnPreviousPage(object sender, EventArgs e)
{
    pdfViewer.GoToPreviousPage();
}

private void OnNextPage(object sender, EventArgs e)
{
    pdfViewer.GoToNextPage();
}

Toolbar Customization: Hide, Show, or Roll Your Own

Let’s be honest: no one wants a bloated toolbar if they don’t need it. IronPDF Viewer lets you toggle features to match your app’s vibe.

Hiding Built-In Features

pdfViewer.ShowThumbnails = false;      // Hide thumbnail sidebar
pdfViewer.ShowSearchBox = false;       // Hide search bar
pdfViewer.ShowZoomControls = false;    // Hide zoom in/out
pdfViewer.ShowPageNavigation = false;  // Hide next/prev buttons

Use this to strip things down for a help viewer, TOS popup, or read-only doc scenario.

Rolling Your Own Toolbar

Need a branded toolbar or custom interaction? Hide the built-in one and add your own MAUI controls:

pdfViewer.ShowToolbar = false;

var nextBtn = new Button { Text = "Next" };
nextBtn.Clicked += (s, e) => pdfViewer.GoToNextPage();

var prevBtn = new Button { Text = "Previous" };
prevBtn.Clicked += (s, e) => pdfViewer.GoToPreviousPage();

var zoomInBtn = new Button { Text = "Zoom In" };
zoomInBtn.Clicked += (s, e) => pdfViewer.ZoomIn();

var zoomOutBtn = new Button { Text = "Zoom Out" };
zoomOutBtn.Clicked += (s, e) => pdfViewer.ZoomOut();

var layout = new StackLayout
{
    Orientation = StackOrientation.Horizontal,
    Children = { prevBtn, nextBtn, zoomInBtn, zoomOutBtn }
};

var page = new ContentPage
{
    Content = new StackLayout
    {
        Children = { layout, pdfViewer }
    }
};

Advanced: Styling or Rearranging the Built-In Toolbar

Want to integrate with your company’s design system, or move toolbar elements? While the built-in toolbar follows native platform styles (WinUI, macOS, or mobile), you can subclass the control or suggest enhancements to Iron Software—they’re super responsive to dev feedback.


Data Binding & MVVM: Dynamic PDFs and State

You’re probably working with MVVM. Good news: PdfViewer.Source is bindable.

ViewModel:

public class PdfViewModel : INotifyPropertyChanged
{
    private object _pdfSource;
    public object PdfSource
    {
        get => _pdfSource;
        set
        {
            _pdfSource = value;
            OnPropertyChanged();
        }
    }

    // Load PDF dynamically (e.g., in response to user action)
    public async Task LoadPdfAsync(string url)
    {
        using var client = new HttpClient();
        var bytes = await client.GetByteArrayAsync(url);
        PdfSource = bytes;
    }
}

XAML:

<viewer:PdfViewer Source="{Binding PdfSource}" />

Now you can swap out the PDF by updating your ViewModel—no code-behind needed!


Licensing: Removing the Watermark

By default, IronPDF Viewer runs in evaluation mode—fully functional, but with a trial watermark banner. For production, you need a license key.

How to Add Your License Key

At startup (best practice: don’t hardcode in source):

// Ideal: load from config or environment variable
var licenseKey = Environment.GetEnvironmentVariable("IRONPDF_LICENSE_KEY");
builder.ConfigureIronPdfView(licenseKey);

App config (e.g., appsettings.json):

{
  "IronPdf": {
    "LicenseKey": "YOUR-LICENSE-KEY-HERE"
  }
}

Load in MauiProgram.cs:

var licenseKey = builder.Configuration["IronPdf:LicenseKey"];
builder.ConfigureIronPdfView(licenseKey);

For desktop apps, you can also prompt for a key or let admins input via a settings UI, then store securely.

Security tip: Never commit real license keys to source control. Use environment variables or a secrets manager (e.g., Azure Key Vault, AWS Secrets Manager) for production.


Platform Support: What Works Today (and What Doesn’t Yet)

Right now: IronPDF MAUI Viewer is Windows-first. macOS and mobile support (iOS/Android) are on the roadmap, but not here yet.

  • Windows: Full support. The viewer is native, fast, and pixel-perfect.

  • macOS/iOS/Android: In progress. For now, you’ll need an alternative (see below).

Handling Multi-Platform Projects

If you’re targeting Windows + mobile, you can use conditional code:

#if WINDOWS
    var pdfViewer = new IronPdf.Viewer.Maui.PdfViewer { Source = "document.pdf" };
    Content = pdfViewer;
#elif ANDROID || IOS
    // Use WebView or a native PDF viewer for now
    var webView = new WebView { Source = "document.pdf" };
    Content = webView;
#endif

Or, if your app is Windows-only, lock it down in your project file:

<TargetFrameworks>net8.0-windows10.0.19041.0</TargetFrameworks>

Alternatives for Mobile

Until mobile support lands, your best cross-platform fallback is to use PDF.js in a WebView, or rely on platform-native viewers (e.g., QuickLook on iOS, Intent on Android). Not as seamless, but it gets the job done for now.


Advanced Features: Beyond Just Viewing

The IronPDF MAUI Viewer isn’t just a static doc renderer—it includes features your users will expect from a modern viewer:

  • Thumbnail navigation: Jump to any page visually.

  • Text search with highlight: Search the entire document; matches are highlighted.

  • Pinch/zoom and pan: Smooth navigation on touch and desktop.

  • Page navigation: Next/prev buttons, or even via swipe gestures.

  • Toolbar customization: Only show what you need, or build your own.

Example: Enabling Text Search and Thumbnails

pdfViewer.ShowSearchBox = true;
pdfViewer.ShowThumbnails = true;

You can even hook into events (e.g., page changed, search result clicked) for analytics or custom flows.


Real-World Scenarios: Putting It All Together

Here are a few “recipes” I’ve used in production apps.

1. Document Management System

  • PDFs are stored in Azure Blob Storage.

  • User logs in, picks a doc, and it streams down into the viewer.

  • No temp files, minimal memory overhead.

public async Task ShowPdfFromBlobAsync(string blobUrl, string authToken)
{
    var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);

    using var stream = await httpClient.GetStreamAsync(blobUrl);
    pdfViewer.Source = stream;
}

2. Help Viewer with Embedded Docs

  • Ship the PDF manual as an embedded resource.

  • Hide toolbar for a “read-only” feel.

  • Add custom navigation at the bottom.

pdfViewer.ShowToolbar = false;

var assembly = typeof(App).Assembly;
using var stream = assembly.GetManifestResourceStream("MyApp.Resources.manual.pdf");
pdfViewer.Source = stream;

3. Dynamic Report Viewer

  • User picks a report type and date; you generate the PDF on the fly (using IronPDF’s generation tools).

  • Display immediately in the viewer.

byte[] reportPdf = await ReportService.GenerateReportAsync(type, date);
pdfViewer.Source = reportPdf;

Common Pitfalls & Troubleshooting

No dev experience is complete without a few “gotchas.” Here are some headaches I’ve run into—and how to dodge them:

1. Stream Disposed Too Soon

Symptom: PDF fails to load, or loads partially, when using a stream.

Fix: Keep the stream open until the viewer has finished loading. Use a MemoryStream if you need to close the original stream.

// Good
var ms = new MemoryStream(await GetPdfBytesAsync());
pdfViewer.Source = ms; // MemoryStream stays alive

// Bad
using (var stream = await GetPdfStreamAsync())
{
    pdfViewer.Source = stream;
} // stream is disposed too soon!

2. Resource Not Found

Symptom: Embedded resource PDF doesn’t load.

Fix: Double-check your resource name, build action (should be “Embedded Resource”), and namespace. Use assembly.GetManifestResourceNames() to debug.

3. Watermark Still Shows

Symptom: You added a license key but still see the evaluation banner.

Fix: - Confirm the key is valid (no copy-paste errors, no extra spaces). - Ensure you’re setting the key before any viewer is created. - If loading from config/env, make sure the value is read correctly.

4. Platform Build Errors

Symptom: Project won’t build for iOS/Android.

Fix: IronPDF Viewer is Windows-only as of now. Use conditional compilation, or restrict target frameworks.

5. Large PDFs Slow to Render

Symptom: UI stutters or hangs with big documents.

Fix: IronPDF renders only visible pages, but if you hit issues: - Profile with smaller PDFs. - Check memory usage. - If pulling from a slow network, pre-download the file.

6. PDF Not Displaying Correctly

Symptom: Fonts missing, wrong colors, etc.

Fix: IronPDF uses Chromium rendering—so your PDF should look like it does in Chrome. If it doesn’t: - Check if the PDF itself has embedded fonts. - Try opening in Chrome for comparison. - Report rendering bugs to Iron Software.


Quick Reference: Common Tasks

TaskCode Example
Initialize viewerbuilder.ConfigureIronPdfView()
Add to XAML<viewer:PdfViewer Source="file.pdf" />
Add from C#new PdfViewer { Source = "file.pdf" }
Load from bytespdfViewer.Source = byteArray
Load from streampdfViewer.Source = stream
Next pagepdfViewer.GoToNextPage()
Previous pagepdfViewer.GoToPreviousPage()
Zoom inpdfViewer.ZoomIn()
Zoom outpdfViewer.ZoomOut()
Add licensebuilder.ConfigureIronPdfView("LICENSE-KEY")

Best practices: - Do initialization once (in MauiProgram.cs). - Use XAML for static docs, C# for dynamic/interactive scenarios. - Source accepts file paths, byte arrays, or streams—super flexible. - License key removes watermark banner. - For complete walkthroughs, check out the complete PDF viewing tutorial.


Wrapping Up

There you have it—a practical, modern, developer-friendly way to embed PDF viewing in your .NET MAUI apps. With IronPDF, you get a native experience, rich features, and the flexibility to handle real-world scenarios—from local files to cloud-hosted docs, from simple viewers to fully branded custom navigation.

I love that it’s “set it and forget it”—initialize once, then use it like any other control, with no platform hacks or messy workarounds.

Gotchas? Sure, but nothing you can’t handle. And if you hit a wall, the Iron Software team is super active—you’ll find me and others in the comments.

If you’ve found a better way to handle mobile fallbacks, or have tips to make PDF viewing even smoother in MAUI, let’s hear it below. Happy coding!


Written by Jacob Mellor, CTO at Iron Software. Building developer tools like IronPDF that make document processing simple. Got questions? Find me in the comments.