ScreenshotNeo

BlogHTML to image & PDF

How to Make wkhtmltopdf Wait for JavaScript to Finish Loading

Use `--window-status` when your page can signal readiness, or `--javascript-delay` for a fixed post-load pause. Includes runnable examples, troubleshooting, and reliability tips.

By the ScreenshotNeo team4 October 20268 min read

Use --window-status when the page can tell wkhtmltopdf that the content is ready. Set window.status to a distinctive value only after the asynchronous work needed in the PDF has completed, then pass that same value to wkhtmltopdf. For a page without a readiness signal, use --javascript-delay to wait a fixed number of milliseconds after page load. A delay is an estimate; it does not prove that application work has finished.

JavaScript is enabled by default in the documented command-line tool. Keep it enabled when the page relies on scripts to render content or set the readiness marker. The project documentation lists 200 ms as the default JavaScript delay. See the wkhtmltopdf usage reference.

1. Use an explicit ready marker when you control the page

The most direct approach is to set a distinctive window.status value after fetching data and applying the DOM updates that the PDF requires. The value must match the command line exactly.

<script>
async function renderReport() {
  try {
    const response = await fetch('/api/report');
    if (!response.ok) throw new Error(`Report request failed: ${response.status}`);

    const report = await response.json();
    renderReportIntoPage(report); // Update the DOM and any required charts.

    // Set this only after all required content has been rendered.
    window.status = 'pdf-ready';
  } catch (error) {
    console.error(error);
    window.status = 'pdf-error';
  }
}

renderReport();
</script>

Save that page as report.html and run:

wkhtmltopdf --enable-javascript --window-status pdf-ready report.html report.pdf

--enable-javascript is shown for clarity; JavaScript is already enabled by default in the documented CLI. Do not add --disable-javascript when relying on page code to set the marker.

Put the marker after every required asynchronous task

If several independent requests feed the report, wait for all of them before setting the marker. For example:

async function renderReport() {
  try {
    const [summaryResponse, chartResponse] = await Promise.all([
      fetch('/api/summary'),
      fetch('/api/chart-data')
    ]);

    if (!summaryResponse.ok || !chartResponse.ok) {
      throw new Error('A report data request failed');
    }

    const [summary, chartData] = await Promise.all([
      summaryResponse.json(),
      chartResponse.json()
    ]);

    renderSummary(summary);
    await renderChart(chartData); // If chart rendering is asynchronous, await it.
    window.status = 'pdf-ready';
  } catch (error) {
    console.error(error);
    window.status = 'pdf-error';
  }
}

renderReport();

Do not set the marker on the initial page load event if the application still has work in progress. Likewise, a fixed timer inside the page is not a reliable substitute for awaiting the work: it may fire too early on a slow run.

2. Use a fixed delay when no readiness signal is available

--javascript-delay <msec> waits a configured amount of time after page load before printing. Use an integer number of milliseconds and choose a duration based on the actual page and the variation you observe. The documented default is 200 ms; dynamic pages often need a larger value.

# Wait two seconds after page load
wkhtmltopdf --javascript-delay 2000 https://example.com/report report.pdf

For a local HTML file:

wkhtmltopdf --javascript-delay 2000 report.html report.pdf

A fixed delay is useful when you cannot change the page to expose a ready marker, but it has two predictable failure modes: too short produces an incomplete PDF, while too long wastes time on every conversion. It also cannot account reliably for variable network or rendering time.

3. Choose one readiness strategy and validate it

Approach Printing is triggered by Use it when Limit
--window-status VALUE window.status equals the supplied string You control the page and can signal completion after required rendering. The documentation does not state a universal timeout if the value never appears.
--javascript-delay MSEC The configured post-load time elapses A bounded approximate wait is acceptable and you cannot add a readiness signal. It is not a signal that arbitrary asynchronous application work has finished.

The official option descriptions do not define universal precedence or interaction when both options are used together. Historical issue reports describe differing observed behavior across setups. Prefer one clear strategy and validate it with the exact executable, input structure, and page you deploy. The issue discussing the two options is a historical report, not a cross-version contract.

4. Configure the library interface

If you use libwkhtmltox rather than the CLI, its analogous delay setting is load.jsdelay, measured in milliseconds. The library reference also documents JavaScript enablement through web.enableJavascript and notes that rendering may proceed when JavaScript calls window.print(). Configure the setting on the page object being converted, and enable JavaScript if the page needs it. Consult the libwkhtmltox page settings reference for the binding and setting names available in your integration.

// Conceptual libwkhtmltox settings; use the equivalent calls in your language binding.
load.jsdelay = "2000";
web.enableJavascript = "true";

