Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To include content created by JavaScript in a Ruby-generated PDF, use a renderer that runs a real browser engine. Grover, backed by Puppeteer and Chromium, is one option: put the remote script in the HTML with a normal <script src="…"> tag, let the page finish the work that affects the document, and then convert it to PDF. A renderer that only turns HTML into a document without executing its JavaScript will not create content that depends on that script.

Use a browser-backed renderer for JavaScript-dependent PDFs

A script URL is not enough on its own: the renderer must fetch and execute the script in the page. Grover uses Puppeteer and Chromium, making it a suitable Ruby route for pages whose printed output depends on browser JavaScript. Its documentation covers URL or HTML input, script-related options, and waiting mechanisms. Check the README for the exact option syntax supported by the Grover version installed in your application; do not assume an option copied from a different release still applies. Grover README

Here is the essential page-level pattern. Replace the example host and library with a real, publicly reachable script URL and your own application code:

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <script src="https://cdn.example.test/library.js"></script>
</head>
<body>
  <main id="report"></main>
  <script>
    // Populate the report using the loaded library.
    // Signal readiness only after all asynchronous work that changes
    // the printed report has completed.
    window.pdfReady = true;
  </script>
</body>
</html>

The readiness assignment above is illustrative: if the library or report generation is asynchronous, set the flag in the completion path, not immediately after starting a request. The PDF should be captured only after that condition is true.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall

Generate a PDF from Ruby with Grover

A minimal Ruby example can pass rendered HTML to Grover and write the returned PDF bytes to a file. Install and configure Grover and its required browser runtime according to the project README for your environment; this example focuses on the conversion call and deliberately does not assume an unverified wait-option parameter.

require "grover"

html = <<~HTML
  <!doctype html>
  <html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdn.example.test/library.js"></script>
  </head>
  <body>
    <main id="report"></main>
    <script>
      // Replace this with real report code and signal only when ready.
      window.pdfReady = true;
    </script>
  </body>
  </html>
HTML

pdf = Grover.new(html).to_pdf
File.binwrite("report.pdf", pdf)

This compact example demonstrates HTML input and conversion. It does not establish that asynchronous work has finished merely because to_pdf was called. For a production report with delayed data, images, or client-side rendering, configure the documented wait mechanism for your installed Grover version to wait for an application-specific selector or function. Grover documents selector and function waits, but the current README and gem version should be treated as authoritative for the parameter shape. See Grover’s options and timing guidance.

Choose the right way to load the script

Put a script tag in the page when normal page order is right

Use a regular external <script src> when the page’s own scripts should use the library during normal document loading. Put the dependency before code that needs it, and confirm that the script host is reachable from the machine running Chromium. This approach keeps the dependency visible in the HTML and makes its intended load order easy to inspect.

Inject a script when you cannot change the HTML

Puppeteer’s Page API supports adding a script tag from a URL or from supplied content. That is useful when the page is generated elsewhere and you control the browser interaction rather than its markup. The timing matters: add the dependency before code that needs it. Puppeteer documents page.addScriptTag in its Page API.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not confuse post-render code with an early dependency

Grover documents execute_script as supplementary JavaScript run after rendering and before conversion. That can make final edits, but it is too late if earlier page code needs the library to initialize. Grover also documents evaluate_on_new_document for code run before page scripts; use the mechanism whose documented timing matches your dependency order. Confirm exact names and configuration in the installed version’s README.

Wait for the content, not an arbitrary number of seconds

A fixed sleep can be too short on a slow request and needlessly long on a fast one. Prefer a condition tied to the report: for example, a selector that appears only when rendering is complete or an application readiness function that becomes true after data and layout are ready. Grover documents selector/function waits; use the exact supported configuration for your version. Puppeteer’s PDF guide describes browser PDF generation and its print-media behavior. Puppeteer PDF generation guide

  • Set the readiness condition after all asynchronous tasks that affect printed output finish.
  • Make the signal specific to this report rather than waiting for a generic event that may occur before data rendering.
  • Check whether the page relies on fonts, images, or other network resources that may still be loading when the report signal is set.
  • Remember that Puppeteer PDF generation uses print media by default; a page may therefore render differently from its screen layout.

Make remote resources resolvable in the deployment environment

