How to Convert HTML Elements and Partial Page Sections to PDF in C#

IronPDF does not expose a built-in SelectElement or SelectCss method for targeting specific HTML elements. The ChromePdfRenderer renders complete HTML documents — a full page, a full URL, a full HTML string. To produce a PDF from a specific section of a page, we isolate the target element before rendering using one of four approaches: JavaScript DOM manipulation, CSS injection, server-side HTML fragment extraction, or URL rendering with JS targeting.

Each approach suits a different constraint. JavaScript DOM isolation works when rendering URLs or full pages where we need to strip everything except the target. CSS injection hides unwanted content without altering the DOM. Server-side extraction provides the cleanest result when we have access to the raw HTML. URL rendering with JS targeting handles live dashboards and third-party pages where the source HTML is not available.

Start a free 30-day trial to test all four approaches.

Quickstart: Extract a Specific HTML Element as PDF

Target any element by CSS selector using JavaScript DOM isolation and WaitFor, then render only that fragment to PDF.

  1. Install IronPDF with NuGet Package Manager

    PM > Install-Package IronPdf
  2. Copy and run this code snippet.

    using IronPdf;
    
    var renderer = new ChromePdfRenderer();
    renderer.RenderingOptions.EnableJavaScript = true;
    renderer.RenderingOptions.Javascript = @"
        var target = document.querySelector('#invoice-summary');
        document.body.innerHTML = target.outerHTML;
    ";
    renderer.RenderingOptions.WaitFor.HtmlQuerySelector("#invoice-summary", 10000);
    
    var pdf = renderer.RenderHtmlAsPdf(fullPageHtml);
    pdf.SaveAs("invoice-summary.pdf");
  3. Deploy to test on your live environment

    Start using IronPDF in your project today with a free trial

    arrow pointer

Minimal Workflow (3 Steps)

  1. Install IronPDF via NuGet: Install-Package IronPdf
  2. Configure ChromePdfRenderOptions.Javascript to isolate the target element and WaitFor to ensure it exists
  3. Call RenderHtmlAsPdf() or RenderUrlAsPdf() — the PDF contains only the isolated content

How to Isolate Elements with JavaScript DOM Manipulation?

The ChromePdfRenderOptions.Javascript property accepts a string of JavaScript that executes after the HTML loads but before the PDF renders. By replacing document.body.innerHTML with the outerHTML of the target element, we strip everything else from the rendered page. This is the most versatile approach — it works with both RenderHtmlAsPdf() and RenderUrlAsPdf().

The WaitFor.HtmlQuerySelector() method ensures the target element exists in the DOM before the JavaScript runs. This is critical for pages with asynchronous content — React components, Angular templates, or API-driven data that populates after the initial page load.

using IronPdf;

string fullPageHtml = @"
<html>
<body>
    <header><h1>Acme Corp Invoice</h1></header>
    <nav>Navigation links...</nav>
    <div id='invoice-summary'>
        <h2>Invoice #12345</h2>
        <table>
            <tr><td>Widget A</td><td>$49.99</td></tr>
            <tr><td>Widget B</td><td>$29.99</td></tr>
            <tr><td><strong>Total</strong></td><td><strong>$79.98</strong></td></tr>
        </table>
    </div>
    <footer>Footer content...</footer>
</body>
</html>";

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;

// Replace the body with only the target element
renderer.RenderingOptions.Javascript = @"
    var el = document.querySelector('#invoice-summary');
    if (el) {
        document.body.innerHTML = el.outerHTML;
    }
";

// Wait for the target element to exist before JS executes
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("#invoice-summary", 10000);

PdfDocument pdf = renderer.RenderHtmlAsPdf(fullPageHtml);
pdf.SaveAs("invoice-summary-only.pdf");
using IronPdf;

string fullPageHtml = @"
<html>
<body>
    <header><h1>Acme Corp Invoice</h1></header>
    <nav>Navigation links...</nav>
    <div id='invoice-summary'>
        <h2>Invoice #12345</h2>
        <table>
            <tr><td>Widget A</td><td>$49.99</td></tr>
            <tr><td>Widget B</td><td>$29.99</td></tr>
            <tr><td><strong>Total</strong></td><td><strong>$79.98</strong></td></tr>
        </table>
    </div>
    <footer>Footer content...</footer>