This is a settings illustration, not a complete program: libwkhtmltox bindings expose these values through their own APIs. The cited settings reference documents a delay, not a separate library setting equivalent to the CLI’s --window-status; check the binding you use before assuming that option is exposed.

5. Options and details that affect waiting

  • --enable-javascript / --disable-javascript: JavaScript is enabled by default in the documented CLI. Disabling it prevents page scripts from populating content or setting window.status.
  • --window-status VALUE: The expected value is an exact string. Use a marker unlikely to be set accidentally, such as pdf-ready.
  • --javascript-delay MSEC: This is a fixed wait after page load. The documented default is 200 ms. Specify an explicit value if the default is too short for your page.
  • --run-script JS: The CLI documentation describes this as running extra JavaScript after the page is done loading. It does not promise to wait for your application’s later asynchronous work, so it is not itself a readiness guarantee. Use it only when the injected script’s timing and effect are appropriate.
  • Multiple page objects or a table of contents: Option placement and object structure can affect observed behavior. Test the actual full command, not only a simplified single-page invocation. A historical report describes a table-of-contents case: wkhtmltopdf issue 2559.

6. Troubleshooting

Symptom Likely cause What to check or change
The PDF has empty chart areas or missing fetched data. The delay elapsed before application rendering completed, or the marker was set too early. Prefer a readiness marker after data fetches and DOM updates. If that is not possible, increase the delay and account for normal variation.
wkhtmltopdf appears to wait indefinitely with --window-status. The page never assigned the exact expected string, JavaScript failed, or the page did not reach the code that assigns it. Inspect the page’s console/log output, confirm JavaScript is enabled, and verify that the marker is set on success. Record the CLI version/build and reproduce with the same page and arguments. The documentation does not promise a general status-wait timeout.
The command finishes quickly despite dynamic content. The page may set the marker too early, the scripts may not run, or the selected strategy may not be supported as expected by that executable. Check marker placement and script errors; confirm the binary version and build; try a fixed delay as a diagnostic, then choose a strategy based on the result.
Combining status and delay gives a surprising wait. The option descriptions do not establish universal combined-option behavior; reports vary by version and setup. Remove one option at a time and validate each strategy independently with the exact deployed command.
The simple command works but the production command does not. The production input may contain multiple page objects, a table of contents, different option placement, or different resource timing. Capture the full command, version/build, input structure, and marker value. Reduce the case only to diagnose it, then retest the original structure.
JavaScript-dependent content is absent even with a long delay. Scripts may be disabled, fail due to unsupported page behavior, or depend on resources the renderer cannot load. Confirm JavaScript is enabled, inspect errors and network/resource availability, and check whether the page’s scripting assumptions work in the wkhtmltopdf build you deploy.

7. Performance and reliability

  • Keep the wait bounded. A fixed delay adds its full duration to conversions even when rendering finishes sooner. An explicit readiness signal can avoid choosing a large blanket delay, but it is only useful if every success path sets the marker correctly.
  • Handle failure paths visibly. Set a separate failure marker or render an error state if application data cannot load. Do not label a failed report ready. Since the documented CLI reference does not specify a universal timeout for a missing status value, supervise conversions with an outer process timeout in production and report the failure to your caller.
  • Test representative slow runs. Validate the output with slow data responses and delayed chart rendering, not only a warm local page. Confirm the PDF contains the content whose readiness matters.
  • Pin and record the executable build. Record wkhtmltopdf --version, whether the build uses patched Qt, the command line, page-object structure, and relevant logs. Historical issue reports are setup-specific and are not guarantees for another build.
  • Balance wait time against throughput. Longer delays keep a renderer occupied longer and reduce the number of conversions a fixed worker pool can process. Use the shortest delay that is safe for the observed page, or use a meaningful readiness signal.

8. Or skip the browser setup

If you need a screenshot or PDF without maintaining your own browser capture flow, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a screenshot or PDF. See the API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up free for 1,000 screenshots a month, no card required.

FAQ

What should the page set in window.status?

Any distinctive string works if the page and command use the exact same value. For example, set window.status = 'pdf-ready' and pass --window-status pdf-ready.

Can I use a delay and a status marker together?

The option references do not establish a universal interaction rule. Choose one strategy first and verify the behavior of your exact executable and input.

Does --run-script wait for my fetch calls?

No readiness guarantee is documented for application-specific asynchronous work. Run injected code only when its timing is suitable; use a page-level signal after the required work for an explicit readiness condition.

Is this guidance for wkhtmltoimage too?

This guide is about wkhtmltopdf. Do not assume the PDF tool’s waiting behavior applies identically to wkhtmltoimage; verify that tool and its build separately.