IRONSOFTWAREHOME
MIGRATION GUIDES

How to Migrate from jsreport to IronPDF in C#

Curtis Chau
Curtis Chau
Updated: August 1, 2026

Migrating from jsreport to IronPDF transforms your .NET PDF workflow from a Node.js-dependent system with external binary management and separate server processes to a pure .NET library that runs entirely in-process. This guide provides a comprehensive, step-by-step migration path that eliminates infrastructure complexity and JavaScript templating requirements for professional .NET developers.

Why Migrate from jsreport to IronPDF

The jsreport Challenges

jsreport introduces complexity that doesn't belong in a pure .NET environment:

  1. Node.js Dependency: Requires Node.js runtime and binaries, adding infrastructure complexity to what should be a straightforward .NET application.
  2. External Binary Management: Must download and manage platform-specific binaries for Windows, Linux, and OSX through separate NuGet packages (jsreport.Binary, jsreport.Binary.Linux, jsreport.Binary.OSX).
  3. Separate Server Process: Runs as either a utility or web server - additional process management is required with StartAsync() and KillAsync() lifecycle methods.
  4. JavaScript Templating: Forces learning Handlebars, JsRender, or other JavaScript templating systems instead of using native C# capabilities.
  5. Complex Request Structure: Requires verbose RenderRequest objects with nested Template configurations for even simple PDF generation.
  6. Licensing Limitations: Free tier is capped at 5 templates per instance; commercial use beyond that requires an Enterprise license ($395/year per instance) or Enterprise Scale ($1,295/year, royalty-free across instances). See jsreport.net/buy.
  7. Stream-Based Output: Returns streams requiring manual file operations and memory stream management.

jsreport vs IronPDF Comparison

FeaturejsreportIronPDF
RuntimeNode.js + .NETPure .NET
Binary ManagementManual (jsreport.Binary packages)Automatic
Server ProcessRequired (utility or web server)In-process
TemplatingJavaScript (Handlebars, etc.)C# (Razor, string interpolation)
API StyleVerbose request objectsClean fluent methods
OutputStreamPdfDocument object
PDF ManipulationLimitedExtensive (merge, split, edit)
Async SupportAsync-onlyBoth sync and async

For teams planning .NET 10 and C# 14 adoption, IronPDF provides a native .NET foundation without external runtime dependencies.


Migration Complexity Assessment

Estimated Effort by Feature

FeatureMigration Complexity
HTML to PDFVery Low
URL to PDFVery Low
Headers/FootersLow
Page SettingsLow
Server LifecycleLow
Binary ManagementLow

Paradigm Shift

The fundamental shift in this jsreport migration is from verbose request objects with server management to simple in-process method calls:

jsreport:  LocalReporting().UseBinary().AsUtility().Create() → RenderAsync(RenderRequest) → Stream → File
IronPDF:   ChromePdfRenderer → RenderHtmlAsPdf(html) → SaveAs()
Text

Before You Start

Prerequisites

  1. .NET Environment: .NET Framework 4.6.2+ or .NET Core 3.1+ / .NET 5/6/7/8/9+
  2. NuGet Access: Ability to install NuGet packages
  3. IronPDF License: Obtain your license key from ironpdf.com

NuGet Package Changes

# Remove jsreport packages
dotnet remove package jsreport.Binary
dotnet remove package jsreport.Binary.Linux
dotnet remove package jsreport.Binary.OSX
dotnet remove package jsreport.Local
dotnet remove package jsreport.Types
dotnet remove package jsreport.Client

# Install IronPDF
dotnet add package IronPdf
SHELL

License Configuration

// Add at application startup (Program.cs or Startup.cs)
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";

Identify jsreport Usage

# Find all jsreport references
grep -r "using jsreport\|LocalReporting\|RenderRequest\|RenderAsync" --include="*.cs" .
grep -r "JsReportBinary\|Template\|Recipe\|Engine\." --include="*.cs" .
SHELL

Complete API Reference

Class Mappings

jsreport ClassIronPDF Equivalent
LocalReportingChromePdfRenderer
ReportingServiceChromePdfRenderer
RenderRequestMethod parameters
TemplateMethod parameters
ChromeRenderingOptions
ReportPdfDocument
Engine(not needed)

