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

Build the InfoWindow from a real DOM element, open it on the map or marker, wait until the overlay is visible and has layout dimensions, then pass the rendered node to html2canvas. Export the returned canvas with toDataURL('image/png') or toBlob(). This captures the InfoWindow content your page renders; it is not a native browser screenshot, so cross-origin assets and unsupported CSS still apply.

What you are actually capturing

A Google Maps InfoWindow has two relevant parts: the content element that your application supplies and the surrounding bubble, close button, pointer and positioning markup that Maps generates. Your content element is the stable part. Classes such as .gm-style-iw belong to Maps’ rendered DOM and may change, so use an application-controlled wrapper whenever possible.

html2canvas reconstructs pixels from DOM and CSS. It does not ask the browser for a native screenshot. That makes it useful for an element-specific, scriptable export, but fidelity depends on supported CSS, image policy and canvas limits.

Complete JavaScript example

The example below creates a DOM-backed InfoWindow, opens it on a marker, waits for the rendered node, captures it, and downloads a PNG. Replace the map initialization and API loading with the method used by your application.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<button id="capture" type="button">Capture InfoWindow</button>
<div id="map" style="height:420px"></div>
<script type="module">
  import html2canvas from 'html2canvas';

  let map;
  let marker;
  let infoWindow;

  async function initMap() {
    map = new google.maps.Map(document.getElementById('map'), {
      center: { lat: 40.741, lng: -73.989 },
      zoom: 14,
    });

    marker = new google.maps.Marker({
      map,
      position: { lat: 40.741, lng: -73.989 },
      title: 'Example place',
    });

    const content = document.createElement('div');
    content.className = 'capture-infowindow';
    content.innerHTML = `
      <h2>Example place</h2>
      <p>Details shown in the InfoWindow.</p>
      <p data-html2canvas-ignore="true">
        <button type="button" id="close-info">Close</button>
      </p>
    `;

    infoWindow = new google.maps.InfoWindow({
      content,
      maxWidth: 320,
    });

    marker.addListener('click', () => {
      infoWindow.open({ map, anchor: marker, shouldFocus: false });
    });

    document.getElementById('capture').addEventListener('click', captureInfoWindow);
  }

  async function waitForLayout(node, timeout = 5000) {
    const started = performance.now();
    return new Promise((resolve, reject) => {
      function check() {
        const rect = node.getBoundingClientRect();
        if (rect.width > 0 && rect.height > 0) return resolve(node);
        if (performance.now() - started > timeout) {
          return reject(new Error('InfoWindow did not become visible before timeout'));
        }
        requestAnimationFrame(check);
      }
      check();
    });
  }

  async function captureInfoWindow() {
    // Open it explicitly if your UI does not already have it open.
    infoWindow.open({ map, anchor: marker, shouldFocus: false });

    // Prefer a documented Maps visibility/rendering signal in production.
    // This selector is a fallback for the current rendered DOM.
    const rendered = await waitForRenderedInfoWindow();
    const canvas = await html2canvas(rendered, {
      backgroundColor: '#fff',
      scale: window.devicePixelRatio,
      useCORS: true,
      logging: false,
    });

    canvas.toBlob((blob) => {
      if (!blob) throw new Error('The canvas could not be exported');
      const url = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.download = 'infowindow.png';
      link.href = url;
      link.click();
      URL.revokeObjectURL(url);
    }, 'image/png');
  }

  async function waitForRenderedInfoWindow() {
    const started = performance.now();
    return new Promise((resolve, reject) => {
      function check() {
        const node = document.querySelector('.gm-style-iw');
        if (node) {
          const rect = node.getBoundingClientRect();
          if (rect.width > 0 && rect.height > 0) return resolve(node);
        }
        if (performance.now() - started > 5000) {
          return reject(new Error('Rendered InfoWindow was not found'));
        }
        requestAnimationFrame(check);
      }
      check();
    });
  }

  window.initMap = initMap;
</script>

Load the Maps JavaScript API so it calls initMap, and install html2canvas with your package manager. The fallback polling loop avoids a fixed sleep: slower devices get more time, while a missing overlay eventually fails with a useful error.

Waiting for the overlay reliably

InfoWindow.open() schedules asynchronous rendering. Calling html2canvas on the same tick can capture nothing or a zero-sized element.

Use a Maps rendering signal when available

In production, prefer the visibility or rendering event documented for the Maps API version you load. Trigger capture from that event, then verify that the target has non-zero dimensions. This avoids depending on private class names.

Use DOM and layout checks as a fallback

If your version does not expose a suitable signal, poll with requestAnimationFrame until the content exists and getBoundingClientRect() reports width and height. Include a timeout and surface an error instead of waiting forever. A fixed setTimeout can race network-loaded images, fonts or slower devices.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Capture your wrapper or the bubble?

Capturing the application-owned content wrapper is more stable and avoids Maps’ private markup. Capture .gm-style-iw only when you specifically need the visible bubble. Inspect the current DOM after a Maps API update because that selector is an implementation detail.

Control dimensions and image quality

Make wrapping predictable