</body>
</html>";

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;

// Replace the body with only the target element
renderer.RenderingOptions.Javascript = @"
    var el = document.querySelector('#invoice-summary');
    if (el) {
        document.body.innerHTML = el.outerHTML;
    }
";

// Wait for the target element to exist before JS executes
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("#invoice-summary", 10000);

PdfDocument pdf = renderer.RenderHtmlAsPdf(fullPageHtml);
pdf.SaveAs("invoice-summary-only.pdf");
Imports IronPdf

Dim fullPageHtml As String = "
<html>
<body>
    <header><h1>Acme Corp Invoice</h1></header>
    <nav>Navigation links...</nav>
    <div id='invoice-summary'>
        <h2>Invoice #12345</h2>
        <table>
            <tr><td>Widget A</td><td>$49.99</td></tr>
            <tr><td>Widget B</td><td>$29.99</td></tr>
            <tr><td><strong>Total</strong></td><td><strong>$79.98</strong></td></tr>
        </table>
    </div>
    <footer>Footer content...</footer>
</body>
</html>"

Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.EnableJavaScript = True

' Replace the body with only the target element
renderer.RenderingOptions.Javascript = "
    var el = document.querySelector('#invoice-summary');
    if (el) {
        document.body.innerHTML = el.outerHTML;
    }
"

' Wait for the target element to exist before JS executes
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("#invoice-summary", 10000)

Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fullPageHtml)
pdf.SaveAs("invoice-summary-only.pdf")
$vbLabelText   $csharpLabel

The JavaScript replaces the entire body with the #invoice-summary div's outerHTML. The resulting PDF contains only the invoice table — no header, no navigation, no footer. The WaitFor.HtmlElementById() method provides a simpler alternative when targeting by ID:

// Alternative: wait by ID directly
renderer.RenderingOptions.WaitFor.HtmlElementById("invoice-summary", 10000);
// Alternative: wait by ID directly
renderer.RenderingOptions.WaitFor.HtmlElementById("invoice-summary", 10000);
' Alternative: wait by ID directly
renderer.RenderingOptions.WaitFor.HtmlElementById("invoice-summary", 10000)
$vbLabelText   $csharpLabel

For complex selectors (class names, data attributes, nested elements), HtmlQuerySelector() accepts any valid CSS selector string that document.querySelector() would accept. Additional WaitFor convenience methods include HtmlElementByClassName(), HtmlElementByName(), and HtmlElementByTagName() — each internally delegates to HtmlQuerySelector() but provides clearer intent in the code.

When the target element depends on inherited styles from parent containers, the outerHTML replacement can lose CSS rules that rely on ancestor selectors (e.g., .dashboard .widget table { ... }). To preserve these, copy the relevant <style> and <link> tags from the <head> into the JS isolation:

renderer.RenderingOptions.Javascript = @"
    var el = document.querySelector('#invoice-summary');
    if (el) {
        var head = document.head.innerHTML;
        document.body.innerHTML = el.outerHTML;
        document.head.innerHTML = head;
    }
";
renderer.RenderingOptions.Javascript = @"
    var el = document.querySelector('#invoice-summary');
    if (el) {
        var head = document.head.innerHTML;
        document.body.innerHTML = el.outerHTML;
        document.head.innerHTML = head;
    }
";
renderer.RenderingOptions.Javascript = "
    var el = document.querySelector('#invoice-summary');
    if (el) {
        var head = document.head.innerHTML;
        document.body.innerHTML = el.outerHTML;
        document.head.innerHTML = head;
    }
"
$vbLabelText   $csharpLabel

This retains the original <head> content (stylesheets, fonts, meta tags) while replacing only the body. The JavaScript to PDF how-to and the WaitFor how-to cover additional configuration options including NetworkIdle0() for pages with multiple async data sources.

How to Isolate Elements with CSS Injection?

The ChromePdfRenderOptions.CustomCssUrl property accepts a file path or URL to a stylesheet that IronPDF applies before rendering. Instead of manipulating the DOM, we hide everything except the target element using CSS display: none. This preserves the original DOM structure and avoids JavaScript execution entirely.

using IronPdf;