Method Mappings

jsreport MethodIronPDF Equivalent
LocalReporting().UseBinary().AsUtility().Create()new ChromePdfRenderer()
rs.RenderAsync(request)renderer.RenderHtmlAsPdf(html)
rs.StartAsync()(not needed)
rs.KillAsync()(not needed)
report.Content.CopyTo(stream)pdf.SaveAs(path) or pdf.BinaryData

RenderRequest Property Mappings

jsreport Template PropertyIronPDF Equivalent
Template.ContentFirst parameter to RenderHtmlAsPdf()
Template.Recipe = Recipe.ChromePdf(not needed)
Template.Engine = Engine.None(not needed)
Chrome.HeaderTemplateRenderingOptions.TextHeader or HtmlHeader
Chrome.FooterTemplateRenderingOptions.TextFooter or HtmlFooter
Chrome.DisplayHeaderFooter(automatic)
Chrome.MarginTopRenderingOptions.MarginTop

Placeholder Mappings (Headers/Footers)

jsreport PlaceholderIronPDF Placeholder
<span class='pageNumber'></span>{page}
<span class='totalPages'></span>{total-pages}
{#pageNum}{page}
{#numPages}{total-pages}
{#timestamp}{date}

Code Migration Examples

Example 1: Basic HTML to PDF

Before (jsreport):

// NuGet: Install-Package jsreport.Binary
// NuGet: Install-Package jsreport.Local
// NuGet: Install-Package jsreport.Types
using jsreport.Binary;
using jsreport.Local;
using jsreport.Types;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var rs = new LocalReporting()
            .UseBinary(JsReportBinary.GetBinary())
            .AsUtility()
            .Create();

        var report = await rs.RenderAsync(new RenderRequest()
        {
            Template = new Template()
            {
                Recipe = Recipe.ChromePdf,
                Engine = Engine.None,
                Content = "<h1>Hello from jsreport</h1><p>This is a PDF document.</p>"
            }
        });

        using (var fileStream = File.Create("output.pdf"))
        {
            report.Content.CopyTo(fileStream);
        }

        Console.WriteLine("PDF created successfully!");
    }
}

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderHtmlAsPdf("<h1>Hello from IronPDF</h1><p>This is a PDF document.</p>");
        pdf.SaveAs("output.pdf");
        Console.WriteLine("PDF created successfully!");
    }
}

The jsreport approach requires three NuGet packages (jsreport.Binary, jsreport.Local, jsreport.Types), three namespace imports, async-only execution, a fluent builder chain (LocalReporting().UseBinary().AsUtility().Create()), a verbose RenderRequest with nested Template object specifying Recipe and Engine, and manual stream-to-file copying with a using block.

IronPDF reduces this to one NuGet package, one namespace, three lines of code, and synchronous execution. The ChromePdfRenderer.RenderHtmlAsPdf() method accepts HTML directly and returns a PdfDocument with a simple SaveAs() method. See the HTML to PDF documentation for additional rendering options.

Example 2: URL to PDF

Before (jsreport):

// NuGet: Install-Package jsreport.Binary
// NuGet: Install-Package jsreport.Local
// NuGet: Install-Package jsreport.Types
using jsreport.Binary;
using jsreport.Local;
using jsreport.Types;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var rs = new LocalReporting()
            .UseBinary(JsReportBinary.GetBinary())
            .AsUtility()
            .Create();

        var report = await rs.RenderAsync(new RenderRequest()
        {
            Template = new Template()
            {
                Recipe = Recipe.ChromePdf,
                Engine = Engine.None,
                Content = "<html><body><script>window.location='https://example.com';</script></body></html>"
            }
        });

        using (var fileStream = File.Create("webpage.pdf"))
        {
            report.Content.CopyTo(fileStream);
        }

        Console.WriteLine("Webpage PDF created successfully!");
    }
}

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderUrlAsPdf("https://example.com");
        pdf.SaveAs("webpage.pdf");
        Console.WriteLine("Webpage PDF created successfully!");
    }
}

jsreport can render a URL by setting Chrome.Url on the request, but there is no top-level convenience method - and a common pattern in existing code is the JavaScript redirect workaround (window.location='https://example.com') shown above, which can fail on sites that block scripted navigation.