Set maxWidth on the InfoWindow and, when appropriate, explicit width or height on your content element. The bubble sizes itself from its content; late-loading text or images can change the result. Wait for those assets before capture if exact dimensions matter.

Render at device-pixel density

Set scale: window.devicePixelRatio for sharper output on high-density displays. Remember that increasing scale also increases canvas memory use.

Crop or change the virtual viewport

Use x, y, width and height when only part of a larger target belongs in the file. If content is clipped because the page is scrolled or responsive, set windowWidth and windowHeight to the dimensions you intend html2canvas to use. Test the resulting pixel dimensions on your largest target.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Hide controls from the export

Add data-html2canvas-ignore="true" to close buttons, capture controls or other nodes that should remain interactive in the page but not appear in the PNG.

Images, map tiles and browser security

Set useCORS: true when remote images are served with an appropriate Access-Control-Allow-Origin response header. The option does not grant permission by itself. If an image server does not allow your origin, the browser may skip the image or taint the canvas.

When a same-origin proxy is required

A same-origin proxy can fetch permitted images on your server and serve them from your own origin. Implement it with strict allowlists, size limits, content-type checks and authentication appropriate to your application. Do not proxy arbitrary URLs without considering server-side request forgery and data-ownership obligations.

What may not render

  • Cross-origin iframes cannot be read and reconstructed by html2canvas.
  • Some CSS effects and browser features are unsupported or differ from native pixels.
  • Google basemap tiles, marker images and third-party assets may not be exportable unless their response headers permit it.
  • Very large or high-scale canvases can exceed browser canvas limits.

Because of these constraints, do not promise that every map style or asset will appear. Handle export errors from toDataURL and toBlob, and test the exact assets your application uses.

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.
Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

PNG export choices

Data URL

const pngDataUrl = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.download = 'infowindow.png';
link.href = pngDataUrl;
link.click();

A data URL is convenient for small images but keeps the entire file in memory as a string.

Blob

canvas.toBlob((blob) => {
  if (!blob) return;
  const url = URL.createObjectURL(blob);
  // upload url or click a temporary download link
  URL.revokeObjectURL(url);
}, 'image/png');

toBlob is generally preferable for uploads and larger files. Revoke object URLs after use.

Common failures and fixes

Symptom Likely cause Fix
Blank or zero-size image Capture ran before asynchronous rendering. Use the Maps rendering signal or poll for a visible, laid-out node with a timeout.
InfoWindow content is missing The selector matched the bubble before its child content was ready, or a private class changed. Capture your stable content wrapper; otherwise inspect the current DOM and wait for dimensions.
Remote image omitted Image server lacks CORS permission. Enable appropriate CORS headers or serve the asset through a controlled same-origin proxy.
SecurityError during export A cross-origin resource tainted the canvas. Remove or proxy the offending asset; useCORS cannot bypass browser policy.
Text wraps differently Width, font loading or viewport differs at capture time. Set explicit dimensions or maxWidth, wait for fonts/assets, and set the intended viewport.
Blurry output Canvas rendered at CSS-pixel resolution. Use a suitable scale, usually window.devicePixelRatio, while watching memory use.
Browser freezes or export fails on huge captures Canvas dimensions exceed practical browser limits. Reduce target size or scale, crop the element, and avoid capturing the entire map when only the InfoWindow is needed.

Performance and reliability checklist

  • Capture only the content needed instead of the whole map.
  • Wait for layout, fonts and important images rather than using an arbitrary delay.
  • Use a single capture per user action and disable the button while it runs.
  • Keep a timeout and report whether the node was absent, zero-sized or failed during export.
  • Choose PNG for lossless text and transparency needs; consider another workflow when native pixel fidelity is more important than DOM-level control.
  • Test on the browsers, device-pixel ratios and map styles you support.
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 request can return a PNG, JPEG, WebP or PDF, so you do not need to load the Maps JavaScript API or coordinate an in-page capture. For a public map URL, the API can remove cookie banners, newsletter popups and chat widgets before the shot. Bot checks, blank pages, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

For a URL that is already rendered, the simplest call is:

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://www.google.com/maps -o shot.webp

See the ScreenshotNeo API documentation for options such as viewport and device presets, full-page capture, element selectors, dark mode, retina scale, custom CSS or JavaScript, clicks, waits, blocked resources, cookies, headers, geolocation, signed links, asynchronous jobs and bulk capture.

Python

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

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://www.google.com/maps' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', data);

The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan. Create a free ScreenshotNeo account to try it.

FAQ

Can html2canvas capture the InfoWindow’s pointer and close button?

It can capture whatever rendered DOM you select, including Maps-generated bubble markup when that markup is available to html2canvas. Those classes are private, so a content-wrapper capture is less brittle.

Should I capture the whole map instead?

Only when the map context is part of the deliverable. Whole-map capture adds controls, tiles and more cross-origin assets, increasing fidelity and security problems.

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

Why does a native screenshot look different?

A native browser or operating-system capture records final compositor pixels. html2canvas rebuilds the scene from DOM and CSS, so unsupported styling, iframes and blocked images can differ.

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.