DEV Community

Cover image for How to Screenshot a Specific Element on a Page (With a CSS Selector)
Nico Acosta for Grabbit

Posted on • Originally published at grabbit.live

How to Screenshot a Specific Element on a Page (With a CSS Selector)

To screenshot a specific element instead of the whole page, target it with a CSS selector and capture only its bounding box. You can do this three ways: manually in the browser's DevTools, in code with a headless browser, or over an API by sending the page URL and the selector. Which one fits depends on whether it is a one-off, whether you control the page, and how many elements you need to capture.

This guide covers all three, the difference between a true element screenshot and a client-side <div> re-render, and why element capture is often the right call for monitoring a single component.

The manual way: DevTools node screenshot

For a quick one-off, the browser already does this. In Chrome:

  1. Right-click the element and choose Inspect to select its node in the Elements panel.
  2. Open the Command Menu with Cmd/Ctrl + Shift + P.
  3. Type screenshot and choose Capture node screenshot.

Chrome saves a PNG cropped to that element. Firefox has the same feature: in the Inspector, right-click a node and pick Screenshot Node. This is perfect when you need one image once. It does not help when you need the same element from many pages, on a schedule, or from a server, which is where the next two paths come in.

The code way: capture an element's bounding box

Every headless browser can screenshot a single element. The shape in Puppeteer:

// DIY: find the element, screenshot only its box
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
const el = await page.$('#pricing-table');
await el.evaluate((node) => node.scrollIntoView());
await el.screenshot({ path: 'element.png' });
await browser.close();
Enter fullscreen mode Exit fullscreen mode

Playwright is page.locator('#pricing-table').screenshot(). Selenium 4 added it too: find the element, then call getScreenshotAs (Java) or .screenshot() (Python) on the element itself. See how to take screenshots in Selenium for the full element-capture example there.

Two things bite people:

  • Scroll into view. If the element is below the fold, the capture can come back clipped or blank. Scroll it into view first.
  • Wait for it to render. If the element is populated by JavaScript after load, the selector resolves before the content paints. Wait for the element, or add a short delay, before capturing.

This is the right path when the page is yours and you are already running a browser. The cost is the same as any DIY screenshot setup: you own the browser pool, the scaling, and the storage.

The API way: send a URL and a selector

When you want one element from a page you do not control, or the same element across many URLs, sending the work to a screenshot API is the shortest path. You pass the page URL and a CSS selector, and you get back an image cropped to that element. No browser to run, no scroll-and-wait logic to maintain:

curl https://api.grabbit.live/v1/grabs \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://stripe.com/pricing",
    "selector": ".PricingTable",
    "format": "webp",
    "delay_ms": 800
  }'
Enter fullscreen mode Exit fullscreen mode

The API renders the real page in a browser, finds the element matching selector, and returns an image_url pointing at a hosted capture of just that element. delay_ms of 800 gives content that loads after first paint time to settle before the capture. format of webp keeps the file small.

A screenshot cropped to just the header element of a web page, captured by sending the page URL and a selector to an API.

A few notes on the parameters:

  • selector takes any CSS selector: an id (#hero), a class (.PricingTable), or a more specific path. If it matches multiple elements, the first match is captured.
  • width still sets the viewport the page renders at (320 to 1920), which decides whether the element renders in its desktop or mobile layout before it is cropped. Leave it at the default 1280 for a desktop render.
  • Skip full_page here. Element capture and full-page capture are different jobs: selector crops to one element, full_page captures the entire document. If you want the whole page instead, see how to take a full-page screenshot.

Element screenshot vs a client-side div re-render

There is a popular fourth approach that looks like an element screenshot but is not one: html2canvas and similar libraries. These run in the user's browser and redraw a DOM node onto a <canvas>. That is a re-render of the DOM, not a capture of what the browser actually painted, so it has real limits:

  • It can miss or blank out cross-origin images the canvas cannot read.
  • Complex CSS (filters, some gradients, transforms, custom fonts) often renders differently from the real page.
  • It only works on a page already loaded in a browser you control, so it is no help for capturing an element from an external URL.

For a faithful image of an element as a browser renders it, you want a real render: headless Chrome in code, or a screenshot API. The client-side canvas trick is fine for capturing something inside your own running app (a generated chart, a receipt) where pixel-perfect fidelity to the live page does not matter.

When to capture one element instead of the whole page

Element capture is not just a smaller screenshot. For some jobs it is the better one:

  • Component monitoring. Watching a single pricing table, status banner, or hero for changes is cleaner against just that element. A full-page diff lights up every time an unrelated ad or footer shifts; an element capture only changes when the thing you care about does. This pairs naturally with visual regression testing.
  • Embedding a specific piece. Pulling one card, chart, or quote out of a page to show elsewhere is exactly an element crop.
  • Stable captures across pages. Capturing .product-card across a catalog gives you uniform images without per-page cropping math.

Putting it together

To screenshot a specific element: pick it with a CSS selector, then capture only its bounding box. For a one-off, DevTools' node screenshot is instant. In your own app with a browser already running, screenshot the element in Puppeteer, Playwright, or Selenium. For an element from a URL you do not control, or the same element across many pages, send the URL and selector to an API and get back a cropped image with no browser to run.

For every capture option (viewport, format, full-page, selector, delay) see the screenshot API. To capture whole pages from URLs, see how to screenshot a website from a URL.


Originally published on the Grabbit blog.

Top comments (0)