Skip to footer content
USING IRONPDF

Xamarin PDF Generator: Build Mobile PDF Apps with IronPDF

Creating PDF files in Xamarin.Forms can be tricky. Most .NET PDF libraries do not directly support mobile apps, and trying to generate PDF documents on a device often leads to errors or missing functionality. That's where IronPDF steps in.

While IronPDF does not run natively inside a Xamarin.Forms app, a server-side API approach bridges that gap cleanly. Your mobile app sends HTML content to the API and receives finished PDF files in return -- giving you access to professional PDF generation including forms, headers, footers, images, and custom layouts.

Important note: Microsoft ended support for Xamarin in May 2024. For new projects, .NET MAUI is the recommended successor and supports IronPDF more directly. This guide covers the server-side pattern for legacy Xamarin projects still in maintenance and explains the migration path to MAUI for teams starting fresh.

Why Does a Server-Side Approach Work for Mobile PDF Generation?

IronPDF excels at converting HTML content into polished PDF documents with full support for CSS, JavaScript, and complex layouts. Running IronPDF on a dedicated server -- rather than inside a mobile application -- sidesteps the platform constraints that prevent direct on-device PDF rendering on iOS and Android.

The server-side pattern offers several concrete advantages:

  • Consistent output: Fonts, images, and CSS are resolved server-side, eliminating rendering differences between Android and iOS hardware.
  • Feature access: IronPDF features such as PDF form creation, digital signatures, watermarks, and multi-page layouts are all available on the server without restriction.
  • Lighter mobile app: The device only sends an HTTP request and stores the returned PDF bytes -- no heavy PDF engine runs on the phone.
  • Centralized licensing: A single IronPDF license covers your server deployment rather than licensing each device separately.

The Xamarin.Forms client calls the API, receives a byte array, writes it to local storage, and optionally opens a PDF viewer. The server handles everything else.

How Do You Set Up an IronPDF PDF Generation API?

Start by creating an ASP.NET Core Web API project. This is a standard .NET 10 minimal API that you can host anywhere -- Azure App Service, AWS, an on-premise server, or a Docker container.

Install IronPDF

Install IronPDF from NuGet using either of these commands:

Install-Package IronPdf
dotnet add package IronPdf
Install-Package IronPdf
dotnet add package IronPdf
SHELL

Create the PDF Controller

With IronPDF installed, add a controller that accepts HTML and returns a PDF:

using IronPdf;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();

namespace PdfGenerationApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class PdfController : ControllerBase
    {
        [HttpPost("generate")]
        public async Task<IActionResult> GeneratePdf([FromBody] PdfRequest request)
        {
            var renderer = new ChromePdfRenderer();
            renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
            renderer.RenderingOptions.MarginTop = 25;
            renderer.RenderingOptions.MarginBottom = 25;
            renderer.RenderingOptions.MarginLeft = 20;
            renderer.RenderingOptions.MarginRight = 20;

            var pdf = await renderer.RenderHtmlAsPdfAsync(request.HtmlContent);
            return File(pdf.BinaryData, "application/pdf", "document.pdf");
        }
    }

    public class PdfRequest
    {
        public string HtmlContent { get; set; } = string.Empty;
    }
}
using IronPdf;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();

namespace PdfGenerationApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class PdfController : ControllerBase
    {
        [HttpPost("generate")]
        public async Task<IActionResult> GeneratePdf([FromBody] PdfRequest request)
        {
            var renderer = new ChromePdfRenderer();
            renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
            renderer.RenderingOptions.MarginTop = 25;
            renderer.RenderingOptions.MarginBottom = 25;
            renderer.RenderingOptions.MarginLeft = 20;
            renderer.RenderingOptions.MarginRight = 20;

            var pdf = await renderer.RenderHtmlAsPdfAsync(request.HtmlContent);
            return File(pdf.BinaryData, "application/pdf", "document.pdf");
        }
    }

    public class PdfRequest
    {
        public string HtmlContent { get; set; } = string.Empty;
    }
}
$vbLabelText   $csharpLabel

ChromePdfRenderer uses a Chromium-based engine to render HTML exactly as a modern browser would. The HTML to PDF conversion respects CSS animations, embedded fonts, SVG graphics, and JavaScript-generated content. Paper size and margin settings translate directly into the final document layout.