The browser process must be able to resolve and fetch the script, stylesheets, images, and any data requests the page needs. Use complete URLs where practical. Relative paths need a valid page URL or base URL so the renderer knows what they are relative to. A URL that works in your laptop’s browser may fail from a production worker because of DNS, outbound network policy, TLS trust, redirects, authentication, or access restrictions.

If you use PDFKit or Wicked PDF instead, verify JavaScript behavior for the particular wkhtmltopdf build you deploy rather than assuming modern browser behavior. PDFKit’s documentation explains external resource paths and the root_url/protocol configuration. It also warns that a single-threaded development server can deadlock if PDF generation calls back to that same server to retrieve assets; embedding resources or using a multi-worker setup are documented workarounds. PDFKit README Wicked PDF README

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot missing scripts and incomplete PDFs

The PDF omits content that JavaScript should create

  • Likely cause: the renderer does not execute JavaScript, or conversion starts before the code finishes.
  • Fix: use a browser-backed renderer such as Grover with Puppeteer/Chromium. Add an application-specific readiness condition and wait for it using the syntax documented for your installed Grover release.

The remote library is unavailable in the PDF process

  • Likely cause: the script URL is relative or the renderer cannot reach its host, or a redirect, authentication requirement, DNS issue, TLS certificate, or network policy blocks the request.
  • Fix: use an absolute URL or configure a base URL, then test the same URL from the worker/container that runs Chromium. Check the browser’s console and network errors rather than only the HTML source.

The library loads but report content is still blank

  • Likely cause: the code that requests data or renders the report is asynchronous, and the PDF is captured before its completion path.
  • Fix: expose a readiness flag or completed-state element only after the final DOM updates, then wait for that signal. Avoid treating a guessed delay as proof that rendering is complete.

Styles or images disappear, or the layout differs from the browser

  • Likely cause: asset paths cannot be resolved from the renderer, or print media applies different styles.
  • Fix: make resource URLs absolute or supply the correct root URL, verify outbound access, and test the page’s print CSS. Puppeteer PDFs use print media by default, so check print-specific styles before changing screen styles.

PDF generation hangs while fetching local application assets

  • Likely cause: in a single-threaded development server, the PDF request occupies the worker while the renderer tries to request assets from that same server.
  • Fix: follow PDFKit’s documented workarounds: embed resources or use a multi-worker setup. PDFKit’s resource guidance

Handle untrusted pages and scripts carefully

JavaScript loaded into a browser context executes as part of rendering; it is not inert PDF content. Only render content and scripts you intend to execute, especially when URLs or HTML come from users. Grover’s README warns, in the context of a particular option: “Do not enable if rendering content from outside entities (user uploads, external URLs, etc).” Read that warning with its option context in the project documentation rather than treating it as a blanket description of every Grover configuration. Grover README

Or skip the browser setup

If your need is to capture a webpage as an image or PDF rather than build a custom Ruby PDF pipeline, ScreenshotNeo is a website screenshot API and MCP server. It is a separate capture route, not a Ruby library for injecting JavaScript into a document. Its API accepts a URL in one GET request; use the documentation for output and PDF parameters. ScreenshotNeo 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

ScreenshotNeo removes cookie/consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are not billed. Its MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for the free plan.

Practical operating checklist

  • Choose a renderer that runs browser JavaScript if the PDF depends on it.
  • Place the remote dependency in the correct order, either in the page HTML or through an appropriately timed browser script-tag mechanism.
  • Wait on a meaningful application completion signal and verify that the signal covers asynchronous work relevant to the printed result.
  • Check resource resolution, network access, print styling, and the installed renderer version in the same environment that will produce PDFs.
  • Keep untrusted HTML and scripts out of privileged rendering contexts unless you have deliberately addressed the execution risk.

Frequently Asked Questions

Does adding a remote script to HTML make PDFKit execute it?

Not necessarily. PDFKit wraps wkhtmltopdf, and JavaScript behavior depends on the particular wkhtmltopdf build and setup. Verify that combination directly; use a browser-backed renderer when browser JavaScript execution is a requirement.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I use Grover’s post-render script hook to load a library used by the page?

No, not if earlier page code depends on that library. Grover documents execute_script as running after render and before conversion, so use a normal script tag or an early injection mechanism for that dependency.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.