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.

Direct answer: wait until the Leaflet basemap tiles and polygon paths are visible, capture the map element with html2canvas, then call canvas.toDataURL('image/png') and download the result. A working browser implementation is:

async function saveLeafletPng() {
  const mapElement = document.getElementById('map');
  const canvas = await html2canvas(mapElement, {
    useCORS: true,
    backgroundColor: '#fff',
    scale: window.devicePixelRatio
  });
  const link = document.createElement('a');
  link.download = 'leaflet-map.png';
  link.href = canvas.toDataURL('image/png');
  link.click();
}

The difficult part is usually not the download. Cross-origin tile images, an incorrectly sized map container, unfinished tile loading, and browser canvas limits determine whether the polygons and basemap appear in the PNG. This guide covers the reliable setup, alternatives, failure recovery, and an automated API option.

Prepare the Leaflet map before capturing it

Give the map container an explicit, nonzero height. Leaflet cannot render a useful image from an element whose height collapses to zero.

#map {
  width: 100%;
  height: 500px;
}

Create the map and polygon normally. For example:

const map = L.map('map').setView([51.505, -0.09], 13);

const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© OpenStreetMap contributors',
  crossOrigin: true
}).addTo(map);

const polygon = L.polygon([
  [51.509, -0.08],
  [51.503, -0.06],
  [51.51, -0.047]
], { color: 'blue', fillColor: '#3388ff', fillOpacity: 0.35 }).addTo(map);

The crossOrigin option is important when you need to read tile pixels. Every raster source must permit cross-origin access, including additional tile layers and image overlays. A single image without compatible CORS headers can taint the canvas.

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.

Wait for the visible state

Start the capture only after the map has been laid out and the tiles have finished loading. For a simple map, waiting for the tile layer’s load event is sufficient; a short delay can also allow polygon styling and other asynchronous code to finish.

function waitForTiles(tileLayer) {
  return new Promise(resolve => {
    if (tileLayer._tilesToLoad === 0) return resolve();
    tileLayer.once('load', resolve);
  });
}

async function downloadMap() {
  await waitForTiles(tiles);
  await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
  await saveLeafletPng();
}

For applications that fetch polygon data remotely, call the download function only after the data has been added and the relevant request has completed.

Capture the map with html2canvas

Include html2canvas, call it with the actual map element, and convert the returned canvas to PNG. The library reconstructs the visible DOM rather than taking a browser-native pixel screenshot, so it follows browser CSS and content-security rules.

<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
<button type="button" onclick="downloadMap()">Save PNG</button>

async function saveLeafletPng() {
  const mapElement = document.querySelector('#map');
  const canvas = await html2canvas(mapElement, {
    useCORS: true,
    backgroundColor: '#fff',
    scale: window.devicePixelRatio
  });

  const link = document.createElement('a');
  link.download = 'leaflet-map.png';
  link.href = canvas.toDataURL('image/png');
  link.click();
}

Include or exclude controls

Because html2canvas captures the DOM composition, controls, legends, zoom buttons, and surrounding HTML can appear in the output. Mark an unwanted element with data-html2canvas-ignore:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<div class="leaflet-control-zoom" data-html2canvas-ignore="true">...</div>

Alternatively, pass an ignoreElements callback when you need a rule that cannot be expressed in markup.

Capture the full map element

Capture #map, not the page body. If the map is inside a larger scrolling layout, set html2canvas’s windowWidth and windowHeight to the required scroll dimensions. A clipped result usually means the selected element or viewport does not include the complete visible area.

Fix CORS and tainted-canvas errors

A browser prevents JavaScript from reading pixels from a cross-origin image unless that server grants permission. html2canvas’s useCORS: true works only when the image server sends an appropriate Access-Control-Allow-Origin response header. It cannot bypass browser content-policy restrictions. See the html2canvas guidance on configuration and getting started.

If canvas.toDataURL() throws a security error, or tiles are missing, check the tile requests in browser developer tools. Set Leaflet’s tile-layer crossOrigin option, and configure it for every raster source. If you control neither server, use a same-origin proxy that fetches the image and returns suitable headers, or choose a tile provider that explicitly supports browser CORS. The proxy must comply with the provider’s terms and preserve required attribution.

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

Use leaflet-image when you need a layer-focused export

leaflet-image renders Leaflet layers into a canvas without a server component. It is useful when the output is primarily the map itself, but it does not reproduce arbitrary HTML. Controls, legends, and L.divIcon marker content are omitted.

Render polygon paths with Leaflet’s Canvas renderer:

const map = L.map('map', { preferCanvas: true });
const polygon = L.polygon(coords, {
  renderer: L.canvas(),
  color: '#1368ce'
}).addTo(map);

After all layers and CORS-capable tiles are ready:

