Convert a JavaScript-rendered page or SPA to Markdown
Getting an SPA to Markdown means converting the DOM after JavaScript runs, not the HTML the server sent. A plain HTTP fetch of a React, Vue or Angular app returns an empty shell and a script tag, which is why docs portals, dashboards and storefronts come back blank. The Markdown API can render the page in a real browser first, wait for the content and convert what the user actually sees.
A fetched SPA is an empty shell, so the Markdown is empty too
Single-page applications ship a root div and load everything else at runtime. Convert the response body of a React app to Markdown and you get a title, maybe a noscript warning, and nothing else. The request succeeded, the content is missing, and nothing in the output tells you why.
Running your own headless browser fixes the render and creates a new problem: deciding when the page is ready. A fixed sleep is too short for the slow case and wasted time for the fast one. Network-idle heuristics never settle on pages that poll, and they fire too early on pages that load data after a click.
Microlink picks the fetch mode automatically with prerender set to auto. Force it to true for pages you know are client-rendered, add waitForSelector for the element that proves the data has arrived, and the conversion runs against the rendered DOM.
How to render an SPA before converting it to Markdown
Two options decide correctness: prerender chooses the browser, and the wait options decide when the DOM is ready to convert. The page preparation guide ranks the wait controls from the cheapest to the most forgiving.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const markdown = await microlink.markdown('https://app.example.com/docs', {
prerender: true,
waitForSelector: 'main h1'
})prerender: true skips the plain fetch and spawns a headless browser. waitForSelector pauses until the heading appears, so the Markdown string you get back includes the hydrated content.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const cards = await microlink.markdown('https://app.example.com/catalog', {
prerender: true,
waitUntil: 'domcontentloaded',
waitForSelector: '.product-card',
selectorAll: '.product-card'
})A quick lifecycle event plus a selector wait is the fastest reliable pattern when the default navigation signal is too slow. selectorAll resolves to an array with one Markdown string per card.
curl 'https://api.microlink.io/?url=https%3A%2F%2Fapp.example.com%2Fdocs&data.markdown.attr=markdown&meta=false&prerender=true&waitForSelector=main+h1'The Markdown arrives in the data.markdown field of the JSON response. Check the x-fetch-mode response header: prerender confirms that a browser render was used, and x-fetch-time reports how long the fetch step took.
- prerender auto by default, true to force a headless browser render, false for a plain HTTP GET.
- waitForSelector Pause the browser until an element matching the CSS selector appears.
- waitUntil auto by default. Also load, domcontentloaded, networkidle0 or networkidle2.
- waitForTimeout A fixed delay for pages with no stable selector. Capped by the plan timeout: 30 seconds free, 60 seconds Pro.
- click Click one selector or several to open a tab or dismiss a dialog before the conversion.
When x-fetch-mode reports fetch, the page was served as static HTML and no browser was needed. prerender: auto makes that call for you and only renders when it must, which is the right default for a mixed list of URLs.
Why a browser render comes before the Markdown conversion
The Markdown can only be as complete as the DOM it is converted from, and for a client-rendered app that DOM does not exist until the bundle has run.
The html method returns the same rendered DOM as markup when you need to parse it yourself.
The same wait logic applies to screenshots of JavaScript-rendered pages and to metadata from single-page apps, so one selector per site serves all three.
When not to: server-rendered documentation and articles convert fine with a plain fetch, and forcing prerender there only adds latency. Once the page renders, scope the conversion to keep the app chrome out of the Markdown.
FAQ
Why does my Markdown come back empty for a React or Vue app?
How do I convert an SPA to Markdown with an API?
How do I know whether a browser render was used for the Markdown?
How long can a Markdown conversion wait for a single-page app?
Can I convert content to Markdown that only appears after a click?
Solve the next problem with the same API
Clean Markdown, no boilerplate
Markdown from bot-protected pages
Bulk Markdown conversion with caching
Screenshots of JavaScript-rendered pages
PDFs of JavaScript-rendered pages
Metadata from single-page apps
Ready to convert rendered apps?
Render first, wait for the data, then convert. Start on the free tier and turn your first single-page app into Markdown today.