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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Most html-to-image failures in React come from one of four places: the target element is not ready, an image or font cannot be embedded, SVG foreignObject rendering differs in the browser, or a cross-origin resource or oversized output blocks canvas export. Start by exporting a mounted element from a React ref and handling the promise; then isolate the failing resource or style rather than changing React state at random.

How html-to-image turns a React element into an image

html-to-image does not take a screenshot of the visible browser tab. It clones the selected DOM subtree, copies computed styles, embeds fonts and images, serializes the result as XML inside an SVG foreignObject, and may render that SVG on an off-screen canvas for PNG or pixel output. The project README describes the SVG feature as allowing “arbitrary HTML content inside of the <foreignObject> tag.” Each stage can fail independently, so first determine whether the target, resources, SVG rendering, or final canvas is the problem. See the html-to-image project README for its documented API and behavior.

Start with a mounted React ref and visible errors

Attach a ref to the exact element to export. Do not call the library while the ref is null, and do not discard the returned promise: a rejection is often the most useful clue.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { useRef } from 'react';
import { toPng } from 'html-to-image';

export function Card() {
  const cardRef = useRef(null);

  async function downloadCard() {
    const node = cardRef.current;
    if (!node) return;

    try {
      const dataUrl = await toPng(node);
      const link = document.createElement('a');
      link.download = 'card.png';
      link.href = dataUrl;
      link.click();
    } catch (error) {
      console.error('Could not export card:', error);
    }
  }

  return (
    <>
      

Shareable card

Content to export

); }

This follows the project’s documented ref-and-promise pattern. If the target contains content loaded asynchronously, trigger export only after that content has mounted and its images and fonts are ready. Compare the visible element with the output; a missing part of the page may never have been present in the cloned target at capture time.

Fix blank or missing images

The library attempts to embed image sources, including CSS background images, before serialization. A resource can display in the ordinary page yet fail during export because its URL is unreachable from the capture context or browser security rules prevent it from being fetched or reused.

  1. Open the browser developer tools Network panel and inspect the image and background-image requests. Confirm the URLs, response status, redirects, and whether the request completes before export.
  2. Check the console for fetch, security, or serialization errors. Reduce the target to one image; if that still fails, investigate that image’s origin and delivery rather than React rendering.
  3. For an image that cannot be fetched, provide a data-URL fallback with imagePlaceholder. This substitutes a placeholder; it does not repair access to the original resource.
  4. Use cacheBust: true only to test whether a stale cached resource is involved. It appends the current time as a query parameter to resource requests and is not a general cross-origin fix.

Do not treat “enable CORS” as a universal instruction. The server must return suitable access headers, and the resource must be used in a compatible way. A browser security restriction is not necessarily a React state bug.

Fix missing fonts and styles

Font embedding is a separate step from image embedding. The documented font process finds @font-face declarations, fetches font files, base64-encodes them, and adds processed CSS to the cloned node. Verify that the declaration and the font URLs are reachable, and check whether the intended font is actually applied to the live element.

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.
  • Use preferredFontFormat when a font provider lists multiple formats and you want the embedding step to retain a preferred format.
  • For repeated exports that use the same font CSS, prepare it with getFontEmbedCSS() and pass the result as fontEmbedCSS to subsequent captures.
  • Test stylesheets that rely on CSS @import separately. The project issue tracker has an open report titled “Parsing @import in CSS causes style loss”; that report is a reason to isolate imported styles, not proof that every imported stylesheet fails.

Also check computed styles on the target, not only the source stylesheet. CSS selectors that depend on ancestors outside the exported subtree may produce a different appearance when the node is cloned.

Diagnose browser-specific output

The export path relies on SVG foreignObject support and browser image handling. The project README names Chrome, Firefox, and Safari as tested and explicitly says Internet Explorer is unsupported; the version notes in that README are historical, not a current compatibility matrix. The npm package page also notes browser differences.

If output fails only in one browser, reproduce it with a small component in the actual browser, operating system, and version where it occurs. The issue tracker contains a report titled “html-to-image not working on Safari,” but an individual report does not establish that Safari is universally unsupported or that every Safari user is affected. Compare a minimal plain-text element with the full component, then add the styles and resources back a piece at a time.

Check canvas security and export dimensions

If the target contains a canvas, such as a chart or drawing surface, the project warns that a tainted canvas can prevent rendering. Isolate that canvas and inspect any cross-origin inputs it uses. This is a browser origin-security constraint, not necessarily a defect in React state or the chart library.

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

For clipping, unexpectedly small output, or memory pressure, distinguish element dimensions from canvas dimensions:

  • width and height apply dimensions to the node before rendering.
  • canvasWidth and canvasHeight scale the canvas and the elements inside it.
  • pixelRatio controls the image pixel ratio and defaults to the device ratio.
  • skipAutoScale bypasses automatic scaling for very large DOMs, but the README warns that very large output may lose image content.