// Create a CSS file that hides everything except #invoice-summary
string cssContent = @"
body > *:not(#invoice-summary) {
    display: none !important;
}
#invoice-summary {
    display: block !important;
    margin: 0;
    padding: 20px;
}
";
File.WriteAllText("isolate-element.css", cssContent);

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CustomCssUrl = "isolate-element.css";

PdfDocument pdf = renderer.RenderHtmlAsPdf(fullPageHtml);
pdf.SaveAs("invoice-css-isolated.pdf");
using IronPdf;

// Create a CSS file that hides everything except #invoice-summary
string cssContent = @"
body > *:not(#invoice-summary) {
    display: none !important;
}
#invoice-summary {
    display: block !important;
    margin: 0;
    padding: 20px;
}
";
File.WriteAllText("isolate-element.css", cssContent);

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CustomCssUrl = "isolate-element.css";

PdfDocument pdf = renderer.RenderHtmlAsPdf(fullPageHtml);
pdf.SaveAs("invoice-css-isolated.pdf");
Imports IronPdf
Imports System.IO

' Create a CSS file that hides everything except #invoice-summary
Dim cssContent As String = "
body > *:not(#invoice-summary) {
    display: none !important;
}
#invoice-summary {
    display: block !important;
    margin: 0;
    padding: 20px;
}
"
File.WriteAllText("isolate-element.css", cssContent)

Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.CustomCssUrl = "isolate-element.css"

Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fullPageHtml)
pdf.SaveAs("invoice-css-isolated.pdf")
$vbLabelText   $csharpLabel

Note: The CustomCssUrl property only works with RenderHtmlAsPdf() when rendering from HTML strings. For URL rendering, embed the CSS injection into the Javascript property instead:

renderer.RenderingOptions.Javascript = @"
    var style = document.createElement('style');
    style.textContent = 'body > *:not(#invoice-summary) { display: none !important; }';
    document.head.appendChild(style);
";
renderer.RenderingOptions.Javascript = @"
    var style = document.createElement('style');
    style.textContent = 'body > *:not(#invoice-summary) { display: none !important; }';
    document.head.appendChild(style);
";
renderer.RenderingOptions.Javascript = "
    var style = document.createElement('style');
    style.textContent = 'body > *:not(#invoice-summary) { display: none !important; }';
    document.head.appendChild(style);
"
$vbLabelText   $csharpLabel

When we control the source HTML, @media print rules offer the lightest alternative — no external dependencies, no runtime injection:

@media print {
    header, nav, footer, .sidebar { display: none !important; }
    #invoice-summary { width: 100%; margin: 0; }
}

Set CssMediaType to PdfCssMediaType.Print on the renderer to activate these rules:

renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print
$vbLabelText   $csharpLabel

This is ideal for form section printing scenarios where the application already defines print-specific layouts. Insurance claim forms, multi-step wizards, and checkout summaries commonly use @media print rules to hide navigation and progress indicators while expanding the relevant content section to full width.

The CSS approach has one important limitation: hidden elements still occupy space in the document flow if display: none is used at the wrong specificity level. Always use !important to override framework styles (Bootstrap, Tailwind) that might re-display elements at certain breakpoints. For deeply nested targets, a more precise selector avoids collateral hiding:

