Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a template ref to capture the component’s rendered DOM with html2canvas, wait for its content to be ready, then turn the returned canvas into a PNG download. This browser-side method works well for cards, charts, badges and previews, but it reconstructs the page from the DOM rather than taking a compositor-level screenshot. CSS support, cross-origin images and very large dimensions therefore need deliberate handling.

What you will build

The example below is a Vue Single-File Component (SFC) using the Composition API and <script setup>, the style shown in Vue’s current Vite quick start. It captures only the element assigned to captureTarget, creates a PNG, and starts a download named vue-component.png.

Install html2canvas

Install the package used by your project’s current html2canvas release instructions. The project repository currently shows:

npm i @html2canvas/html2canvas

Package names and release instructions can change, so verify the package name and version in the project’s release documentation before pinning it in a new application. The library is intended for the browser; its project README does not support Node.js execution as a replacement for a browser.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture a Vue element and download a PNG

<template>
  <section>
    <div ref="captureTarget" class="export-card">
      <h1>{{ title }}</h1>
      <p>{{ description }}</p>
    </div>

    <button type="button" @click="downloadPng">Download PNG</button>
    <p v-if="errorMessage" role="alert">{{ errorMessage }}</p>
  </section>
</template>

<script setup>
import { ref } from 'vue'
import html2canvas from '@html2canvas/html2canvas'

const title = ref('A shareable card')
const description = ref('Rendered from a Vue component')
const captureTarget = ref(null)
const errorMessage = ref('')

async function downloadPng() {
  errorMessage.value = ''
  const element = captureTarget.value
  if (!element) return

  try {
    const canvas = await html2canvas(element, {
      scale: window.devicePixelRatio,
      backgroundColor: null,
    })

    const link = document.createElement('a')
    link.download = 'vue-component.png'
    link.href = canvas.toDataURL('image/png')
    link.click()
  } catch (error) {
    errorMessage.value = 'Could not create the PNG. Check the element and its image resources.'
    console.error(error)
  }
}
</script>

<style scoped>
.export-card {
  width: 640px;
  padding: 24px;
  color: #172033;
  background: white;
  border-radius: 16px;
}
</style>

The sequence is important:

  1. Ref the DOM node. A template ref points to the actual rendered element after mounting; it is not the Vue component instance.
  2. Run from a user action. The button handler captures the current, visible state.
  3. Await html2canvas. It returns a Promise containing a canvas.
  4. Export PNG data. canvas.toDataURL('image/png') produces a PNG data URL.
  5. Trigger the download. An anchor with a download filename is clicked programmatically.

Make the capture reflect final Vue content

Wait for reactive updates

When a click changes the text, chart data or classes immediately before capture, let Vue flush the DOM first:

import { nextTick } from 'vue'

async function downloadPng() {
  errorMessage.value = ''
  await nextTick()
  const element = captureTarget.value
  if (!element) return
  const canvas = await html2canvas(element)
  // export canvas here
}

If the component uses a transition, wait until the transition has reached the visual state you want. Capturing during an animation can produce an intermediate frame; disabling the transition for export is often more predictable.

Wait for images and fonts

An <img> may have a DOM node before its pixels are available. Wait for the images inside the target and, where supported, for document fonts:

async function waitForAssets(element) {
  const images = [...element.querySelectorAll('img')]
  await Promise.all(images.map((img) => {
    if (img.complete) return Promise.resolve()
    return new Promise((resolve) => {
      img.addEventListener('load', resolve, { once: true })
      img.addEventListener('error', resolve, { once: true })
    })
  }))

  if (document.fonts?.ready) await document.fonts.ready
}

Call await waitForAssets(element) immediately before html2canvas. Treat an image error as a signal to inspect the source rather than silently assuming the export is complete.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Control resolution, size and cropping

Scale for sharp output

The configuration’s scale option defaults to the device pixel ratio. Passing window.devicePixelRatio, as in the example, usually gives a sharper result on high-density screens. Higher scale multiplies the pixel count and memory requirement, so a large card can fail on a phone even when the same card works at scale 1.

const canvas = await html2canvas(element, {
  scale: Math.min(window.devicePixelRatio || 1, 2),
  backgroundColor: '#ffffff',
})

Choose a fixed scale when exports must have predictable dimensions across devices. Test the largest target on the least capable device you support.

Set dimensions or crop a region

Use width and height to control the rendered area. The x and y options define crop coordinates. These are useful when the element contains overflow or when you need a precise export rectangle:

const canvas = await html2canvas(element, {
  width: element.scrollWidth,
  height: element.scrollHeight,
  x: 0,
  y: 0,
  scale: 1,
})

Large canvases are subject to browser bitmap limits and available memory. Reduce scale, split the design into pages, or export a smaller region when a canvas is blank or the browser throws a memory-related error.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose transparency deliberately

backgroundColor: null preserves transparency where the browser and source styles permit it. Use an explicit color when the PNG must have an opaque background or when transparent output makes antialiased edges look unexpected.

Keep controls and overlays out of the PNG

Add data-html2canvas-ignore to buttons, menus and other interface elements that should remain on screen but not appear in the image:

<button data-html2canvas-ignore type="button" @click="downloadPng">
  Download PNG
</button>

You can also use the library’s ignore callback for a rule that applies to several elements:

const canvas = await html2canvas(element, {
  ignoreElements: (node) => node.matches?.('[data-export-ignore]'),
})

Handle images and cross-origin resources