Increase output dimensions in small steps and compare the result. Do not assume the largest requested capture is supported; data-URI limits vary, and dimensions can increase memory use substantially.

Isolate problematic CSS or markup

When a single visual effect breaks the export, remove features one at a time in a minimal reproduction. The issue tracker includes reports titled “repeating-linear-gradient acts like linear-gradient (CSS),” “Clip-path URLs with absolute same-document references break in exported images,” and “Node contains illegal XML comment node export fail.” Issue titles record reports, not confirmed universal limitations, so reproduce against the dependency version and browser you use.

  • Use the filter option to exclude a node and its descendants, which can help identify whether a particular subtree is involved.
  • Use style to override styles on the cloned root when a root-level style causes an unwanted result.
  • Use includeStyleProperties to limit copied style properties where performance or style-copying scope matters.

These options help narrow or shape an export; none guarantees a fix for every malformed XML node or unsupported CSS feature.

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

Choose the output method and options that match the job

The library provides promise-based methods for different results. Select one deliberately rather than converting formats after capture without a reason.

Method or option What it does Useful when
toPng Returns a PNG data URL. You need a lossless raster image or a browser download.
toJpeg Returns a JPEG data URL; quality from 0 to 1 controls JPEG quality. You need a compressed photograph-like output.
toSvg Returns an SVG data URL. You need the serialized SVG representation.
toBlob Returns image output as a Blob; type selects the image MIME type and PNG is the default. You want a Blob for upload or object-URL handling.
toCanvas Returns a rendered canvas. You need to draw further or inspect canvas pixels.
toPixelData Returns pixel data. You need pixel-level processing rather than a downloadable file.
backgroundColor Sets the output background color. The target is transparent but the desired format or viewer needs a solid backdrop.
imagePlaceholder Provides a data URL for an image whose fetch fails. A missing image should be represented by a fallback graphic.
preferredFontFormat, fontEmbedCSS Control font-format selection or reuse prepared font CSS. Fonts are missing or repeated exports need font embedding.

For the full option definitions and method signatures, consult the project README. Treat output format and dimensions as part of the debugging variables: begin with a small PNG at ordinary dimensions, then vary one option at a time.

Common failures and fixes

Symptom Likely stage First check
Blank output or rejected promise Target, resource loading, or SVG/canvas render Confirm the ref is non-null; log the caught error; try a text-only target.
Images absent but text visible Image fetch or origin security Inspect image requests and test one image by itself; use imagePlaceholder only as fallback.
Fallback font or altered text layout Font embedding or CSS Verify reachable @font-face URLs and test font embedding options.
Works in one browser but not another foreignObject or browser-specific rendering Reproduce in the affected browser and reduce to a minimal element.
Fails when a chart is included Tainted canvas Isolate the chart canvas and investigate its cross-origin inputs.
Large output is clipped or incomplete Scaling, data URI limits, or resource use Reduce dimensions; compare width/height with canvas dimensions; test skipAutoScale cautiously.
One gradient, clip-path, or comment breaks output CSS/XML serialization edge case Remove that feature in a minimal reproduction and check the matching issue report.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a screenshot of a URL rather than exporting a React component’s DOM, a screenshot API avoids configuring this client-side rendering pipeline. ScreenshotNeo is a website screenshot API and MCP server; its clean-shot flow accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.

One GET request returns an image or PDF. The following cURL example saves a WebP image; replace the target URL and key with your own values. See the ScreenshotNeo documentation for request options.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. A URL screenshot API is not a substitute for html-to-image when you specifically need a React component or unsaved client-side state: it captures a URL, not an arbitrary in-memory DOM node.

Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.

Reliability and cost considerations

For in-app exports, the client library avoids a remote screenshot request, but your result still depends on the mounted DOM, fetchable resources, browser support, and output size. For automated URL captures, an API can simplify browser setup, but evaluate what counts as a billable result, what failure information the response exposes, and whether URL capture matches your need. ScreenshotNeo’s stated pricing is monthly: Free, 1,000; Starter, $5 for 3,000; Growth, $15 for 15,000; Pro, $39 for 60,000; Scale, $99 for 250,000; Business, $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. These are ScreenshotNeo plan terms, not a performance comparison with the React library.

Frequently asked questions

Can html-to-image export a React component that is not in the DOM?

No. It accepts a DOM node. Render the component into the document first and pass the mounted node, typically through a ref.

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

Does cacheBust solve CORS problems?

No. It changes resource URLs by appending a current-time query parameter to test cache behavior; it does not grant cross-origin access.

Can I use ScreenshotNeo to capture a chart that exists only in React state?

Not as an arbitrary DOM-node export. ScreenshotNeo captures a URL, so the chart must be rendered on a page that the service can request.

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.