body > *:not(#target),
body > *:not(#target) ~ * {
    display: none !important;
}

How to Extract HTML Fragments Server-Side?

When we have access to the raw HTML — reading it from a file, a database, a CMS, or an HTTP response — the cleanest approach is to extract the target element server-side using an HTML parser, then pass the fragment to RenderHtmlAsPdf(). No JavaScript, no CSS injection, no runtime DOM manipulation.

AngleSharp is the standard .NET HTML parser for this pattern:

using IronPdf;
using AngleSharp;
using AngleSharp.Html.Parser;

string fullPageHtml = @"
<html>
<head>
    <style>
        table { border-collapse: collapse; width: 100%; }
        td, th { border: 1px solid #ddd; padding: 8px; text-align: left; }
    </style>
</head>
<body>
    <header><h1>Dashboard</h1></header>
    <div id='revenue-widget'>
        <h3>Q4 Revenue</h3>
        <table>
            <tr><th>Month</th><th>Revenue</th></tr>
            <tr><td>October</td><td>$1.2M</td></tr>
            <tr><td>November</td><td>$1.5M</td></tr>
            <tr><td>December</td><td>$1.8M</td></tr>
        </table>
    </div>
    <div id='other-content'>Other widgets...</div>
</body>
</html>";

// Parse and extract the target element
var parser = new HtmlParser();
var document = parser.ParseDocument(fullPageHtml);
var targetElement = document.QuerySelector("#revenue-widget");

if (targetElement is null)
{
    Console.WriteLine("Target element not found.");
    return;
}

// Wrap the fragment in a minimal HTML document to preserve styles
string fragmentHtml = $@"
<html>
<head>
    <style>
        table {{ border-collapse: collapse; width: 100%; }}
        td, th {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
    </style>
</head>
<body>
    {targetElement.OuterHtml}
</body>
</html>";

var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml);
pdf.SaveAs("revenue-widget.pdf");
using IronPdf;
using AngleSharp;
using AngleSharp.Html.Parser;

string fullPageHtml = @"
<html>
<head>
    <style>
        table { border-collapse: collapse; width: 100%; }
        td, th { border: 1px solid #ddd; padding: 8px; text-align: left; }
    </style>
</head>
<body>
    <header><h1>Dashboard</h1></header>
    <div id='revenue-widget'>
        <h3>Q4 Revenue</h3>
        <table>
            <tr><th>Month</th><th>Revenue</th></tr>
            <tr><td>October</td><td>$1.2M</td></tr>
            <tr><td>November</td><td>$1.5M</td></tr>
            <tr><td>December</td><td>$1.8M</td></tr>
        </table>
    </div>
    <div id='other-content'>Other widgets...</div>
</body>
</html>";

// Parse and extract the target element
var parser = new HtmlParser();
var document = parser.ParseDocument(fullPageHtml);
var targetElement = document.QuerySelector("#revenue-widget");

if (targetElement is null)
{
    Console.WriteLine("Target element not found.");
    return;
}

// Wrap the fragment in a minimal HTML document to preserve styles
string fragmentHtml = $@"
<html>
<head>
    <style>
        table {{ border-collapse: collapse; width: 100%; }}
        td, th {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
    </style>
</head>
<body>
    {targetElement.OuterHtml}
</body>
</html>";

var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml);
pdf.SaveAs("revenue-widget.pdf");
Imports IronPdf
Imports AngleSharp
Imports AngleSharp.Html.Parser

Dim fullPageHtml As String = "
<html>
<head>
    <style>
        table { border-collapse: collapse; width: 100%; }
        td, th { border: 1px solid #ddd; padding: 8px; text-align: left; }
    </style>
</head>
<body>
    <header><h1>Dashboard</h1></header>
    <div id='revenue-widget'>
        <h3>Q4 Revenue</h3>
        <table>
            <tr><th>Month</th><th>Revenue</th></tr>
            <tr><td>October</td><td>$1.2M</td></tr>
            <tr><td>November</td><td>$1.5M</td></tr>
            <tr><td>December</td><td>$1.8M</td></tr>
        </table>
    </div>
    <div id='other-content'>Other widgets...</div>
</body>
</html>"

' Parse and extract the target element
Dim parser As New HtmlParser()
Dim document = parser.ParseDocument(fullPageHtml)
Dim targetElement = document.QuerySelector("#revenue-widget")

If targetElement Is Nothing Then
    Console.WriteLine("Target element not found.")
    Return
End If

' Wrap the fragment in a minimal HTML document to preserve styles
Dim fragmentHtml As String = $"
<html>
<head>
    <style>
        table {{ border-collapse: collapse; width: 100%; }}
        td, th {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
    </style>
</head>
<body>
    {targetElement.OuterHtml}
</body>
</html>"

Dim renderer As New ChromePdfRenderer()
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fragmentHtml)
pdf.SaveAs("revenue-widget.pdf")
$vbLabelText   $csharpLabel

The key detail is wrapping the extracted fragment in a complete HTML document with the relevant <style> or <link> tags. Without this wrapper, inline styles render correctly, but external stylesheets and inherited CSS rules are lost. For email template preview rendering — where the template HTML is already stored as a string — this extraction pattern gives pixel-perfect results because we control every aspect of the rendered content.

The same pattern works with HtmlAgilityPack as an alternative parser:

using HtmlAgilityPack;
using IronPdf;

var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(fullPageHtml);

var targetNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='revenue-widget']");
if (targetNode is null)
{
    Console.WriteLine("Target element not found.");
    return;
}

string fragmentHtml = $"<html><body>{targetNode.OuterHtml}</body></html>";

var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml);
pdf.SaveAs("revenue-widget-hap.pdf");
using HtmlAgilityPack;
using IronPdf;

var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(fullPageHtml);

var targetNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='revenue-widget']");
if (targetNode is null)
{
    Console.WriteLine("Target element not found.");
    return;
}

string fragmentHtml = $"<html><body>{targetNode.OuterHtml}</body></html>";

var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml);
pdf.SaveAs("revenue-widget-hap.pdf");
Imports HtmlAgilityPack
Imports IronPdf