Add Headers and Footers

For professional documents, add headers and footers before calling the renderer:

renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='text-align:right; font-size:12px; color:#555;'>Confidential -- Page {page} of {total-pages}</div>",
    DrawDividerLine = true
};
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='text-align:center; font-size:11px;'>Generated by MyCompany App</div>"
};
renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='text-align:right; font-size:12px; color:#555;'>Confidential -- Page {page} of {total-pages}</div>",
    DrawDividerLine = true
};
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='text-align:center; font-size:11px;'>Generated by MyCompany App</div>"
};
$vbLabelText   $csharpLabel

Page number tokens like {page} and {total-pages} are resolved automatically at render time.

How Do You Implement the Xamarin Client?

In the Xamarin.Forms application, create a service class that calls the API. Keep the service lean -- its only job is to serialize the HTML payload, send it, and return the raw PDF bytes to the caller.

using System.Net.Http;
using System.Text;
using System.Text.Json;

namespace XamarinPdfApp.Services
{
    public class PdfService
    {
        private readonly HttpClient _httpClient;
        private const string ApiUrl = "https://your-api.example.com/api/pdf/generate";

        public PdfService()
        {
            _httpClient = new HttpClient
            {
                Timeout = TimeSpan.FromSeconds(60)
            };
        }

        public async Task<byte[]> GeneratePdfAsync(string htmlContent)
        {
            var payload = new { HtmlContent = htmlContent };
            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await _httpClient.PostAsync(ApiUrl, content);

            if (response.IsSuccessStatusCode)
                return await response.Content.ReadAsByteArrayAsync();

            var error = await response.Content.ReadAsStringAsync();
            throw new InvalidOperationException($"PDF generation failed ({(int)response.StatusCode}): {error}");
        }
    }
}
using System.Net.Http;
using System.Text;
using System.Text.Json;

namespace XamarinPdfApp.Services
{
    public class PdfService
    {
        private readonly HttpClient _httpClient;
        private const string ApiUrl = "https://your-api.example.com/api/pdf/generate";

        public PdfService()
        {
            _httpClient = new HttpClient
            {
                Timeout = TimeSpan.FromSeconds(60)
            };
        }

        public async Task<byte[]> GeneratePdfAsync(string htmlContent)
        {
            var payload = new { HtmlContent = htmlContent };
            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await _httpClient.PostAsync(ApiUrl, content);

            if (response.IsSuccessStatusCode)
                return await response.Content.ReadAsByteArrayAsync();

            var error = await response.Content.ReadAsStringAsync();
            throw new InvalidOperationException($"PDF generation failed ({(int)response.StatusCode}): {error}");
        }
    }
}
$vbLabelText   $csharpLabel

A 60-second timeout accommodates complex HTML documents with many images or CSS resources. For very large files, consider returning a presigned download URL from the API instead of streaming the binary directly -- this keeps mobile memory usage predictable.

How Do You Save and Open PDF Files on Device?

Once the service returns the byte array, write it to device storage and open it in the platform PDF viewer. Xamarin.Forms uses the DependencyService pattern to call platform-specific implementations.

Define the interface in shared code:

using System.Threading.Tasks;

namespace XamarinPdfApp.Interfaces
{
    public interface ISaveFile
    {
        Task<string> SavePdfAsync(string filename, byte[] pdfData);
    }
}
using System.Threading.Tasks;

namespace XamarinPdfApp.Interfaces
{
    public interface ISaveFile
    {
        Task<string> SavePdfAsync(string filename, byte[] pdfData);
    }
}
$vbLabelText   $csharpLabel

Register the iOS implementation using DependencyService:

using Foundation;
using QuickLook;
using UIKit;
using XamarinPdfApp.Interfaces;
using Xamarin.Forms;

[assembly: Dependency(typeof(XamarinPdfApp.iOS.SaveFileIOS))]
namespace XamarinPdfApp.iOS
{
    public class SaveFileIOS : ISaveFile
    {
        public async Task<string> SavePdfAsync(string filename, byte[] pdfData)
        {
            var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            var filePath = System.IO.Path.Combine(documents, filename);
            await System.IO.File.WriteAllBytesAsync(filePath, pdfData);
            return filePath;
        }
    }
}
using Foundation;
using QuickLook;
using UIKit;
using XamarinPdfApp.Interfaces;
using Xamarin.Forms;