leafletImage(map, function (error, canvas) {
  if (error) {
    console.error(error);
    return;
  }
  const link = document.createElement('a');
  link.download = 'leaflet-layers.png';
  link.href = canvas.toDataURL('image/png');
  link.click();
});

The project’s documentation describes Canvas and CORS requirements and warns that generated images generally need the same attribution as the source map. Keep the provider’s attribution in the exported image or add it afterward.

Choose the right export approach

Requirement html2canvas leaflet-image Static image API
Map controls, legends, surrounding HTML Best fit; reconstructs the DOM Not included Usually not included unless the service supports annotations
Polygon paths Visible SVG or Canvas paths can be captured Use Canvas rendering for reliable paths Depends on API’s geometry support
CORS requirement All cross-origin images must permit access, or use a proxy Tiles and markers must be CORS-capable Provider fetches resources server-side
Rendering location Browser Browser Server-side service
Operational limits Browser canvas dimensions and CSS support Browser canvas dimensions and supported layers Service quotas, parameters and licensing

Use html2canvas for a faithful representation of the visible page composition. Choose leaflet-image for a clean layer export when controls and HTML markers are unnecessary. For repeatable reports, background jobs, or many URLs, a static-map service is often simpler and faster than maintaining a browser capture pipeline; leaflet-image’s documentation names the Mapbox Static Image API as an example.

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

Improve sharpness without exhausting the browser

The scale option controls output resolution. Using window.devicePixelRatio produces sharper output on high-density displays, but it multiplies canvas width and height and therefore memory use. Very large maps can exceed browser canvas limits or fail with an empty result. Try a fixed scale such as 2, reduce the map dimensions, or export smaller regions when a high-DPI capture fails.

PNG preserves crisp polygon edges and supports transparency, but files can be large. If transparency is not needed, set a white backgroundColor for predictable output. For photographs or large basemaps, convert the canvas to JPEG or WebP, accepting their respective quality and transparency trade-offs.

Common failures and precise fixes

  • Blank PNG: verify that #map has nonzero width and height, that Leaflet has been initialized, and that capture runs after tiles and polygons are loaded.
  • Basemap missing but polygon appears: inspect tile CORS headers, add crossOrigin: true, enable useCORS, or route tiles through a compliant same-origin proxy.
  • SecurityError from toDataURL: one image tainted the canvas. Check every tile and overlay, not just the first visible layer.
  • Output is cropped: select the map element rather than a parent with clipping, and provide appropriate windowWidth and windowHeight values.
  • Controls or legends are absent: this is expected with leaflet-image. Use html2canvas for HTML controls, or draw the required labels into a separate layer.
  • divIcon markers disappear: leaflet-image does not render HTML-based marker content. Replace them with Canvas/SVG-compatible layers or use html2canvas.
  • Polygon is missing in leaflet-image: set preferCanvas: true on the map or renderer: L.canvas() on the polygon, then recapture after the layer has been added.
  • Blurry output: increase scale gradually while monitoring canvas dimensions and memory.
  • Attribution is missing: retain Leaflet’s attribution control or add equivalent attribution text to the exported composition; tile-provider terms still apply.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

For automated screenshots, a server-side capture avoids assembling a browser, waiting for tiles, and handling DOM/CORS details yourself. ScreenshotNeo is the recommended screenshot API: it removes cookie banners, newsletter popups and chat widgets before capture, bills only clean shots, and has the lowest paid plan.

One GET request returns PNG, JPEG, WebP or PDF. Full-page capture, custom CSS and JavaScript, selector capture, device and viewport settings, geolocation, headers, cookies, waiting rules, request blocking and other options are available in the ScreenshotNeo documentation. Use the map page’s URL as the target; ensure the polygon is rendered by the time the requested wait condition completes.

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

cURL

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

Python

import requests

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

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/map'
});
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());
await import('node:fs/promises').then(fs => fs.writeFile('leaflet-map.webp', data));

ScreenshotNeo identifies the result with X-Page-Verdict and X-Billed headers. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

Frequently Asked Questions

Can I export only the polygon and not the basemap?

Yes. Hide the tile layer before capture, or create a separate Canvas/SVG layer containing the polygon and export that element. Remember that a transparent export may need a transparent background instead of html2canvas’s white default.

Why does a polygon look different in the PNG than on screen?

html2canvas reconstructs styles in the browser and leaflet-image redraws supported layers on Canvas. Check renderer choice, zoom timing, CSS rules, and output scale when stroke widths or fills differ.

Should I use PNG, JPEG, or WebP?

PNG is the safest choice for crisp boundaries, text, transparency and lossless output. JPEG is smaller for photographic content but loses quality and transparency. WebP can reduce size when your delivery pipeline supports it.

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.