Dim htmlDoc As New HtmlDocument()
htmlDoc.LoadHtml(fullPageHtml)

Dim targetNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='revenue-widget']")
If targetNode Is Nothing Then
    Console.WriteLine("Target element not found.")
    Return
End If

Dim fragmentHtml As String = $"<html><body>{targetNode.OuterHtml}</body></html>"

Dim renderer As New ChromePdfRenderer()
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fragmentHtml)
pdf.SaveAs("revenue-widget-hap.pdf")
$vbLabelText   $csharpLabel

The choice between AngleSharp and HtmlAgilityPack is mostly a preference. AngleSharp uses CSS selectors (QuerySelector), which aligns with frontend developer mental models. HtmlAgilityPack uses XPath (SelectSingleNode), which is more familiar in XML-heavy .NET codebases.

For email template preview rendering — where template HTML is stored as a string in a database or CMS — server-side extraction gives pixel-perfect results because we control every aspect of the rendered content. We can also use RenderHtmlAsPdf(string Html, string BaseUrlOrPath) with the second parameter pointing to a local asset directory, ensuring images and stylesheets referenced by relative paths resolve correctly:

PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml, @"C:\templates\assets\");
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml, @"C:\templates\assets\");
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fragmentHtml, "C:\templates\assets\")
$vbLabelText   $csharpLabel

The trade-off versus JavaScript DOM isolation is that server-side extraction cannot handle content that requires client-side rendering (JavaScript-generated elements, SPA components, API-fetched data). If the HTML contains <div id="app"></div> that a React or Vue application populates at runtime, the extracted fragment will be empty. For those cases, use Approach 1 or 4.

How to Target Elements When Rendering Live URLs?

For live URLs where we cannot access the source HTML — third-party dashboards, external reports, hosted applications — we combine RenderUrlAsPdf() with the Javascript property and WaitFor to isolate a specific section after the page loads.

This is the dashboard widget export scenario: a BI tool renders charts and tables on a web page, and we need to export a single widget as a PDF for stakeholder distribution.

using IronPdf;

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;

// Wait for the dashboard widget to render (async content)
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("[data-widget='revenue-chart']", 15000);

// Isolate the widget after it renders
renderer.RenderingOptions.Javascript = @"
    var widget = document.querySelector('[data-widget=""revenue-chart""]');
    if (widget) {
        // Preserve computed styles by cloning into a clean body
        document.body.innerHTML = '';
        document.body.appendChild(widget);
    }
";

PdfDocument pdf = renderer.RenderUrlAsPdf("https://dashboard.example.com/q4-report");
pdf.SaveAs("revenue-chart-export.pdf");
using IronPdf;

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;

// Wait for the dashboard widget to render (async content)
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("[data-widget='revenue-chart']", 15000);

// Isolate the widget after it renders
renderer.RenderingOptions.Javascript = @"
    var widget = document.querySelector('[data-widget=""revenue-chart""]');
    if (widget) {
        // Preserve computed styles by cloning into a clean body
        document.body.innerHTML = '';
        document.body.appendChild(widget);
    }
";

PdfDocument pdf = renderer.RenderUrlAsPdf("https://dashboard.example.com/q4-report");
pdf.SaveAs("revenue-chart-export.pdf");
Imports IronPdf

Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.EnableJavaScript = True

' Wait for the dashboard widget to render (async content)
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("[data-widget='revenue-chart']", 15000)

