Skip to main content

Tester UI

PDF Server ships a tester UI — a small React app served from a static directory in the runtime image (at /static/tester/). It lets you pick a template, provide data, and preview the rendered PDF interactively. It is the fastest way to iterate on a template.

Open it

Browse to the server root:

http://localhost:9999/

(The root redirects to /static/.) The server logs the exact URL on startup.

What it offers

  • Template picker — every loaded template and composition.
  • Data input — paste JSON, or load one of the template's committed examples/*.json datasets.
  • Live preview — renders the PDF and displays it inline.
  • Schema & metadata — shows the template's JSON Schema and server metadata.
  • Problems — what the browser noticed while rendering, when the server runs with --debug-enabled.
  • Layout — the printed pages with every measured box drawn over them, and the problems list next to them.

HTML preview

The HTML preview tab — shown only when the server runs with --debug-enabled — shows the body, header and footer markup the template engine produced for the data on the left, before any of it reaches the browser. It is the fastest way to tell a template-engine problem from a print-layout one.

Each part is shown one way at a time, switched by the Preview / Source toggle above them: Preview renders the markup in an isolated frame, Source shows it as text to read and copy. Both get the full width, because a rendering squeezed into half of it lays out nothing like the printed page.

The rendered body loads the template's own assets/ — stylesheets, images, fonts — over the gated GET /_debug/templates/<name>/assets/<path> route, so it shows the styled page rather than bare markup. The header and footer previews deliberately do not: the browser gives them no access to assets/ when it prints (see Header & footer), and a preview that loaded them would show a page the printer will never produce. Scripts do not run either — the frame is sandboxed with no privilege over the tester's origin, because previewed markup is exactly where untrusted template output shows up.

A composition lists its child tasks instead; pick one to preview it, and use Back to task list to return.

Problems

The Problems tab — like the HTML preview, only when the server runs with --debug-enabled — lists what the browser noticed while it rendered the data on the left: assets that never arrived, scripts that threw, a font the image does not have. Each row carries the severity as a coloured ! / ~ / . chip, the kind, and a one-line detail; the list is ordered worst-first, then by page, then in document order. Reading a render's problems explains every kind.

The tab asks for a PDF-only bundle, because problems.jsonl is written whatever the artifacts selector says — so opening it costs one render, not the page images and HTML it never shows. Under --templates-reload-mode=watch it re-renders when the template changes on disk, like the preview beside it.

Layout

The Layout tab — --debug-enabled again — draws the layout snapshot over the pages it was measured from: every page is rasterized, and every measured box outlined on top of it. Hovering a box names its selector, its size, and how far it overflows; clicking one holds that. The problems list is the same list the Problems tab shows, and selecting a problem scrolls to the element it is about and flashes it, so a finding that reads as an abstract distance becomes a place on a page. It sits to the right of the pages when the tab is wide enough for both and moves under them when it is not — which depends on the split between the input and preview panes, not on the size of the window.

Unlike the Problems tab this one asks for the PDF and the snapshot together — the boxes have to be drawn over the render they were measured from, not over a second one — so it is the most expensive tab to open, and it renders when you open it rather than while it is closed. It re-renders on a template change under --templates-reload-mode=watch, because an overlay left on the pre-edit render does not look broken, it looks wrong.

Pages open scaled to the width available to them, never magnified past 100%, and rescale when that width changes. The - and + buttons zoom between 25% and 300% instead, and the third button returns to the fitted scale. A warning beside them says when the snapshot's own pagination is approximate — the page count it measured and the PDF's disagree, or the capture already knew its page model was inferred — so an estimated box position is never read as a measurement.

Overlay an example render

An example page carries the same tabs the render page does, so the overlay for a committed example is that page's Layout tab:

http://localhost:9999/static/tester/#/templates/hello/examples/basic?tab=layout

layout=shown was how an example page reached the overlay while it had no tabs. It no longer exists, nor does the Overlay layout boxes on example previews setting beside it; tab=layout is what both of them did.

With hot reload

Run pdf-server --templates-reload-mode=always server (the dev default) and each render re-reads the template from disk, so you can edit the template file and re-render in the tester without restarting. See the author workflow.

Live watch

Run pdf-server --templates-reload-mode=watch server and the tester UI updates by itself: the server watches the templates directory and pushes changes over Server-Sent Events, so creating a template makes it appear in the menu and editing the current one re-renders its preview — no page refresh. See template store & hot reload for how it works.

Collapsed sidebar

Open the tester with sidebar=collapsed to start with the template menu hidden — handy for demos and embedding. The parameter works both before the anchor and inside it:

http://localhost:9999/static/tester/?sidebar=collapsed
http://localhost:9999/static/tester/#/templates/hello/render?sidebar=collapsed

The menu toggle in the top bar still shows or hides the sidebar as usual.

Hide the PDF viewer toolbar

The preview iframes (on both the render and example pages) show the browser's built-in PDF viewer, toolbar and all. Toggle Hide PDF viewer toolbar in the settings popover — or open the tester with pdfToolbar=hidden — to strip that chrome for a cleaner preview:

http://localhost:9999/static/tester/?pdfToolbar=hidden
http://localhost:9999/static/tester/#/templates/hello/render?pdfToolbar=hidden

The setting rides in the anchor query string like the sidebar one, and works before the anchor too, so it is shareable. It relies on the viewer's own parameters: Chromium hides its toolbar, while Firefox's pdf.js keeps its sidebar collapsed but still shows its top toolbar, which pdf.js offers no way to hide.

Open a preview tab from the URL

The preview area has tabs — PDF preview, Render Plan Preview, HTML preview, Problems, Layout. tab= opens one directly, so "look at this render's problems" is a link rather than a click someone has to be talked through:

http://localhost:9999/static/tester/#/templates/hello/render?tab=problems
http://localhost:9999/static/tester/#/templates/hello/examples/basic?tab=problems
http://localhost:9999/static/tester/?tab=html

Both pages have them: the render page previews the data in its editor, an example page previews the committed examples/*.json data set it is named for.

ValueTab
pdfPDF preview (the default, and what a clean URL opens)
renderPlanRender Plan Preview — only meaningful for a composition
htmlHTML preview
problemsProblems
layoutLayout

Switching tabs by hand rewrites the parameter, so the URL in the address bar is always shareable. A value that names no tab is ignored and the PDF preview opens, which keeps a stale link from showing an empty page.

API spec viewers

The static assets also include OpenAPI viewers (Swagger UI, Redoc, Scalar) served alongside the tester, pointed at the live /spec/swagger.json. See the API reference.