Wait Strategies
CobaltPdf must know when the page is "ready" before it captures the PDF. The right strategy depends on how the page loads its content.
Overview
| Strategy | Factory method | Best for |
|---|---|---|
| Network Idle (default) | (automatic) | Most pages — waits until no network requests for 500 ms |
| Fixed Delay | WaitOptions.ForDelay(timeSpan) |
Pages with known animation/transition durations |
| CSS Selector | WaitOptions.ForSelector(selector) |
Pages that insert a sentinel element when ready |
| JavaScript Expression | WaitOptions.ForJavaScript(expression) |
Pages that expose a ready flag on the window |
| Manual Signal | WaitOptions.ForSignal(timeout) |
Full control — you call window.cobaltNotifyRender() |
Call WaitFor(strategy) in the fluent chain before the terminal render call.
Network Idle (Default)
Waits until there have been no outstanding network requests for at least 500 ms. This is the default and works well for the majority of pages.
// Explicit, but identical to the default
var pdf = await renderer
.WaitFor(WaitOptions.DefaultNetworkIdle)
.RenderUrlAsPdfAsync("https://example.com");
Fixed Delay
Waits for an exact duration after the page has loaded. Useful when a page has a known CSS animation that must complete before the PDF is captured.
var pdf = await renderer
.WaitFor(WaitOptions.ForDelay(TimeSpan.FromSeconds(2)))
.RenderUrlAsPdfAsync("https://example.com/animated-chart");
CSS Selector
Waits until a specific DOM element exists and is visible. The render fires as soon as the element appears, without waiting for a fixed time.
// Wait for the data table to render
var pdf = await renderer
.WaitFor(WaitOptions.ForSelector("#data-table-loaded"))
.RenderUrlAsPdfAsync("https://example.com/report");
// With a custom timeout (defaults to 30 s)
var pdf2 = await renderer
.WaitFor(WaitOptions.ForSelector(".chart-ready", TimeSpan.FromSeconds(15)))
.RenderUrlAsPdfAsync("https://example.com/dashboard");
If the selector does not appear before the timeout, a TimeoutException is thrown with a descriptive message.
JavaScript Expression
Waits until a JavaScript expression evaluates to a truthy value. The expression is polled repeatedly until it returns true.
// Wait until the app signals it is done loading
var pdf = await renderer
.WaitFor(WaitOptions.ForJavaScript("window.myApp && window.myApp.isReady === true"))
.RenderUrlAsPdfAsync("https://example.com/app");
// With a custom timeout
var pdf2 = await renderer
.WaitFor(WaitOptions.ForJavaScript("document.querySelectorAll('.chart').length >= 3",
TimeSpan.FromSeconds(20)))
.RenderUrlAsPdfAsync("https://example.com/dashboard");
If the expression does not become truthy before the timeout, a TimeoutException is thrown.
Manual Signal (Most Precise)
The most powerful strategy. CobaltPdf exposes a function called window.cobaltNotifyRender() and waits for your custom JavaScript to call it. This gives you complete control over exactly when the PDF is captured.
Use this with WithCustomJS — your script performs whatever async work is needed, then calls window.cobaltNotifyRender():
string js = """
(async () => {
// Wait for the chart library to finish rendering
await new Promise(resolve => {
if (window.Chart && window.Chart.instances.length > 0) {
resolve();
} else {
window.addEventListener('chartsReady', resolve, { once: true });
}
});
// Dismiss the cookie banner if present
const btn = document.querySelector('[data-testid="accept-cookies"]');
if (btn) btn.click();
// Signal CobaltPdf to capture
window.cobaltNotifyRender();
})();
""";
var pdf = await renderer
.WaitFor(WaitOptions.ForSignal(TimeSpan.FromSeconds(15)))
.WithCustomJS(js)
.RenderUrlAsPdfAsync("https://example.com/complex-dashboard");
Important
window.cobaltNotifyRender() must be called, otherwise the render will wait until the timeout and throw a TimeoutException. If WithCustomJS is configured with ForSignal but your script might not always call cobaltNotifyRender(), add a fallback.
Tip
CobaltPdf logs a warning at render time if ForSignal is set but no custom JS is provided.
Combining with Custom JavaScript
WithCustomJS executes your script after the page has loaded and before the wait strategy fires. For most strategies (Network Idle, Selector, JS) this means the script runs first, and then CobaltPdf waits for the selected condition. For ForSignal, the script is responsible for both the work and the signal.
// Run JS to dismiss cookie banners, then wait for network idle
var pdf = await renderer
.WithCustomJS("document.querySelector('.cookie-banner')?.remove();")
.RenderUrlAsPdfAsync("https://example.com");
Tip
For a full guide including DOM manipulation examples, async scripts, and CSP notes, see the Custom JavaScript article.