[assembly: Dependency(typeof(XamarinPdfApp.iOS.SaveFileIOS))]
namespace XamarinPdfApp.iOS
{
    public class SaveFileIOS : ISaveFile
    {
        public async Task<string> SavePdfAsync(string filename, byte[] pdfData)
        {
            var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            var filePath = System.IO.Path.Combine(documents, filename);
            await System.IO.File.WriteAllBytesAsync(filePath, pdfData);
            return filePath;
        }
    }
}
$vbLabelText   $csharpLabel

For Android, write to the app's external files directory and register a FileProvider in the manifest so you can hand the URI to a PDF viewer intent. The DependencyService.Get<ISaveFile>() call in shared code retrieves whichever implementation is registered for the current platform at runtime.

Wiring It All Together

In your Xamarin.Forms page or ViewModel, combine the service and the platform saver:

var htmlContent = BuildInvoiceHtml(invoice);
var pdfBytes = await _pdfService.GeneratePdfAsync(htmlContent);
var saver = DependencyService.Get<ISaveFile>();
var filePath = await saver.SavePdfAsync("invoice.pdf", pdfBytes);
await Launcher.OpenAsync(new OpenFileRequest
{
    File = new ReadOnlyFile(filePath, "application/pdf")
});
var htmlContent = BuildInvoiceHtml(invoice);
var pdfBytes = await _pdfService.GeneratePdfAsync(htmlContent);
var saver = DependencyService.Get<ISaveFile>();
var filePath = await saver.SavePdfAsync("invoice.pdf", pdfBytes);
await Launcher.OpenAsync(new OpenFileRequest
{
    File = new ReadOnlyFile(filePath, "application/pdf")
});
$vbLabelText   $csharpLabel

This opens the saved PDF in whatever viewer the user has installed, which is typically a native PDF application on both iOS and Android.

How Do You Generate Professional Invoice and Report PDFs?

The quality of a PDF depends almost entirely on the quality of the HTML template passed to the renderer. Use C# string interpolation or a templating library like Scriban to build data-driven HTML:

