What Is Cheerio? β Definition and How It Works
Learn what Cheerio is, how its jQuery-style API parses HTML in Node, why it does not run JavaScript, and where it fits against a headless browser.
Cheerio is Node's standard HTML parsing library. It implements a familiar jQuery-style API over a parsed document, without a browser, a DOM implementation, or any JavaScript execution.
1. What Is Cheerio?
It parses an HTML string into a traversable structure and exposes jQuery's selection API over it.
import * as cheerio from 'cheerio';
const $ = cheerio.load(html);
const title = $('h1').first().text().trim();
const prices = $('.product .price').map((i, el) => $(el).text()).get();
- Key idea: jQuery's ergonomics, server-side, with none of the browser.
- Mechanism: parse with
htmlparser2orparse5, then query the resulting tree. - Not included: networking. Fetch with Axios,
fetchorgotand pass the string in.
2. Why the jQuery API
It is a deliberate choice rather than nostalgia. jQuery's selection and traversal API is one of the most widely known interfaces in JavaScript, so most developers can use Cheerio productively with no learning curve.
The methods behave as expected:
$('.product').each((i, el) => {
const $el = $(el);
console.log($el.find('.name').text(), $el.attr('data-id'));
});
find, parent, siblings, next, closest, attr, text, html β all present, all familiar.
3. What It Deliberately Omits
Cheerio is not a browser, and the omissions are the point:
- No JavaScript execution. It parses markup; it does not run scripts.
- No rendering. Cheerio parses markup without a layout engine, so element geometry and computed visibility are unavailable.
- No CSS resolution.
.css()reads inline style attributes, not computed styles. - No network. It never fetches anything.
These are why it is fast. There is no engine to start and no page to render β just a parse and a tree walk, in milliseconds against a browser's hundreds.
4. Cheerio vs. a Headless Browser
| Cheerio | Playwright / Puppeteer | |
|---|---|---|
| Runs JavaScript | No | Yes |
| Memory per page | A few MB | 100β300 MB |
| Time per page | Milliseconds | Hundreds of ms to seconds |
| Concurrency | Thousands | Dozens |
| Client-rendered content | Invisible | Visible |
| Interaction | None | Full |
The decision is straightforward: if the content is in the HTML you fetched, Cheerio. If it is not, you need something that executes scripts β or, better, the endpoint that supplies the data.
A useful middle path: fetch with an HTTP client, check whether the data is present as embedded JSON (__NEXT_DATA__ and similar), and parse that directly. Faster than Cheerio and more stable than either, because JSON does not restructure when a designer changes the layout.
5. Choosing a Parser
Cheerio can parse with htmlparser2 (fast, lenient) or parse5 (spec-compliant, matches what a browser produces).
cheerio.load(html, { xml: false }); // default
cheerio.load(html, null, false); // fragment mode
The distinction matters with badly broken markup: a browser recovers from errors in a specific, specified way, and only a spec-compliant parse reproduces the tree DevTools shows you. If a selector works in the browser console and not in Cheerio, that mismatch is usually why.
6. Practical Notes
.text()concatenates descendants. Selecting a container gives you every nested string joined together, including whitespace. Select precisely and trim.- Empty selections are silent.
$('.gone').text()returns'', not an error. Zero results after a redesign look identical to a genuinely empty field β assert on counts. .map()returns a Cheerio object. Call.get()for a real array. Forgetting this is the most common first-time mistake.- Anchor on stable attributes.
data-*and IDs survive redesigns; generated class names do not. - It handles large documents well, but a full parse still allocates. Stream and parse in chunks for very large inputs.
7. Real-World Examples
- A crawler parsing thousands of pages a second on one process, where a browser would have managed dozens.
- A scraper storing empty strings for a month, because nothing checked that a product page yielded products.
- A selector working in DevTools and failing in Cheerio, resolved by parsing with
parse5on malformed markup. - A pipeline that dropped Cheerio entirely after finding the same data in
__NEXT_DATA__. - A
.map()result that would not iterate, missing the.get()call.
8. Summary
Cheerio parses HTML in Node and exposes a jQuery-style API for selecting and traversing it. It runs no JavaScript, renders nothing, and fetches nothing β which is precisely why it is orders of magnitude cheaper than a headless browser.
Pair it with an HTTP client for server-rendered content. Where content is built client-side, Cheerio cannot see it, and the right answer is usually the page's own JSON endpoint rather than a browser.
Its characteristic failure is silence: empty selections return empty strings rather than raising, so extraction that has stopped working looks exactly like data that is absent. Assert on what you extracted, and anchor on selectors that carry meaning rather than styling.