To generate a PDF from an Angular app on the server, serve the document route through Angular server-side rendering (SSR), then open that route in a controlled headless browser and call page.pdf(). Angular SSR supplies rendered HTML; the browser is what prints that page to PDF. The key to a complete, consistent file is waiting for the report’s data, fonts, and layout to be ready, then applying deliberate print styles and PDF options.
How the server-side PDF flow works
A typical request passes through two rendering stages: Angular produces the document’s HTML, and a browser renderer turns that page into PDF bytes. The server should authenticate the caller, prepare the report data, load a controlled Angular route in Chromium, wait for a route-specific ready signal and other required assets, and then return the PDF with Content-Type: application/pdf.
- Authenticate and authorize the PDF request.
- Load report data using server-controlled state.
- Open the document route in an isolated browser page or context.
- Wait for the Angular route, required data, images, and fonts to be ready.
- Set page size, margins, and print options, then call
page.pdf(). - Stream the resulting bytes to the caller as a PDF.
This pattern keeps Angular templates and document styling in the application while moving the final print operation to a server-managed browser. It also avoids relying on the end user’s browser, installed fonts, or manual “Print” action.
Set up Angular SSR for the document route
Add server rendering
For a new Angular application, start with ng new --ssr. To add server rendering to an existing application, run ng add @angular/ssr. Angular supports hybrid rendering: routes can use client rendering, server rendering, or prerendering, so select a mode based on when the document’s content is known.
#1 Best Overall
Choose server rendering or prerendering
Use RenderMode.Server for reports that need request-specific data, such as a particular customer’s invoice or a report generated from a current request. Use RenderMode.Prerender when the document content is known at build time and is the same for every visitor. Register the server routes with provideServerRendering(withRoutes(serverRoutes)).
For a request-specific PDF route, the configuration follows this pattern:
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
{
path: 'reports/:reportId/pdf',
renderMode: RenderMode.Server,
},
];
Exact surrounding files and bootstrap setup depend on the Angular application, but the important choice is that the PDF route is rendered on the server when its content depends on the request. SSR does not create a PDF; it supplies HTML for the browser renderer.
Keep browser-only code out of server execution
Angular’s SSR guidance warns that server execution cannot use browser globals such as window, document, navigator, and location, or certain HTMLElement properties. Put browser-only setup in afterNextRender or afterEveryRender. When document access must work across platforms, inject Angular’s DOCUMENT rather than assuming a global browser document exists.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Generate the PDF with Playwright
The example below shows the core browser-rendering pattern. It assumes internalBaseUrl points to the Angular application, the server has already authorized the request, and the route exposes data-pdf-ready="true" only after report content has been bound and laid out. Install Playwright and its Chromium browser as part of the service’s deployment setup.
import { chromium } from 'playwright';
export async function renderReportPdf(
internalBaseUrl: string,
reportId: string,
): Promise<Buffer> {
const browser = await chromium.launch();
try {
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(
`${internalBaseUrl}/reports/${encodeURIComponent(reportId)}/pdf`,
{ waitUntil: 'domcontentloaded' },
);
await page.locator('[data-pdf-ready="true"]').waitFor();
await page.evaluate(() => document.fonts.ready);
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
margin: {
top: '16mm',
right: '14mm',
bottom: '16mm',
left: '14mm',
},
});
return Buffer.from(pdf);
} finally {
await browser.close();
}
}
The browser call returns PDF bytes; the server framework should send those bytes with the PDF content type. The example intentionally uses domcontentloaded followed by an application-specific marker rather than treating network quiet as proof that a report is complete. A route may still be loading data or arranging its final layout after its initial HTML arrives.
Rank #2
In a long-running service, creating a new browser process for every request may be an operational choice rather than a requirement. A service can reuse a controlled browser and create an isolated context or page for each job. Whichever lifecycle you choose, ensure cleanup occurs on success and failure, and cap concurrent work to protect the host.
Puppeteer alternative
Puppeteer uses the same basic approach: navigate to the Angular route, wait for application readiness and fonts, then call page.pdf(). For example, the PDF operation is:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
});
Puppeteer documents this method as generating a PDF using the print CSS media type. Playwright’s equivalent also prints with print media by default. Both are viable Chromium-centered options for this task; neither has a published head-to-head throughput, latency, or memory benchmark for Angular PDF workloads in the sources reviewed here.
Make readiness explicit
A successful navigation does not necessarily mean the report is ready to print. The route should signal completion after final data binding and any layout-affecting work. For example, render data-pdf-ready="true" on a stable wrapper once the report is ready, and keep it absent while required data is pending. The browser can then wait for that specific condition instead of guessing with an arbitrary sleep.
Wait separately for assets that affect appearance or completeness. The Playwright example waits for document.fonts.ready; reports that depend on images should also make those images part of the route’s readiness condition. If a report has several asynchronous sections, the ready signal should represent all sections whose absence would make the PDF incomplete.
Playwright supports navigation wait states including commit, domcontentloaded, load, and networkidle. Its documentation cautions that networkidle is discouraged for testing. In a data-driven report, prefer a route-specific signal because pages may maintain connections or issue unrelated requests even after the report itself is ready.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteRank #3
Control page size, colors, and pagination with print CSS
Design the PDF route as a document, not merely as a screenshot of the interactive application. Use print styles to remove controls and navigation, set appropriate page breaks, and preserve meaningful table structure. Define paper size and margins in CSS when CSS should own pagination; set preferCSSPageSize: true so the browser prefers that CSS page size. Set printBackground: true when background colors or graphics are part of the document design.
@page {
size: A4;
margin: 16mm 14mm;
}
@media print {
.app-navigation,
.report-actions {
display: none !important;
}
thead {
display: table-header-group;
}
tr,
.keep-together {
break-inside: avoid;
}
.new-page {
break-before: page;
}
}
These rules are practical print-layout guidance, not a guarantee that every complex layout will paginate identically in every browser revision. Test long tables, page-break boundaries, and repeated headers using representative report data. If you want the PDF to resemble the screen design rather than print styling, emulate screen media before printing. For exact color reproduction, print CSS can use -webkit-print-color-adjust.
Or skip the browser setup
If the job is to capture a URL rather than produce an Angular-specific, data-authorized report, ScreenshotNeo is a screenshot API and MCP server. One request can return an image or PDF; the API can also capture the rendered appearance of an Angular route. It does not replace the SSR and authorization design above when a report must use request-specific server-controlled data.
For example, this cURL call captures a route as a WebP image:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-app.example/reports/example/pdf -o shot.webp
See the ScreenshotNeo API documentation for request options, including PDF output. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The Free plan includes 1,000 shots a month with no card, and paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.
Secure and operate the PDF service
Control what the browser can load
Do not let an untrusted caller supply an arbitrary URL for the browser to visit. Authenticate the report request, keep the target origin on an allow-list, and pass report data through server-controlled state. Otherwise, the PDF renderer can become a way to make the server request destinations that the caller should not control.
Bound work and protect report data
Apply navigation, PDF-generation, and overall request timeouts. Limit concurrent browser pages, recycle unhealthy browser processes, and log failures without logging sensitive report contents. Pin Angular, Puppeteer or Playwright, and the browser revision in deployment images; a browser update can change rendering behavior, so keep representative PDF fixtures and check them during releases.
Rank #4
Angular’s server-side HttpClient fetch backend has a default response-body limit of 1 MB. Increase maxResponseBodySize only if a document genuinely requires a larger server-fetched payload, and keep the configured limit as small as practical: buffering more data increases memory use and denial-of-service risk.
Puppeteer or Playwright?
Choose based on the browser automation surface your service needs, not an assumed speed advantage. Both expose a PDF method for Chromium-based rendering, and no authoritative head-to-head benchmark establishes a universal performance winner for Angular PDFs.
| Decision | Puppeteer | Playwright |
|---|---|---|
| PDF output | page.pdf() with print media and Chromium-oriented options. |
page.pdf() with print media and options including format, margins, background printing, CSS page size preference, page ranges, and tagged output. |
| Browser automation scope | A focused choice when the service is intentionally centered on Chrome or Chromium. | A broader browser automation API; PDF generation is documented on its Page API. |
| Readiness controls | Navigation plus application-specific selectors or evaluation. | Navigation wait states plus locators and assertions; its documentation discourages networkidle for testing. |
| Good fit | Choose when a Chromium-focused PDF service is the goal. | Choose when the team already standardizes on Playwright or needs its broader automation surface. |
Troubleshooting common PDF problems
The PDF is blank or missing report data
The browser may have printed before Angular completed its data binding, or the route may have failed to load. Wait for the report-specific ready marker rather than relying only on navigation, and make sure the marker is not set until required data is present. Check the server-side route and browser logs for failed navigation or application errors.
Fonts or images are missing
Ensure required resources are reachable from the renderer and included in the route’s readiness logic. Wait for document.fonts.ready before printing. For images that affect the document, do not mark the route ready until they have loaded or handle their failure explicitly.
Colors, margins, or page size differ from expectations
Remember that PDF generation uses print media by default. Verify the active @media print rules and @page declarations, enable printBackground for required backgrounds, and use preferCSSPageSize when CSS page size should take precedence. If the design depends on screen styles, emulate screen media before generating the PDF.
Tables or blocks split awkwardly across pages
Add print-specific break rules to the relevant rows or containers, and test with content long enough to cross page boundaries. A rule such as break-inside: avoid can keep a short row together, but a block taller than the printable page cannot fit on one page; design those cases to split sensibly.
Requests hang or exhaust server resources
Set navigation, PDF, and total request timeouts; cap concurrent pages; and ensure browser and page resources are cleaned up when an operation throws. Investigate whether the route is waiting on an unnecessary request or whether the readiness condition can never become true.
SSR throws browser-global errors
Move code that uses window, document, navigator, location, or browser-only element properties into browser-only render hooks such as afterNextRender or afterEveryRender. Use Angular’s injected DOCUMENT for platform-agnostic document access.
Performance and cost considerations
There is no published benchmark here that makes Puppeteer or Playwright categorically faster for Angular PDF generation. Measure your own templates, payload sizes, browser revision, and expected concurrency before selecting capacity. Track render duration and failure causes without recording report content, and test whether browser reuse, page concurrency, or report complexity is the source of slow jobs.
Free tools Windows power users keep installed
One-click scans. No signup required.
Large server-fetched report payloads deserve particular care because Angular’s SSR fetch backend defaults to a 1 MB response-body limit. Raising that ceiling may resolve a legitimate payload constraint, but it also increases buffering and memory exposure. Keep the payload and configured limit no larger than the report requires.
Frequently Asked Questions
Does Angular SSR itself create the PDF file?
No. SSR supplies rendered HTML for the route; a server-side browser such as Chromium creates the PDF from that page.
Can a static Angular report use prerendering?
Yes. Prerendering fits content known at build time and identical for visitors; request-specific reports should use a server-rendered route.
Which browser renderer is faster for Angular PDFs?
The available documentation does not establish a universal speed winner. Benchmark your own report templates and concurrency.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Quick Recap
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.