' Isolate the widget after it renders
renderer.RenderingOptions.Javascript = "
    var widget = document.querySelector('[data-widget=""revenue-chart""]);
    if (widget) {
        // Preserve computed styles by cloning into a clean body
        document.body.innerHTML = '';
        document.body.appendChild(widget);
    }
"

Dim pdf As PdfDocument = renderer.RenderUrlAsPdf("https://dashboard.example.com/q4-report")
pdf.SaveAs("revenue-chart-export.pdf")
$vbLabelText   $csharpLabel

The WaitFor.HtmlQuerySelector() call ensures the widget exists in the DOM before the JavaScript executes. The 15-second timeout accommodates slower API calls that populate dashboard data. The JavaScript then strips the page down to just the widget.

For pages with complex CSS dependencies, the appendChild approach (moving the node rather than copying its outerHTML) preserves more computed styles because the element retains its position in the CSSOM. The innerHTML replacement approach from Approach 1 is simpler but may lose styles that depend on ancestor selectors.

When the target page requires authentication, configure cookies on the renderer before calling RenderUrlAsPdf():

renderer.RenderingOptions.CustomCookies = new Dictionary<string, string>
{
    { "session_id", "abc123" },
    { "auth_token", "bearer-xyz" }
};
renderer.RenderingOptions.CustomCookies = new Dictionary<string, string>
{
    { "session_id", "abc123" },
    { "auth_token", "bearer-xyz" }
};
Imports System.Collections.Generic

renderer.RenderingOptions.CustomCookies = New Dictionary(Of String, String) From {
    {"session_id", "abc123"},
    {"auth_token", "bearer-xyz"}
}
$vbLabelText   $csharpLabel

The WaitFor class offers additional wait strategies beyond HtmlQuerySelector(). NetworkIdle0() waits until all network requests complete with zero outstanding connections — useful for dashboards that load data from multiple API endpoints. NetworkIdle2() tolerates up to two outstanding connections, which handles pages with persistent WebSocket connections or long-polling. JavaScript() waits for the page to call window.ironpdf.notifyRender() — the most precise option when we control the target page and can signal render-readiness explicitly after all data loads and animations complete.

For recurring dashboard exports (e.g., nightly PDF generation for stakeholder email distribution), wrap the rendering in a retry loop that catches timeout exceptions. If WaitFor exceeds its maxWaitTime, IronPDF proceeds with rendering whatever content is available — which may be incomplete. Increasing the timeout or switching to NetworkIdle0() typically resolves intermittent failures on slower networks.

Comparison of All Four Approaches

Approach Best For Requires Source HTML JS Dependency Complexity
JavaScript DOM Isolation General-purpose element extraction from any source No Yes Medium
CSS Injection Hiding sections without DOM changes; @media print layouts Partial (CustomCssUrl needs RenderHtmlAsPdf) No (unless injecting via JS for URLs) Low
Server-Side Fragment Extraction CMS content, stored templates, email previews Yes No Low–Medium
URL Rendering with JS Targeting Live dashboards, third-party pages, SPA widgets No Yes Medium–High

Choosing the Right Approach

The decision depends on two factors: whether we have access to the raw HTML, and whether the target content requires JavaScript to render.

Invoice line-item extraction is the most common use case. When the invoice HTML is generated server-side (Razor views, Handlebars templates, stored HTML strings), Approach 3 (server-side extraction) gives the cleanest result with zero runtime overhead. Extract the #line-items table, wrap it in a styled HTML shell, and render.

Dashboard widget export requires JavaScript execution because the widget content is populated by API calls after the initial page load. Approach 1 (JS DOM isolation) handles this when the dashboard runs locally or behind authentication. Approach 4 (URL rendering with JS targeting) is necessary when the dashboard is a third-party hosted application where we only have the URL.

Form section printing — extracting a specific section of a multi-step form for user review or compliance archival — maps naturally to Approach 2 (CSS injection) when the application already defines @media print rules, or Approach 3 when the form HTML is assembled server-side.

Email template preview rendering — generating a PDF preview of an HTML email template before sending — is a pure Approach 3 scenario. The template HTML is a stored string, external resources (images, fonts) are hosted at known URLs, and the BaseUrlOrPath parameter on RenderHtmlAsPdf() resolves all relative paths.

For applications that need to support multiple scenarios, encapsulate the rendering logic behind a service interface that accepts a strategy parameter:

public enum ElementExtractionStrategy
{
    JavaScriptIsolation,
    CssInjection,
    ServerSideExtraction,
    UrlWithJsTargeting
}
public enum ElementExtractionStrategy
{
    JavaScriptIsolation,
    CssInjection,
    ServerSideExtraction,
    UrlWithJsTargeting
}
Public Enum ElementExtractionStrategy
    JavaScriptIsolation
    CssInjection
    ServerSideExtraction
    UrlWithJsTargeting
End Enum
$vbLabelText   $csharpLabel

This lets the calling code select the appropriate approach based on the input type without duplicating renderer configuration.

Next Steps

HTML element isolation in IronPDF is a rendering-time concern, not a built-in API feature. The four approaches above cover the full spectrum — from server-side template extraction (zero JS, cleanest output) to live URL targeting (full JS execution, handles SPAs and async content). The comparison table provides a quick reference, and the real-world scenarios map common business requirements to the appropriate strategy.

A few additional considerations for production deployments:

Performance: Server-side extraction (Approach 3) is the fastest because it skips JavaScript execution entirely. JavaScript-based approaches (1 and 4) add overhead proportional to the page complexity and the WaitFor timeout. For batch processing (e.g., generating 500 invoice PDFs), server-side extraction with Parallel.ForEach and multiple ChromePdfRenderer instances provides the best throughput.

Debugging: When the PDF output is blank or missing content, enable EnableJavaScript = true and increase the WaitFor timeout. If the target element depends on async data, WaitFor.NetworkIdle0() is more reliable than a fixed RenderDelay. The rendering options guide covers viewport width and paper fit configurations that affect layout.

Combining approaches: Nothing prevents mixing strategies. We can use server-side extraction to build a composite HTML document from multiple fragments (header from one source, data table from another, chart SVG from a third), then render the assembled document as a single PDF. The RenderHtmlAsPdf(string Html, string BaseUrlOrPath) method resolves relative asset paths from the base URL, making it straightforward to compose documents from heterogeneous sources.

Explore the JavaScript to PDF how-to for advanced JS execution patterns, the WaitFor documentation for all available wait strategies, the rendering options guide for the complete ChromePdfRenderOptions surface, and the custom JavaScript code example for ready-to-run snippets.

View licensing options starting at $749. The ChromePdfRenderOptions API reference and WaitFor API reference document every property and method.

Frequently Asked Questions

What are the main approaches for converting HTML elements to PDF in C#?

The main approaches include JS isolation, CSS hiding, server-side extraction, and live URL targeting, all of which can be implemented using IronPDF.

How does JS isolation work in the context of HTML to PDF conversion?

JS isolation involves running JavaScript to dynamically manipulate the HTML document before converting it to PDF. This can be achieved using IronPDF to ensure only specific elements are rendered.

What is CSS hiding and how is it used in PDF conversion?

CSS hiding involves using CSS styles to hide elements that should not appear in the PDF. IronPDF supports this by allowing developers to specify stylesheets or style rules during the conversion process.

Can IronPDF extract specific HTML elements server-side for PDF generation?

Yes, IronPDF can extract specific HTML elements server-side, allowing for precise control over which parts of a webpage are converted into a PDF.

What is the advantage of live URL targeting in HTML to PDF conversion?

Live URL targeting allows IronPDF to convert elements from a live webpage URL directly to PDF, ensuring the most current content is captured without the need for local HTML files.

Is it possible to convert only a section of a webpage to PDF using IronPDF?

Yes, IronPDF provides functionality to convert specific sections or elements of a webpage to PDF, making it easy to focus on relevant content.

How can IronPDF handle dynamic content during HTML to PDF conversion?

IronPDF can render dynamic content by executing JavaScript during conversion, ensuring that elements that rely on client-side scripts are accurately represented in the PDF.

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
Ready to Get Started?
Nuget Downloads 17,789,030 | Version: 2026.3 just released
Still Scrolling Icon

Still Scrolling?

Want proof fast? PM > Install-Package IronPdf
run a sample watch your HTML become a PDF.