Embedding PDF viewing in desktop and mobile applications has always been challenging. Native PDF viewers like Adobe Reader require external processes or shell integrations that feel disconnected from your application. Web-based viewers need browser controls and internet connectivity. Building a custom PDF renderer from scratch involves complex PDF specification parsing that most developers shouldn't tackle.
.NET MAUI promised cross-platform UI development for Windows, macOS, iOS, and Android with a single codebase. But PDF viewing wasn't included out of the box. Third-party solutions existed but often required platform-specific implementations or wrapper controls that leaked platform details into supposedly platform-agnostic code.
Iron Software solved this with IronPDF Viewer for MAUI — a native .NET control that embeds PDF rendering directly in MAUI applications. It's a single control that works consistently across platforms, with no JavaScript bridges, no web views, and no external dependencies. You add it to your MAUI page like any other control, bind it to a PDF source, and it renders the document with full zoom, pan, and navigation features.
The implementation leverages IronPDF's Chromium-based rendering engine under the hood. This ensures PDFs render identically to how Chrome would display them — accurate fonts, precise layouts, correct colors. No platform-specific rendering quirks where a PDF looks different on Windows versus macOS. The same rendering engine produces pixel-perfect output regardless of target platform.
I've built document management systems, contract review applications, and report viewers using this control. The developer experience is excellent — it's just XAML markup or C# object instantiation. There's no JavaScript interop layer to debug, no native platform code to maintain, and no conditional compilation directives littering the codebase with platform checks.
The control supports the features users expect from PDF viewers: thumbnail navigation for jumping between pages, text search with highlighting, zoom controls with pinch gesture support on touch devices, page navigation via swipe gestures, and toolbar customization to show only the features your application needs. These aren't bolted-on features; they're integral to the control and work consistently everywhere.
For enterprise applications, license key integration removes evaluation [[watermarks](https://ironpdf.com/java/how-to/print-pdf/)](https://ironsoftware.com/csharp/print/how-to/aspnet-web-application-framework/) and enables full functionality. The licensing model is straightforward — install the NuGet package, add your license key during initialization, and the control unlocks. No activation servers, no phone-home requirements, just standard .NET configuration.
using IronPdf.Viewer.Maui;
// Install via NuGet: Install-Package IronPdf.Viewer.Maui
// In MauiProgram.cs
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureIronPdfView(); // Initialize PDF viewer
return builder.Build();
}
}
That initialization call registers the PDF viewer control with MAUI's dependency injection system and sets up the rendering infrastructure. From this point forward, you can use the PdfViewer control anywhere in your application. The setup is one-time, global configuration that affects all PDF viewer instances.
How Do I Add a PDF Viewer to a MAUI Page?
Once the viewer is initialized in MauiProgram.cs, adding it to pages is straightforward. You can use XAML declarative markup or C# imperative code, depending on your preferred development style. Both approaches produce identical results.
Using XAML markup:
<?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" />
</ContentPage>
This declares a PDF viewer that fills the entire page and loads a PDF from the application's resources. The Source property accepts file paths relative to the application bundle. On Windows, this resolves to the application's installation directory. On mobile platforms, it references embedded resources.
Using C# code instead:
public class PdfViewerPage : ContentPage
{
public PdfViewerPage()
{
var pdfViewer = new IronPdf.Viewer.Maui.PdfViewer
{
Source = "documents/manual.pdf"
};
Content = pdfViewer;
}
}
The imperative approach gives you more control for dynamic scenarios — loading different PDFs based on user selection, applying conditional logic to viewer configuration, or programmatically responding to viewer events. The declarative XAML approach is cleaner for static page layouts where the PDF source doesn't change.
I typically use XAML for fixed-function viewers like help documentation or terms of service displays where the PDF is known at compile time. I use C# for viewers that load PDFs dynamically based on database queries or user navigation, where the source changes at runtime.
The viewer control integrates with MAUI's layout system like any native control. You can embed it in grids, stack layouts, or scroll views. You can apply margins, padding, and size constraints. It responds to platform-specific gestures — pinch to zoom on touch devices, scroll wheel zoom on desktop. These behaviors are built-in and platform-appropriate without developer intervention.
How Do I Load PDFs from Different Sources?
The Source property supports multiple input formats beyond simple file paths. You can load PDFs from embedded resources, network streams, byte arrays, or memory streams. This flexibility enables various application architectures and data sources.
Loading from a byte array (useful for database-stored PDFs):
byte[] pdfBytes = await LoadPdfFromDatabaseAsync(documentId);
pdfViewer.Source = pdfBytes;
Loading from a stream (useful for network downloads):
using (var httpClient = new HttpClient())
{
var stream = await httpClient.GetStreamAsync("https://example.com/document.pdf");
pdfViewer.Source = stream;
}
Loading from embedded resources:
var assembly = GetType().Assembly;
using (var stream = assembly.GetManifestResourceStream("MyApp.Resources.manual.pdf"))
{
pdfViewer.Source = stream;
}
The viewer detects the source type automatically and handles the loading appropriately. Streams are read into memory, byte arrays are processed directly, and file paths are opened with file I/O. This abstraction simplifies application code — you don't need different methods or conversion steps depending on where the PDF comes from.
I've used this flexibility extensively. For a document management system, PDFs were stored in Azure Blob Storage. I downloaded them as streams and passed them directly to the viewer without saving to local disk. For offline-capable apps, PDFs were embedded as resources during compilation and loaded from the assembly at runtime. For user-generated content, PDFs came from API responses as byte arrays.
The viewer handles large documents efficiently. It doesn't load the entire PDF into memory at once. Only visible pages are rendered, with off-screen pages rendered on-demand as users navigate. This keeps memory usage reasonable even for hundred-page documents. I've tested with 500-page manuals and performance remains smooth.
One consideration with streams: ensure they remain open until the viewer finishes loading. The loading happens asynchronously, so disposing the stream immediately after assignment can cause failures. Either keep the stream reference alive or use a MemoryStream that copies data into memory. For most scenarios, letting the viewer manage the stream lifecycle is simplest.
Can I Customize the Viewer Toolbar?
Yes, extensively. The default toolbar includes thumbnail navigation, text search, zoom controls, and page navigation. You can hide individual features, rearrange toolbar buttons, or replace the toolbar entirely with custom UI. This customization ensures the viewer fits your application's design language and feature requirements.
Hiding specific toolbar features:
pdfViewer.ShowThumbnails = false; // Hide thumbnail sidebar
pdfViewer.ShowSearchBox = false; // Hide search functionality
pdfViewer.ShowZoomControls = false; // Hide zoom buttons
These properties control visibility of built-in features. Disabling features reduces UI clutter for simplified viewers where users only need to read PDFs linearly without navigation aids. I use minimal toolbars for help documentation viewers where users primarily scroll through content sequentially.
Rearranging or styling toolbar elements requires subclassing the viewer control and overriding its toolbar composition methods. This is more advanced but provides complete control over toolbar appearance. For most applications, the default toolbar with selective feature hiding is sufficient.
The toolbar itself respects platform design guidelines automatically. On Windows, it uses WinUI styling with appropriate fonts and colors. On macOS, it adopts native macOS appearance. On mobile platforms, it adapts to touch-friendly button sizes and spacing. This platform-appropriate rendering happens without developer configuration — the control queries the platform and adjusts accordingly.
For applications with strict branding requirements, you can hide the built-in toolbar entirely and build custom navigation UI. Place buttons in your page layout that call viewer methods programmatically:
// Custom navigation buttons
var nextPageButton = new Button { Text = "Next Page" };
nextPageButton.Clicked += (s, e) => pdfViewer.GoToNextPage();
var previousPageButton = new Button { Text = "Previous Page" };
previousPageButton.Clicked += (s, e) => pdfViewer.GoToPreviousPage();
var zoomInButton = new Button { Text = "Zoom In" };
zoomInButton.Clicked += (s, e) => pdfViewer.ZoomIn();
var layout = new StackLayout
{
Children = { previousPageButton, nextPageButton, zoomInButton, pdfViewer }
};
This approach gives complete control over navigation UI at the cost of implementing features the default toolbar provides. I use custom navigation when integrating the viewer into complex page layouts where the standard toolbar placement doesn't fit, or when navigation should be contextual to other page content.
How Do I Add a License Key?
The viewer requires a license key for production use. Without a key, evaluation mode adds a watermark banner to rendered PDFs. The banner displays trial information but otherwise doesn't restrict functionality. For development and testing, the evaluation mode is fully functional.
To add a license key, modify the initialization in MauiProgram.cs:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureIronPdfView("YOUR-LICENSE-KEY-HERE");
return builder.Build();
}
}
Replace "YOUR-LICENSE-KEY-HERE" with your actual license key string. The key is a long alphanumeric string provided after purchase. Paste it exactly as received, including any hyphens or special characters. The licensing system validates the key at startup and removes the watermark if valid.
License keys are typically stored in configuration files or environment variables rather than hardcoded in source. For production deployments, load the key from appsettings.json or a secure configuration provider:
var configuration = builder.Configuration;
var licenseKey = configuration["IronPdf:LicenseKey"];
builder.ConfigureIronPdfView(licenseKey);
This separates license management from source code, allowing different keys for development, staging, and production environments without code changes. It also prevents accidentally committing license keys to source control.
I store license keys in Azure Key Vault or AWS Secrets Manager for cloud deployments. The application retrieves the key at startup using the cloud provider's SDK. This centralized secret management ensures keys can be rotated without redeploying applications and provides audit logging of key access.
For desktop applications distributed to end users, license keys are typically embedded during build or configured via application settings files. For enterprise software with per-installation licensing, keys might be entered through an admin interface and stored in local databases.
What Platforms Does the Viewer Support?
Currently, the IronPDF Viewer for MAUI focuses on Windows desktop applications. iOS and Android support is planned but not yet available. This platform limitation is important when architecting multi-platform applications — PDF viewing works on Windows targets but requires alternative solutions on mobile platforms.
For cross-platform projects targeting mobile, use conditional compilation or platform-specific implementations:
#if WINDOWS
var pdfViewer = new IronPdf.Viewer.Maui.PdfViewer { Source = "document.pdf" };
Content = pdfViewer;
#elif ANDROID || IOS
// Use platform-specific PDF viewing (WebView, native APIs, etc.)
var webView = new WebView { Source = "document.pdf" };
Content = webView;
#endif
This ensures Windows builds use the IronPDF viewer while mobile builds fall back to alternative rendering methods. The conditional compilation keeps platform-specific code isolated and prevents build errors on unsupported platforms.
Alternatively, disable mobile targets entirely in the project file if your application is Windows-only:
<TargetFrameworks>net8.0-windows10.0.19041.0</TargetFrameworks>
This focuses the project on Windows and prevents accidental attempts to build for unsupported platforms. I use this approach for internal enterprise tools that only need Windows desktop support. It simplifies the project structure and eliminates cross-platform considerations when they're not necessary.
For mobile applications that need PDF viewing now, consider web-based PDF viewers like PDF.js loaded in a WebView control, or platform-specific libraries that wrap native Android and iOS PDF renderers. These aren't as integrated as the IronPDF control but provide functional PDF viewing until mobile support arrives.
Quick Reference
| Task | Code |
|---|---|
| Initialize viewer |
builder.ConfigureIronPdfView() in MauiProgram.cs |
| Add to XAML | <viewer:PdfViewer Source="file.pdf" /> |
| Add from C# | new PdfViewer { Source = "file.pdf" } |
| Load from bytes | pdfViewer.Source = byteArray |
| Load from stream | pdfViewer.Source = stream |
| Next page | pdfViewer.GoToNextPage() |
| Previous page | pdfViewer.GoToPreviousPage() |
| Zoom in | pdfViewer.ZoomIn() |
| Zoom out | pdfViewer.ZoomOut() |
| Add license | builder.ConfigureIronPdfView("LICENSE-KEY") |
Key Principles:
- Initialize once in MauiProgram.cs for all viewer instances
- Use XAML for static viewers, C# for dynamic scenarios
- Source property accepts file paths, byte arrays, or streams
- Currently Windows-only, mobile support coming
- License key removes evaluation watermark
- Toolbar customization via visibility properties
The complete PDF viewing tutorial includes step-by-step screenshots and troubleshooting guidance.
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)