This guide shows how to add a PDF download button to a reactive app with WebSave as PDF. It covers React, Preact, Vue, Angular, Svelte, Astro, and SolidJS, with a shared configuration and a small component for each framework.
The examples use content mode: WebSave captures the page's rendered HTML and current form values in the browser, then sends the content to PDFCrowd to create the PDF. This suits pages that change as the user interacts with the app.
Load and configure WebSave
Add the configuration and WebSave script once in your application's main HTML file or shared layout:
<script> window.webSaveConfig = { conversionMode: 'content', fileName: 'report.pdf', apiSettings: { page_size: 'A4' } }; </script> <script src="https://edge.pdfcrowd.com/websave/1.3.0/websave.min.js" async></script>
The data-config="webSaveConfig" attribute tells WebSave to use the settings above
for that button. Change fileName for the download name and apiSettings for PDF
options such as page size or margins. See the
WebSave reference for available settings.
Add the button in your framework
WebSave automatically sets up buttons already on the page when its script loads.
If your framework adds a button later, call window.WebSave.initButton(button)
after adding it to the page.
The examples use ?. to call initButton() only if WebSave has loaded. Otherwise,
WebSave will find and set up the button when its script finishes loading.
Each example is a button component you can place beside your page content. The buttons
use your application's styles. The pdfcrowd-remove class keeps the button out of
the PDF while keeping it visible on the page.
The buttons below use the working demo WebSave key, so you can try them without
creating an account. For production, replace it with your
WebSave key.
React
Use a ref to get the button element, then initialize it in
useEffect after React adds it to
the page:
import { useEffect, useRef } from 'react'; export default function PdfDownload() { const button = useRef(null); useEffect(() => { window.WebSave?.initButton(button.current); }, []); return ( <button ref={button} type="button" className="pdfcrowd-websave pdfcrowd-remove" data-key="demo" data-config="webSaveConfig"> Download PDF </button> ); }
In Next.js App Router, make this a
Client Component
by adding 'use client'; before the imports. Load the shared configuration and
WebSave script from your layout. The code that sets window.webSaveConfig must run
in the browser, since window is not available on the server.
Preact
Use the React component above, replacing its import with Preact's hooks:
import { useEffect, useRef } from 'preact/hooks';
Vue
Use a template ref to get the button element, then initialize it in
onMounted after Vue
adds it to the page:
<script setup> import { onMounted, ref } from 'vue'; const button = ref(null); onMounted(() => { window.WebSave?.initButton(button.value); }); </script> <template> <button ref="button" type="button" class="pdfcrowd-websave pdfcrowd-remove" data-key="demo" data-config="webSaveConfig"> Download PDF </button> </template>
The same component works in Nuxt. Run the shared configuration code in the browser,
where window is available.
Angular
Use afterNextRender to initialize
the button after Angular renders it in the browser:
import { afterNextRender, Component, ElementRef, viewChild } from '@angular/core'; @Component({ selector: 'app-pdf-download', standalone: true, template: ` <button #pdfButton type="button" class="pdfcrowd-websave pdfcrowd-remove" data-key="demo" data-config="webSaveConfig"> Download PDF </button> ` }) export class PdfDownloadComponent { private button = viewChild.required<ElementRef<HTMLButtonElement>>('pdfButton'); constructor() { afterNextRender(() => { (window as any).WebSave?.initButton(this.button().nativeElement); }); } }
The main HTML file is usually src/index.html. Import PdfDownloadComponent into the
component that uses it, then add <app-pdf-download /> to that component's template.
Svelte
Use bind:this to get the button element, then initialize it in
onMount after Svelte adds
it to the page:
<script> import { onMount } from 'svelte'; let button; onMount(() => { window.WebSave?.initButton(button); }); </script> <button bind:this={button} type="button" class="pdfcrowd-websave pdfcrowd-remove" data-key="demo" data-config="webSaveConfig"> Download PDF </button>
For SvelteKit, put the shared configuration and external script in src/app.html.
The component's onMount callback runs only in the browser.
Astro
Add the shared configuration and WebSave script to your layout, using
is:inline
on both setup <script> tags so Astro includes them unchanged in the page's HTML.
Then add this button component:
<button type="button" class="pdfcrowd-websave pdfcrowd-remove" data-key="demo" data-config="webSaveConfig"> Download PDF </button> <script> function initializePdfButtons() { document.querySelectorAll('.pdfcrowd-websave').forEach((button) => { (window as any).WebSave?.initButton(button); }); } initializePdfButtons(); document.addEventListener('astro:page-load', initializePdfButtons); </script>
The astro:page-load event
also initializes new buttons after navigation with Astro's ClientRouter.
If the button is part of a React, Vue, or other framework component that becomes
interactive in the browser, use that framework's example instead.
SolidJS
Use a ref to get the button element, then initialize it in
onMount after SolidJS adds
it to the page:
import { onMount } from 'solid-js'; export default function PdfDownload() { let button; onMount(() => { window.WebSave?.initButton(button); }); return ( <button ref={button} type="button" class="pdfcrowd-websave pdfcrowd-remove" data-key="demo" data-config="webSaveConfig"> Download PDF </button> ); }
When page content or settings change
Content mode captures the page when the visitor clicks Download PDF. You do not need to initialize the button again when page content or form values change. If navigation or a page update creates a new button, initialize that new button. The examples above do this each time the framework adds the button to the page.
Calling initButton() again on the same element is safe: WebSave does not attach a
second click handler. This also works when React runs an effect again in development.
WebSave reads window.webSaveConfig for each click. You can update its filename or
PDF settings when your application data changes, without loading the script again:
window.webSaveConfig.fileName = 'quarterly-report.pdf'; window.webSaveConfig.apiSettings.page_size = 'Letter';
If a newly displayed button does nothing, check that the WebSave script loaded and that initialization ran for that button element. For conversion or layout issues, see WebSave troubleshooting.