Images generally need to be same-origin or served with appropriate CORS headers. The browser’s security policy is decisive: useCORS: true asks the image request to use CORS, but it cannot grant permission when the remote server does not send an allowing header.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const canvas = await html2canvas(element, {
  useCORS: true,
  imageTimeout: 15000,
})

If the remote host cannot provide CORS, configure a suitable proxy using the library’s proxy option, or copy the asset to an origin you control. Do not treat a proxy as a way to bypass access controls; it must be operated and configured to retrieve the resource lawfully. Cross-origin iframes have additional restrictions and are not converted into faithful content by this approach.

When an image fails, the resulting canvas may omit it or become unusable for export. Use the resource error callback to log failures while diagnosing:

const canvas = await html2canvas(element, {
  useCORS: true,
  onclone: (clonedDocument) => {
    clonedDocument.querySelectorAll('[data-export-only]').forEach((node) => {
      node.style.display = 'block'
    })
  },
})

Use onclone to adjust the cloned document for export without changing the live interface. Keep export-only CSS simple and verify the result in each target browser.

Use a Blob for very large PNGs

toDataURL creates a base64 string in memory. For large images, use toBlob and a temporary object URL instead:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function downloadCanvasPng(canvas, filename) {
  canvas.toBlob((blob) => {
    if (!blob) throw new Error('PNG encoding failed')
    const url = URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.download = filename
    link.href = url
    link.click()
    link.remove()
    URL.revokeObjectURL(url)
  }, 'image/png')
}

Validate this path in the browsers you support, especially if you export multi-megapixel images. Revoke the object URL after the download has been initiated so it does not remain allocated.

What html2canvas can and cannot reproduce

html2canvas traverses the DOM and builds its own representation from styles it understands. It is therefore a DOM reconstruction, not a literal screenshot of the browser compositor. Some CSS properties, filters, blend modes, embedded documents or browser-specific effects may differ from what the user sees.

  • Prefer straightforward layout and styling in the export target.
  • Test shadows, gradients, transforms, SVG, web fonts and complex filters in the browsers that matter to you.
  • Capture after layout has settled; a responsive element may have different dimensions on mobile.
  • Do not assume a canvas export proves that a print or server-rendered version will match.

Modern evergreen Firefox, Chrome/Chromium-based browsers and Safari are listed by the project README, but particular browser versions were not tested here. Keep a fallback message when the export is business-critical.

Common failures and fixes

Symptom Likely cause Fix
captureTarget.value is null The function ran before mount, or the ref name does not match. Call it from a mounted, visible component and verify ref="captureTarget".
PNG is missing remote images The image is cross-origin without permitted CORS. Serve it with appropriate CORS headers, use a correctly configured proxy, or host a same-origin copy. useCORS alone cannot override policy.
Fonts or images look incomplete Capture started before resources finished loading. Await image load events and document.fonts.ready; capture after Vue’s nextTick.
Buttons appear in the image They are descendants of the target. Add data-html2canvas-ignore or an ignoreElements rule.
Canvas is blank or export throws on mobile Pixel dimensions exceed browser or device memory limits. Lower scale, reduce width/height, crop, or split the export.
Output differs from the screen Unsupported or differently interpreted CSS; this is reconstructed rendering. Simplify export CSS and test the exact browser/device combinations you support.
Download does not start The browser blocked a detached or non-user-initiated action. Run the handler directly from the button click and create/click the anchor in that handler after the awaited capture.

When a browser export is the wrong tool

Use this method when the user already has the Vue page open and the exported design can tolerate DOM-based rendering differences. Choose a browser-rendering service when you need a server-side workflow, scheduled captures, PDFs, authenticated pages, or a screenshot of the final compositor output. A service also avoids shipping a large capture operation to every user’s device, but introduces network, authentication and deployment considerations.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP or PDF. Before capture it accepts cookie/consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.

For a deployed Vue page, the minimal call is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for authentication and the full option set. You can also call it from Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Or Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper and page options, HTML/CSS to image, custom JavaScript, pre-capture clicks, hidden selectors, waits for selectors/delays/network idle, request and resource blocking, custom headers/cookies/user agents/Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed public image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.

Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000. Every feature is on every plan, and yearly billing gives two months free. Sign up free to try it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

FAQ

Can I capture the Vue component instance itself?

No. Pass the rendered DOM element obtained from a template ref. The capture library reads the element tree and styles available in the document.

Does this produce a PDF?

No. The code above creates PNG output. PDF generation requires a separate browser or server workflow; ScreenshotNeo can return PDFs when that is the required artifact.

Why is a PNG larger than expected?

PNG is lossless, and device-pixel-ratio scaling increases pixel dimensions. Use an intentional scale and consider resizing or a different format when lossless output is not required.

Can I run html2canvas during server-side rendering?

No. It needs browser DOM and canvas APIs. Run it after hydration in the browser, or use a separate browser-rendering service for server-side capture.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can I capture the Vue component instance itself?

No. Pass the rendered DOM element obtained from a template ref. The capture library reads the element tree and styles available in the document.

Does this produce a PDF?

No. The code above creates PNG output. PDF generation requires a separate browser or server workflow; ScreenshotNeo can return PDFs when that is the required artifact.

Why is a PNG larger than expected?

PNG is lossless, and device-pixel-ratio scaling increases pixel dimensions. Use an intentional scale and consider resizing or a different format when lossless output is not required.

Can I run html2canvas during server-side rendering?

No. It needs browser DOM and canvas APIs. Run it after hydration in the browser, or use a separate browser-rendering service for server-side capture.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.