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.

To save an html2canvas rendering in the WordPress Media Library, render the element, export the canvas to a PNG Blob, then send that file to WordPress’s wp/v2/media REST endpoint as multipart form data. The upload requires an authenticated WordPress user with permission to upload media; for a logged-in same-site page, pass a REST nonce in X-WP-Nonce. A returned attachment record—not merely a successfully rendered canvas—is the sign that the upload worked.

What the workflow does—and what it does not do

html2canvas does not take a native screenshot of the browser’s pixels. It reconstructs an image from the page’s DOM and the CSS features it supports. The process therefore has two separate stages: rendering content into an in-memory canvas, then uploading an exported image file to WordPress. If either stage fails, there is no new Media Library attachment.

The browser-to-REST example below is intended for a capture button on a page served by the same WordPress site, while a user is logged in. You need html2canvas loaded on that page, the WordPress REST root, a valid REST nonce, and a user who can upload media. WordPress documents its REST authentication approaches, including cookie authentication with a nonce and Application Passwords for external clients over HTTPS: WordPress REST API authentication.

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

Upload the rendered canvas from a logged-in WordPress page

1. Load html2canvas and obtain the REST settings

Enqueue the library and your own script using WordPress’s supported script-enqueueing approach. Supply your script with the site’s REST root and a nonce created for REST requests; do not hard-code a nonce copied from an old session. In the example, window.wpApiSettings.root and window.wpApiSettings.nonce represent values made available to the page by your WordPress setup. If your setup uses different variable names, substitute its actual REST root and nonce.

The REST root typically ends in /wp-json/. Keeping the root configurable avoids assuming a particular installation path, site URL structure, or REST configuration.

2. Render, export, and POST the Blob

This example captures an element with ID receipt, exports a PNG, and uploads it with the filename capture.png. It reports failure if the canvas cannot be exported or WordPress rejects the request.

async function captureAndUpload(element, restRoot, nonce) {
  const canvas = await html2canvas(element, {
    backgroundColor: "#ffffff",
    useCORS: true,
  });

  const blob = await new Promise((resolve, reject) => {
    canvas.toBlob((result) => {
      if (result) resolve(result);
      else reject(new Error("Canvas could not be exported as an image."));
    }, "image/png");
  });

  const form = new FormData();
  form.append("file", blob, "capture.png");

  const response = await fetch(`${restRoot}wp/v2/media`, {
    method: "POST",
    headers: { "X-WP-Nonce": nonce },
    body: form,
    credentials: "same-origin",
  });

  const result = await response.json();
  if (!response.ok) {
    throw new Error(result.message || "WordPress media upload failed.");
  }
  return result; // Attachment object, including its ID and media URL.
}

const element = document.querySelector("#receipt");
const restRoot = window.wpApiSettings.root;
const nonce = window.wpApiSettings.nonce;

captureAndUpload(element, restRoot, nonce)
  .then((attachment) => {
    console.log("Uploaded attachment:", attachment.id, attachment.source_url);
  })
  .catch((error) => {
    console.error("Capture or upload failed:", error);
  });

The upload request uses FormData with the field name file, which is the uploaded file input expected by the media endpoint. Do not set the multipart Content-Type header yourself: the browser must add the boundary that separates the form fields. The response is an attachment object; its ID identifies the Media Library item, and source_url is the media URL returned for that attachment.

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

3. Treat render and upload errors separately

The example rejects when toBlob() yields no Blob and checks response.ok before reporting a successful upload. For a production interface, show a useful message to the user and keep the two stages distinguishable—for example, “Could not export the image” versus the message returned by WordPress. A canvas existing in memory does not prove that WordPress received or created an attachment.

Choose image settings for the capture

html2canvas options change what is rendered, not WordPress’s authorization or upload behavior. The appropriate settings depend on the element, page layout, and output you need. See the project’s options documentation for the supported configuration.

  • backgroundColor sets the canvas background. The example uses white; the documented transparent-background option is null.
  • scale controls the rendering scale. A larger scale can produce a more detailed image, but also means a larger canvas and potentially more memory use. Check the resulting file and browser behavior rather than assuming every element can be rendered at any size.
  • width and height let you set the canvas dimensions. Use them when the default element dimensions do not match the intended output.
  • useCORS asks html2canvas to load remote images using CORS where possible. It cannot override the browser’s same-origin security rules or a remote host’s missing permission headers.
  • For captures involving content outside the visible viewport, inspect the project’s options for controlling rendering dimensions and window dimensions. Confirm the final image for clipping rather than assuming that a full-page result will match a browser screenshot.

Because html2canvas reconstructs the DOM using supported browser features, an image can differ from what the user sees on screen. Check the project’s explanation of its rendering approach and limitations at Getting Started and About html2canvas.

Authentication and safer ways to handle credentials

Same-site logged-in browser page

For the browser example, the request uses same-origin credentials and an X-WP-Nonce header. This is appropriate when the page is part of the WordPress site and the current logged-in user is authorized to upload. A missing, expired, or mismatched nonce can cause the REST request to be rejected. The current user also needs the capability WordPress requires for media uploads.

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

External application or server

For an external server-side client, use a supported authentication method appropriate to the integration. WordPress documents Application Passwords over HTTPS. Do not put an Application Password or other reusable secret into public browser JavaScript: a visitor can inspect client-side code and requests. Keep credentials on a trusted server and send the image to WordPress from there when browser-side authentication is not suitable.

Pick the upload route that matches where the image exists