public string BuildInvoiceHtml(Invoice invoice)
{
    var rows = string.Join(
        "\n",
        invoice.Items.Select(i =>
            $"<tr><td>{i.Name}</td><td>{i.Quantity}</td><td>${i.UnitPrice:F2}</td><td>${i.Total:F2}</td></tr>"
        )
    );

    return $@"<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<style>
  body {{ font-family: Arial, sans-serif; color: #333; margin: 0; padding: 30px; }}
  h1 {{ color: #1a73e8; }}
  table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }}
  th {{ background: #1a73e8; color: #fff; padding: 10px; text-align: left; }}
  td {{ padding: 10px; border-bottom: 1px solid #e0e0e0; }}
  .total {{ font-weight: bold; font-size: 1.1em; text-align: right; margin-top: 15px; }}
</style>
</head>
<body>
  <h1>Invoice #{invoice.Number}</h1>
  <p>Date: {invoice.Date:yyyy-MM-dd} &nbsp;|&nbsp; Due: {invoice.DueDate:yyyy-MM-dd}</p>
  <p>Bill to: <strong>{invoice.ClientName}</strong></p>
  <table>
    <thead><tr><th>Item</th><th>Qty</th><th>Unit Price</th><th>Total</th></tr></thead>
    <tbody>{rows}</tbody>
  </table>
  <p class='total'>Grand Total: ${invoice.GrandTotal:F2}</p>
</body>
</html>";
}
public string BuildInvoiceHtml(Invoice invoice)
{
    var rows = string.Join(
        "\n",
        invoice.Items.Select(i =>
            $"<tr><td>{i.Name}</td><td>{i.Quantity}</td><td>${i.UnitPrice:F2}</td><td>${i.Total:F2}</td></tr>"
        )
    );

    return $@"<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<style>
  body {{ font-family: Arial, sans-serif; color: #333; margin: 0; padding: 30px; }}
  h1 {{ color: #1a73e8; }}
  table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }}
  th {{ background: #1a73e8; color: #fff; padding: 10px; text-align: left; }}
  td {{ padding: 10px; border-bottom: 1px solid #e0e0e0; }}
  .total {{ font-weight: bold; font-size: 1.1em; text-align: right; margin-top: 15px; }}
</style>
</head>
<body>
  <h1>Invoice #{invoice.Number}</h1>
  <p>Date: {invoice.Date:yyyy-MM-dd} &nbsp;|&nbsp; Due: {invoice.DueDate:yyyy-MM-dd}</p>
  <p>Bill to: <strong>{invoice.ClientName}</strong></p>
  <table>
    <thead><tr><th>Item</th><th>Qty</th><th>Unit Price</th><th>Total</th></tr></thead>
    <tbody>{rows}</tbody>
  </table>
  <p class='total'>Grand Total: ${invoice.GrandTotal:F2}</p>
</body>
</html>";
}
$vbLabelText   $csharpLabel

The ChromePdfRenderer renders this template exactly as a browser would. You can add watermarks using IronPDF's watermark API or apply custom watermark designs for confidential drafts. For documents requiring a signature field, IronPDF's signing support lets you embed digital signature placeholders server-side.

How Do You Handle PDF Forms in a Xamarin App?

PDF forms are a common requirement for mobile business apps -- contracts, onboarding questionnaires, and inspection checklists all benefit from pre-filled editable fields. The server API can accept field data alongside the HTML template and embed form values before returning the PDF:

[HttpPost("form")]
public async Task<IActionResult> GenerateForm([FromBody] FormRequest request)
{
    var renderer = new ChromePdfRenderer();
    // Render an HTML form template to create an interactive PDF form
    var pdf = await renderer.RenderHtmlAsPdfAsync(request.HtmlTemplate);

    // Fill known values before returning
    var form = pdf.Form;
    foreach (var field in request.FieldValues)
    {
        var pdfField = form.Fields.FirstOrDefault(f => f.Name == field.Key);
        if (pdfField is IronPdf.Forms.PdfFormTextFieldField textField)
            textField.Value = field.Value;
    }

    return File(pdf.BinaryData, "application/pdf", "form.pdf");
}
[HttpPost("form")]
public async Task<IActionResult> GenerateForm([FromBody] FormRequest request)
{
    var renderer = new ChromePdfRenderer();
    // Render an HTML form template to create an interactive PDF form
    var pdf = await renderer.RenderHtmlAsPdfAsync(request.HtmlTemplate);

    // Fill known values before returning
    var form = pdf.Form;
    foreach (var field in request.FieldValues)
    {
        var pdfField = form.Fields.FirstOrDefault(f => f.Name == field.Key);
        if (pdfField is IronPdf.Forms.PdfFormTextFieldField textField)
            textField.Value = field.Value;
    }

    return File(pdf.BinaryData, "application/pdf", "form.pdf");
}
$vbLabelText   $csharpLabel

The mobile client sends a dictionary of field names and values. The server fills them and returns a form the user can review, complete any remaining fields in a PDF viewer, and submit.

How Do You Extract Text and Merge PDFs from a Xamarin App?

Beyond generation, IronPDF supports a wide range of document operations you can expose as API endpoints:

Each of these becomes a separate API endpoint. The Xamarin client calls them like any other REST resource, keeping the mobile code free of PDF logic and the server as the authoritative document processor.

What Are the Common Issues and How Do You Fix Them?

The server-client architecture is straightforward, but several production concerns deserve attention:

Common issues and recommended fixes for a Xamarin -- IronPDF API integration
IssueCauseRecommended Fix
Request timeoutComplex HTML with many remote assets takes time to renderIncrease HttpClient.Timeout and set server-side render timeout in rendering options
Large PDF memory spikeStreaming a 20 MB PDF through the response bodyUpload to blob storage and return a short-lived download URL instead
Offline generationDevice has no connectivity when the user requests a PDFQueue requests locally and retry when connectivity is restored
Unauthorized API accessEndpoint is open to the internetSecure with JWT or API key; enforce HTTPS on all routes
Font not embeddedServer OS does not have the font installedEmbed the font in the HTML as a base64 data URI or as a CSS @font-face rule
iOS storage permissionApp targets iOS 14+ with tighter sandboxingWrite to Environment.SpecialFolder.MyDocuments inside the app sandbox

For rate limiting, add middleware on the ASP.NET Core side using a library such as AspNetCoreRateLimit. Log each generation request with timing data so you can spot slow templates before they affect users.

Should You Migrate from Xamarin to .NET MAUI?

If you are starting a new mobile project in 2026, .NET MAUI is the right choice. Microsoft ended Xamarin support in May 2024, meaning no further security patches or bug fixes. .NET MAUI is the direct successor and runs on .NET 10, matching the current IronPDF runtime.

The server-side architecture described in this guide works identically for .NET MAUI apps -- the client HTTP code is essentially the same; only the DependencyService is replaced by MAUI's built-in dependency injection. Teams maintaining existing Xamarin apps should plan a migration to MAUI; the official .NET MAUI migration guide from Microsoft documents the steps in detail.

How Do You Deploy and License IronPDF for Production?

Deployment is straightforward: containerize the ASP.NET Core API with Docker and push it to Azure App Service, AWS ECS, or any Kubernetes cluster. The IronPDF license key is set as an environment variable on the server:

IronPdf.License.LicenseKey = Environment.GetEnvironmentVariable("IRONPDF_LICENSE_KEY")
    ?? throw new InvalidOperationException("IronPDF license key not set.");
IronPdf.License.LicenseKey = Environment.GetEnvironmentVariable("IRONPDF_LICENSE_KEY")
    ?? throw new InvalidOperationException("IronPDF license key not set.");
$vbLabelText   $csharpLabel

Review the IronPDF licensing page to select the right tier for your deployment. A free trial license lets you test the full feature set before committing. For containerized or serverless deployments, check that the license tier covers the number of server instances running simultaneously.

After licensing, explore the IronPDF documentation for advanced configuration options including thread-safe rendering settings, PDF/A compliance, and accessibility tagging.

Production Deployment Checklist

Deployment checklist for an IronPDF-based PDF generation API
ItemRecommendation
License keyStore in environment variables or a secret manager, never in source code
HTTPSEnforce TLS on all API endpoints; never send HTML payloads over plain HTTP
AuthenticationUse JWT bearer tokens or API keys; revoke on breach
CachingCache identical HTML payloads for a short TTL to reduce redundant renders
ScalingIronPDF is thread-safe; run multiple API replicas behind a load balancer
MonitoringTrack render latency and error rates; alert on spikes above baseline

The server-side pattern scales horizontally without any changes to the Xamarin or MAUI client. Add replicas as PDF generation volume grows and rely on the load balancer to distribute requests evenly.

Frequently Asked Questions

Can IronPDF be used natively in Xamarin.Forms?

IronPDF does not run natively in Xamarin.Forms, but it can be integrated using a server-side approach to handle PDF creation and manipulation.

What are the benefits of using IronPDF for PDF generation in mobile apps?

Using IronPDF allows you to create PDF files, fill forms, handle multiple pages, and include images, fonts, and custom layouts, enhancing the PDF generation capabilities of your mobile apps.

How does IronPDF handle PDF creation in Xamarin?

IronPDF uses a server-side approach to generate PDFs in Xamarin, enabling complex PDF functionalities that are not typically supported on mobile devices.

Is it possible to fill PDF forms using IronPDF in Xamarin?

Yes, IronPDF supports filling PDF forms as part of its comprehensive PDF handling features for Xamarin applications.

What challenges does IronPDF address in Xamarin PDF generation?

IronPDF addresses challenges such as errors and missing functionalities when generating PDFs directly on mobile devices by using a server-side solution.

Can IronPDF handle custom layouts in PDFs for Xamarin apps?

Yes, IronPDF can include custom layouts in generated PDFs, giving you control over the design and presentation of your documents.

What kind of PDF features can be implemented in Xamarin using IronPDF?

IronPDF allows implementation of features like multi-page documents, form filling, image and font incorporation, and custom layouts in Xamarin.

Why is a server-side approach recommended for PDF generation in Xamarin with IronPDF?

A server-side approach is recommended because it bypasses the limitations of mobile devices, ensuring reliable PDF creation and advanced features.

Curtis Chau
Technical Writer

Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.

...

Read More