IronPDF provides a dedicated RenderUrlAsPdf() method that takes the URL as a direct argument, with full JavaScript execution and modern CSS support. Learn more about URL to PDF conversion.

Example 3: PDF with Headers and Footers

Before (jsreport):

// NuGet: Install-Package jsreport.Binary
// NuGet: Install-Package jsreport.Local
// NuGet: Install-Package jsreport.Types
using jsreport.Binary;
using jsreport.Local;
using jsreport.Types;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var rs = new LocalReporting()
            .UseBinary(JsReportBinary.GetBinary())
            .AsUtility()
            .Create();

        var report = await rs.RenderAsync(new RenderRequest()
        {
            Template = new Template()
            {
                Recipe = Recipe.ChromePdf,
                Engine = Engine.None,
                Content = "<h1>Document with Header and Footer</h1><p>Main content goes here.</p>",
                Chrome = new Chrome()
                {
                    DisplayHeaderFooter = true,
                    HeaderTemplate = "<div style='font-size:10px; text-align:center; width:100%;'>Custom Header</div>",
                    FooterTemplate = "<div style='font-size:10px; text-align:center; width:100%;'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>"
                }
            }
        });

        using (var fileStream = File.Create("document_with_headers.pdf"))
        {
            report.Content.CopyTo(fileStream);
        }

        Console.WriteLine("PDF with headers and footers created successfully!");
    }
}

After (IronPDF):

// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
        {
            CenterText = "Custom Header",
            FontSize = 10
        };
        renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
        {
            CenterText = "Page {page} of {total-pages}",
            FontSize = 10
        };
        
        var pdf = renderer.RenderHtmlAsPdf("<h1>Document with Header and Footer</h1><p>Main content goes here.</p>");
        pdf.SaveAs("document_with_headers.pdf");
        Console.WriteLine("PDF with headers and footers created successfully!");
    }
}

The jsreport approach requires adding a Chrome object to the Template, setting DisplayHeaderFooter = true, and using HTML templates with special CSS class placeholders (<span class='pageNumber'></span>, <span class='totalPages'></span>). The header and footer templates must include full inline styling.

IronPDF provides a cleaner TextHeaderFooter configuration with dedicated properties for CenterText, LeftText, RightText, and FontSize. Page number placeholders use the simpler {page} and {total-pages} syntax. See the headers and footers documentation for HTML header options.


Critical Migration Notes

Eliminate Server Lifecycle Management

jsreport requires explicit server lifecycle management:

// jsreport (DELETE THIS):
var rs = new LocalReporting()
    .UseBinary(JsReportBinary.GetBinary())
    .AsUtility()
    .Create();

// Or for web server mode:
var rs = new LocalReporting()
    .UseBinary(JsReportBinary.GetBinary())
    .AsWebServer()
    .Create();

await rs.StartAsync();
// ... use rs ...
await rs.KillAsync();

IronPDF runs entirely in-process - no server startup, no process management, no cleanup:

// IronPDF:
var renderer = new ChromePdfRenderer();
// Just use it—no lifecycle management needed

Remove Platform-Specific Binary Packages

jsreport requires separate NuGet packages for each target platform:

# DELETE these platform-specific packages:
dotnet remove package jsreport.Binary
dotnet remove package jsreport.Binary.Linux
dotnet remove package jsreport.Binary.OSX
SHELL

IronPDF handles all platform requirements automatically through a single NuGet package.

Update Placeholder Syntax

jsreport uses CSS class-based or curly brace placeholders. IronPDF uses a different syntax:

// jsreport placeholders:
"<span class='pageNumber'></span>"  // or {#pageNum}
"<span class='totalPages'></span>"  // or {#numPages}

// IronPDF placeholders:
"{page}"
"{total-pages}"
"{date}"
"{html-title}"

Replace Handlebars with C# String Interpolation

jsreport often uses Handlebars templating with Engine.Handlebars:

// jsreport Handlebars (DELETE THIS):
Template = new Template
{
    Content = "<h1>Hello, {{name}}</h1>",
    Engine = Engine.Handlebars
},
Data = new { name = "World" }

// IronPDF with C# string interpolation:
string name = "World";
string html = $"<h1>Hello, {name}</h1>";
var pdf = renderer.RenderHtmlAsPdf(html);

