The document is wrong
Content rendered by JavaScript is missing from the PDF
The static parts of the page are there and anything drawn by a script is not: charts, maps, tables filled by a fetch, anything mounted by a client-side framework.
What is actually happening
The snapshot was taken before the script finished. A render has to decide at some point that the page is done, and the default decision is based on the network going quiet, which is not the same as your chart having finished drawing.
Confirming it is this and not something that looks like it
Compare against a fully static version. If you inline the data as JSON in the markup and the content appears, timing is the cause and not the script itself.
The fix
Do the work on the server and send finished markup. This is the answer that keeps working: the render becomes deterministic, faster, and independent of how long a third-party script takes today. Where the thing genuinely has to draw in a browser, chart libraries can render to a static SVG string server-side, which is the same output without the wait.
// Instead of shipping a script that draws the chart in the render:
// <div id="chart"></div><script>drawChart(data)</script>
// Draw it server-side and send the finished SVG.
import { renderChartToSvg } from "./charts.js";
const svg = renderChartToSvg(monthlyTotals, { width: 520, height: 240 });
const html = wrap(`<section class="chart">${svg}</section>`);If that was not it
These produce the same symptom often enough to be worth ruling out before assuming the fix above did not work.
- Fonts loaded by a script, which shift the layout after the snapshot even when the text is present
- An animation with a delay, where the element is present but still at opacity 0
- Content that only mounts on an intersection observer firing, which never fires in a paged render
- A framework hydration step that replaces server markup with a loading state
Frequently asked
Does this happen with every rendering engine?
The behaviour behind it is not specific to one tool. The snapshot was taken before the script finished. Anything rendering HTML to a paged medium has to make the same decision, so the fix travels with you if you change how the render happens.
Will this show up as an error in my logs?
No, and that is what makes it expensive. A document that renders wrong is still a successful render as far as every log line is concerned. It is found by someone opening the file, which is usually the customer.
Related failures
Problems people arrive at from the same starting point, or mistake for this one.
Background colours and images are missing from the PDF
Browsers drop backgrounds when printing, because a page with every background painted would empty a toner cartridge, and most render APIs inherit that default.
SVG images are missing or wrong in the PDF
There are three different problems wearing one symptom.
The generated PDF is blank
Almost always one of three things: the content is rendered by JavaScript that had not run when the snapshot was taken, the body has no height because everything inside it is absolutely positioned or floated, or the markup you sent was empty because the template call returned before the data arrived..
Images do not appear in the PDF
The image src is a path rather than a URL.
The PDF ignores my CSS
The stylesheet is linked rather than embedded, and the link points somewhere the renderer cannot reach: a bundler-generated path, a digested asset filename, or a URL behind your application's authentication.
CSS for paged documents, and what actually works
Which parts of the paged media specification a browser-based render implements.
Everything that goes wrong, by category
The full list, grouped by where in the pipeline it breaks.
Paste your markup and see the rendered document, without signing up.