If you've ever tried to turn a modern web page into a PDF programmatically, you've probably hit the wall: the file comes out blank, half-empty, or frozen on a loading spinner. The page looks perfect in the browser, so what gives?

The answer is timing. Most PDF approaches grab the HTML before the JavaScript has rendered the content. On a server-rendered page that's fine — the markup is already there. On a React/Vue/Angular app, the server sends an near-empty shell and the browser builds the DOM afterward. Capture too early and you save the shell.

Here's how to do it properly.

Why the naive approaches fail

wkhtmltopdf is the classic Google answer. It's fast and it's been around forever, but it uses an ancient WebKit build with effectively no modern JavaScript support. For a static page it's fine. For anything client-rendered, it captures the empty state.