Simplify Stream Handling

jsreport returns a stream that requires manual copying:

// jsreport stream handling (DELETE THIS):
using (var fileStream = File.Create("output.pdf"))
{
    report.Content.CopyTo(fileStream);
}

// Or for byte array:
using (var memoryStream = new MemoryStream())
{
    await report.Content.CopyToAsync(memoryStream);
    return memoryStream.ToArray();
}

// IronPDF direct access:
pdf.SaveAs("output.pdf");
// Or:
byte[] bytes = pdf.BinaryData;

Troubleshooting

Issue 1: LocalReporting Not Found

Problem: Code references LocalReporting class which doesn't exist in IronPDF.

Solution: Replace with ChromePdfRenderer:

// jsreport
var rs = new LocalReporting().UseBinary().AsUtility().Create();

// IronPDF
var renderer = new ChromePdfRenderer();

Issue 2: RenderRequest Not Found

Problem: Code uses RenderRequest and Template wrapper objects.

Solution: Pass HTML directly to render methods:

// jsreport
await rs.RenderAsync(new RenderRequest { Template = new Template { Content = html } });

// IronPDF
var pdf = renderer.RenderHtmlAsPdf(html);

Issue 3: Page Numbers Not Appearing

Problem: Using jsreport placeholder syntax <span class='pageNumber'></span>.

Solution: Update to IronPDF placeholder syntax:

// jsreport syntax (won't work)
"Page <span class='pageNumber'></span> of <span class='totalPages'></span>"

// IronPDF syntax
"Page {page} of {total-pages}"

Issue 4: JsReportBinary Not Found

Problem: Code references JsReportBinary.GetBinary().

Solution: Delete entirely - IronPDF doesn't require external binaries:

// DELETE this jsreport pattern:
.UseBinary(JsReportBinary.GetBinary())

// IronPDF needs nothing—just create the renderer:
var renderer = new ChromePdfRenderer();

Migration Checklist

Pre-Migration

  • Identify all jsreport using statements
  • List templates using Handlebars/JsRender (convert to C# string interpolation)
  • Document current Chrome options used (margins, paper size)
  • Check for web server vs utility mode (both become in-process)
  • Note platform-specific binary packages (delete all)
  • Obtain IronPDF license key

Package Changes

  • Remove jsreport.Binary package
  • Remove jsreport.Binary.Linux package
  • Remove jsreport.Binary.OSX package
  • Remove jsreport.Local package
  • Remove jsreport.Types package
  • Remove jsreport.Client package
  • Install IronPdf package

Code Changes

  • Add license key configuration at startup
  • Replace LocalReporting with ChromePdfRenderer
  • Remove RenderRequest wrapper
  • Remove Template wrapper
  • Update placeholder syntax (<span class='pageNumber'>{page})
  • Replace Handlebars with C# string interpolation
  • Remove StartAsync() / KillAsync() calls
  • Replace stream copying with BinaryData or SaveAs()

Testing

  • Test all PDF generation paths
  • Verify header/footer rendering
  • Check page numbering
  • Validate margin spacing
  • Test with complex CSS/JavaScript pages
  • Benchmark performance

Post-Migration

  • Delete jsreport binary files
  • Remove Node.js dependencies if no longer needed
  • Update deployment scripts
  • Update documentation

Please note: jsreport is a registered trademark of its respective owner. This site is not affiliated with, endorsed by, or sponsored by jsreport. All product names, logos, and brands are property of their respective owners. Comparisons are for informational purposes only and reflect publicly available information at the time of writing.
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

Related Articles

Key in blue circle

Get your free 30-day Trial Key instantly.

Your trial license will be sent to your email address

No limitations. 100% unlocked. No credit card.

bullet_checkedNo credit card or account creation requiredNo limitations. 100% unlocked. No credit card.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
Book your free Live Demo
Booking Badge

Trusted by Millions of Engineers Worldwide

Iron Software's customer logos
Get Your No-Obligation Consult
Complete the form below or email sales@ironsoftware.com
Your details will always be kept confidential.
Trusted by Millions of Engineers Worldwide
Iron Software's customer logos
Get your free 30-day Trial Key instantly.
No credit card or account creation required