squirrelscan
GitHub

Large DOM: node count, depth and children per element

A page at 1,500 DOM nodes or more, deeper than 32 levels, or with an element holding more than 60 children. squirrel counts all three.

What DOM size costs

Every element on a page is memory the browser holds, work the style engine repeats on each recalculation, and work the layout engine repeats on each reflow. Past a few thousand nodes the cost shows up as slow interactions rather than slow loading, because a change that invalidates style or layout has more nodes to walk before the browser can paint.

Three numbers describe the shape of the problem. Total node count is the size of the tree. Depth is how far nesting goes, and deep trees make selector matching and layout more expensive per node. The widest element, meaning the one with the most direct children, is usually a list that renders every row instead of the rows on screen.

What squirrel checks

The rule runs on every crawled page and counts element nodes in <body> only, for all three checks, as Lighthouse’s DOM size audit does. <head> holds metadata such as hreflang alternates, preload hints and meta tags, which cost no layout, so a page with a large head and a small body reports the body’s numbers. It emits three checks:

  • dom-total-nodes: the number of elements inside <body>, not counting <body> itself. Passes with the message DOM size OK (N nodes) under 1,500 nodes, warns with Large DOM (N nodes) from 1,500 to 2,999, and fails with Excessive DOM size (N nodes) at 3,000 or more.
  • dom-depth: passes with the message DOM depth OK (N levels) at 32 levels or fewer, and warns with DOM depth excessive (N levels) above that. <body> counts as level 1.
  • dom-max-children: passes with the message Max children per element OK (N) when no element has more than 60 direct children, and warns with Element with N children found above that, naming the largest count on the page. <body> itself counts as an element here, so a body with 61 direct children warns.

Instead of all three, a single dom-total-nodes check is emitted as skipped with the message No document root found when the parse produced no root element (empty, text-only or comment-only HTML), or No body element found when the document has no <body>. A page with no parsed document emits nothing at all.

The counts are taken from the HTML the crawl fetched. On a browser-rendered audit that is the hydrated DOM, and on a raw crawl it is the markup the server sent, which can be far smaller on a client-rendered page.

How to fix it

Render the rows that are on screen rather than all of them:

import { FixedSizeList } from "react-window";

<FixedSizeList height={600} itemCount={rows.length} itemSize={40}>
  {Row}
</FixedSizeList>

For depth, the usual cause is wrapper elements added one at a time by nested components. Collapsing three divs that each set one style property into one element with three properties removes two levels everywhere that component appears.

Rule IDperf/dom-size
CategoryPerformance
ScopePer-page
Severitywarning
Weight6/10

Options

OptionTypeDefaultDescription
warn_thresholdnumber1500Warning threshold for total nodes
error_thresholdnumber3000Error threshold for total nodes
max_depthnumber32Maximum DOM depth
max_childrennumber60Maximum children per element

Configuration example

[rules."perf/dom-size"]
warn_threshold = 1500
error_threshold = 3000
max_depth = 32
max_children = 60

Enable / disable

Disable this rule

[rules]
disable = ["perf/dom-size"]

Disable all Performance rules

[rules]
disable = ["perf/*"]

Enable only this rule

[rules]
enable = ["perf/dom-size"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

  • perf/inp-hints: the interaction cost a large DOM shows up in.
  • ax/token-weight: the same markup measured as tokens an agent pays for.
  • crawl/html-size: the byte size of the document those nodes are serialised into.
  • perf/cls-hints: layout shifts, which a deep tree makes more expensive to recover from.

Performance findings ship in every audit next to the SEO, security and agent experience rules. See Core Web Vitals for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Performance section of the report. Each page reports its node count, depth and widest element. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close