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 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 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.
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.
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.
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.
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.
- 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 selector waits beat timers for JavaScript-rendered screenshots
Waiting for a condition is both faster and more reliable than waiting for a duration.
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.
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.
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?
How long can the screenshot API wait for JavaScript to finish?
Do I need prerender to screenshot a single-page app?
Can I wait for several conditions before a screenshot?
How do I screenshot content that loads on scroll?
Solve the next problem with the same API
Screenshot a single element
Screenshots without cookie banners or ads
Faster, smaller screenshots
PDFs of JavaScript-rendered pages
Markdown from JavaScript-rendered pages
Metadata from single-page apps
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.