Skip to content
Screenshot API · Use case

Screenshot JavaScript-rendered pages once the content is ready

To screenshot JavaScript-rendered pages you have to capture after the framework has done its work, not when the HTML arrives. React, Vue and Angular apps paint an empty shell first and fill it in later, and charts, maps and infinite lists arrive later still, so a capture taken at page load shows spinners and placeholders. The wait options of the Screenshot API fire the capture when the content you care about exists.

A dynamic feed captured after its main content rendered
Generated live by the API call below: the capture waits for the main element before firing.
The problem

A screenshot taken at load shows the spinner, not the app

The browser considers a page loaded when its resources are fetched, not when the framework has hydrated and the data has arrived. For client-rendered apps those two moments can be seconds apart, and the screenshot fires in between.

A fixed timer is the usual patch, and it fails in both directions. Three seconds is too short on a slow day and wasted time on every other request, and the right number differs for each page you capture. Waiting for network silence helps until the page opens a long-polling connection in the background and never goes quiet.

Microlink offers three levels of control. waitUntil chooses the lifecycle event, waitForSelector pauses until a specific element appears, and waitForTimeout adds a fixed delay as a last resort. Combined with click and scroll, you capture the exact state you need.

How it works

How to wait for a selector before the screenshot fires

Navigate fast, then wait for the one thing that proves the page is ready. Add a timer only when nothing stable exists to wait for. The page interaction guide walks through every wait, click and scroll option.

1 · Wait for a selector
import createClient from 'microlink.io'

const microlink = createClient({
  apiKey: process.env.MICROLINK_API_KEY
})

const { url } = await microlink.screenshot('https://app.example.com/report', {
  waitUntil: 'domcontentloaded',
  waitForSelector: '.chart svg'
})

domcontentloaded fires as soon as the DOM is parsed, without waiting for images or third-party scripts. The selector wait then holds the capture until the chart’s svg exists, which is the fastest reliable pattern.

2 · Trigger lazy content first
import createClient from 'microlink.io'

const microlink = createClient({
  apiKey: process.env.MICROLINK_API_KEY
})

const { url } = await microlink.screenshot('https://example.com', {
  scroll: '#reviews',
  waitForSelector: '#reviews .card',
  fullPage: true
})

scroll brings a lazy section into view so it starts loading. The capture waits for its cards and then takes the whole page with fullPage.

3 · Open a tab, then wait for its panel
import createClient from 'microlink.io'

const microlink = createClient({
  apiKey: process.env.MICROLINK_API_KEY
})

const { url } = await microlink.screenshot('https://app.example.com/analytics', {
  click: '#tab-revenue',
  waitForSelector: '#panel-revenue canvas'
})

click changes the page state and waitForSelector holds the capture until the result of that click exists. click also accepts an array of selectors in the SDK when several elements need a click.

4 · The same request as a URL
curl 'https://api.microlink.io/?url=https%3A%2F%2Fapp.example.com%2Freport&screenshot=true&meta=false&waitUntil=domcontentloaded&waitForSelector=.chart+svg'

Works on the free endpoint with no API key. The request timeout is 30 seconds on the free plan and 60 seconds on Pro, and every wait has to fit inside it.

Parameters used
  • waitUntil auto (default), load, domcontentloaded, networkidle0 or networkidle2. Accepts an array.
  • waitForSelector Pauses until the CSS selector matches an element in the page.
  • waitForTimeout A fixed delay such as 3s or 3000. Cannot exceed the request timeout of your plan.
  • scroll Scrolls to a selector so lazy sections load before the capture.
  • click Clicks one selector, or several, to open a tab or expand a section first.
  • animations false by default, so CSS animations and transitions never freeze mid-way in the capture.

screenshot.element already waits for its own selector to be visible, so a separate waitForSelector is only needed when you capture the viewport or the full page.

Why it works

Why selector waits beat timers for JavaScript-rendered screenshots

Waiting for a condition is both faster and more reliable than waiting for a duration.

01 · Finishes as soon as ready
A selector wait ends the moment the element appears.
A timer has to be long enough for the slowest case and wastes time in every other case. Waiting for the element you need returns as early as possible and still handles the slow case.

Pair it with domcontentloaded to skip waiting for images and third-party scripts that do not affect the content. The same advice leads the list for faster, smaller screenshots.

02 · Deterministic state
The same condition produces the same capture.
Animations are disabled by default and the wait targets a specific DOM state, so captures do not vary between runs, which is what comparisons and monitoring need.

For content that appears after interaction, click the trigger and wait for its result in the same request. The browser automation feature lists every option that shapes the page before capture.

03 · Escape hatches
networkidle and timeouts cover the rest.
Pages without a stable selector can wait for network silence with networkidle0, or for a fixed delay with waitForTimeout when nothing else is observable. waitUntil also accepts an array when one event alone fires too early.

When not to: if the page is server-rendered and complete at load, adding waits only makes the capture slower. Set javascript to false to skip script execution altogether.

FAQ

Why does my screenshot show a spinner or an empty page?

The capture fired before the app finished rendering. Add waitForSelector for an element that only exists once the data has loaded, or switch waitUntil to networkidle0 when the page keeps fetching. The screenshot troubleshooting guide covers the other causes.

How long can the screenshot API wait for JavaScript to finish?

Up to the request timeout of your plan: 30 seconds on the free endpoint and 60 seconds on Pro. A waitForTimeout larger than that is ignored, so prefer a selector wait that ends as soon as the content is there.

Do I need prerender to screenshot a single-page app?

No. Screenshots always render in a real browser. prerender controls whether metadata extraction uses a browser or a plain HTTP fetch, a different concern covered in metadata from single-page apps.

Can I wait for several conditions before a screenshot?

Yes. waitUntil accepts an array of lifecycle events, and waitForSelector, scroll and click compose in one request: click a tab, wait for its panel, then capture.

How do I screenshot content that loads on scroll?

Pass scroll with the selector of the lazy section so the browser brings it into view and the section starts loading, then add waitForSelector for an element inside it. Add screenshot.fullPage when you want the whole page in one image.
Related use cases

Solve the next problem with the same API

Screenshot a single element

Crop the capture to one CSS selector, such as a chart or a pricing table, with a transparent background if you need it.

Screenshots without cookie banners or ads

Ads, trackers and consent popups are blocked before the page renders, and one option dismisses first-party banners.

Faster, smaller screenshots

Skip metadata, pick JPEG quality and pixel density, and wait for a selector instead of a timer to cut time and bytes.

PDFs of JavaScript-rendered pages

Print dashboards and single-page apps after they render: wait for the chart, open tabs and sections, then print.

Markdown from JavaScript-rendered pages

Render single-page apps in a real browser, wait for the content, then convert the finished DOM to Markdown.

Metadata from single-page apps

Render React, Vue or Angular apps in a headless browser, wait for the tags, then read the normalized metadata.

Ready to capture the finished page?

Wait for the element that matters and capture it once. Start on the free tier and screenshot your first single-page app today.