Route Best fit Input and authorization Tradeoff
Browser POST to /wp/v2/media A logged-in WordPress page has a capture button and should upload directly. A canvas Blob is sent as a file; the request needs a valid REST nonce and an authorized user. Requires the browser’s rendering, CORS, credentials, and request format to be configured correctly.
PHP media_handle_upload() A conventional WordPress form submits an uploaded file. WordPress receives the file through $_FILES; the function returns an attachment ID or a WP_Error. Fits ordinary form handling, but the form must submit a file; an in-memory canvas alone is not a file in $_FILES.
PHP media_handle_sideload() Plugin code already has a local temporary image file. Provide a $_FILES-style array and a post ID; use 0 for unattached media. Useful for server-held files; code must handle errors and clean up temporary files when needed.

Use media_handle_upload() for a submitted file

When a normal form POST sends an image file to WordPress, media_handle_upload() is the server-side function designed to create the attachment. Check its return value: it can be an attachment ID or a WP_Error. See the WordPress function reference.

Use media_handle_sideload() for a temporary server file

If plugin code has already created or obtained a local temporary file, media_handle_sideload() accepts a file-array input and creates an attachment. Check for WP_Error; if the operation fails, handle the temporary file so it does not linger. An unattached Media Library item can be associated with post ID 0. See the WordPress function reference.

The WordPress attachment REST controller documents the media endpoint’s attachment behavior: REST attachments controller. Choose one path based on where the bytes are and which request context should authorize the upload; these methods are alternatives, not steps to run consecutively.

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

Cross-origin images and canvas export

Remote images are a frequent reason a canvas cannot be exported. Browsers restrict reading canvas pixels when cross-origin content has tainted the canvas. Setting useCORS: true does not bypass that restriction: the remote image host must provide appropriate CORS headers, and the image must be loaded in a way that uses them. The html2canvas FAQ describes the cross-origin limitation.

  • If you control the image host, configure its CORS response appropriately and confirm the browser is loading the image through CORS.
  • If you do not control it, exclude the remote image or use a properly controlled proxy. Do not build an unrestricted proxy that fetches arbitrary URLs; that can create a security risk.
  • If toBlob() produces no data or export throws, test a capture without the cross-origin resource to isolate the cause.

Troubleshoot failed captures and uploads

Symptom Likely cause What to check or change
toBlob() returns no Blob, or export throws A cross-origin image tainted the canvas, or the canvas could not be exported. Check remote image CORS headers, test without that image, and confirm useCORS is appropriate. It is not a security bypass.
REST response is unauthorized or forbidden The user is not logged in, the nonce is absent or stale, the REST root is wrong, or the user cannot upload media. Refresh the page/session, pass the current nonce in X-WP-Nonce, confirm the REST root, and verify the user’s upload capability.
WordPress rejects or cannot process the image The installation or hosting environment may enforce file type or size constraints or encounter a server-side processing problem. Read the response body and check the file type, file size, and server upload limits for that installation. Limits are not universal across WordPress sites.
Uploaded image looks different from the page html2canvas reconstructs from the DOM and supported CSS rather than capturing browser pixels directly. Check supported features, simplify or adjust the content, and tune rendering options. Do not assume every effect will be reproduced exactly.
Image is blank or clipped Capture dimensions may not match the target, or the browser may have canvas size constraints. Inspect the element and canvas dimensions; tune width, height, windowWidth, windowHeight, or scale as applicable, then inspect the generated image.
The interface says uploaded, but no Media Library item appears The code may be treating canvas creation or an HTTP request attempt as success without verifying WordPress’s response. Check response.ok, parse the response body, and only show success after WordPress returns the attachment record and ID.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost considerations

Rendering and uploading consume different resources. html2canvas creates an in-memory canvas in the browser; increasing dimensions or scale can make that work heavier. The upload then sends the encoded image to WordPress and is subject to the site’s own request handling and upload constraints. Keep the capture dimensions appropriate to the use case, avoid unnecessarily large outputs, and provide visible progress or failure feedback for workflows where users wait for a result.

For reliability, do not assume retries are harmless: a retry after an ambiguous network failure may create a second attachment if WordPress completed the first request but the browser never received its response. If duplicate prevention matters, add an application-level strategy on the server rather than blindly repeating POST requests. Inspect the returned attachment ID before deciding whether to retry.

Or skip the browser setup

If the goal is a clean screenshot of a public webpage rather than a specific interactive component rendered from your current page, ScreenshotNeo is a website screenshot API and MCP server for developers. It does not turn an html2canvas canvas into a WordPress attachment; it captures a URL and returns an image or PDF. A one-call request looks like this:

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.
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 API setup. ScreenshotNeo accepts cookie or consent banners before capture and removes 60+ known consent platforms, newsletter popups, and chat widgets; each of those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify page verdict and billing status in headers. Its MCP server includes take_screenshot, get_page_info, and capture_pdf 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.

Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.

FAQ

Does this create a Media Library attachment automatically?

No. The canvas is only browser memory until you export and upload it. WordPress creates the attachment when an authorized media endpoint or server-side media function accepts the file.

Can I upload a JPEG instead of a PNG?

The example requests PNG output. If you change the export format, use a matching filename extension and ensure the resulting file type is accepted by the WordPress installation.

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

Can I attach the uploaded image to a post?

The upload creates an attachment record. If you need a relationship to a particular post, set that association through the appropriate supported WordPress workflow after creation; the example returns the attachment object but does not assign a post.

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.