Same library loaded twice: duplicate JavaScript on a page
One library loaded from two script tags, or at two different versions. squirrel reads library names out of the script URLs.
What a duplicate library is
Two script tags loading the same library from different URLs cost the bytes twice and, worse, can leave two copies in memory that do not share state. With React that means hooks failing at runtime. With jQuery it means whichever loaded last winning and any plugin registered against the first one disappearing.
Two different versions is the sharper version of the problem. It usually happens when a third-party embed brings its own copy of a library the page already loads, or when a tag manager injects one.
What squirrel checks
The rule runs on every crawled page over its <script src> elements. It extracts a library name and, where the URL carries one, a version, then groups by name. It emits one or two checks:
duplicate-js-version-mismatch: fails with the messageN library(s) loaded with different versions, listing each as<name> (versions: 3.6.0, 3.7.1).duplicate-js-same-version: warns with the messageN library(s) loaded multiple times, listing each as<name> (2x).duplicate-js: passes with the messageNo duplicate JavaScript libraries detected, recording how many scripts it analysed.
Names are matched from four URL shapes, in order: an unpkg path, a jsDelivr npm path, a cdnjs path, and a generic /<name>.js path where the name may be followed by a dotted or hyphenated numeric version and by .min. When none matches, the leading run of letters, digits and hyphens in the filename becomes the name. Names are lowercased; versions are compared as written. The rule reads the <script src> elements on the page only.
A page with no parsed document emits nothing at all.
Elements inside <noscript> are skipped: a browser with scripting enabled reads <noscript> content as plain text, so nothing in it loads or renders. See content inside noscript.
How to fix it
Load each library once, from one place. Where a bundler is in use, that usually means marking the library external and providing it yourself rather than letting two chunks each include it:
<script src="https://unpkg.com/react@18.3.1/umd/react.production.min.js"></script>Where a third-party embed brings its own copy, the vendor usually has a build that uses the page’s copy instead.
The common false positive comes from matching on filenames. Two unrelated files that share a name, such as /a/main.js and /b/main.js, both reduce to main and are reported as one library loaded twice. Check the listed names before changing anything.
| Rule ID | perf/duplicate-js |
| Category | Performance |
| Scope | Per-page |
| Severity | warning |
| Weight | 5/10 |
Enable / disable
Disable this rule
[rules]
disable = ["perf/duplicate-js"]Disable all Performance rules
[rules]
disable = ["perf/*"]Enable only this rule
[rules]
enable = ["perf/duplicate-js"]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.
Related rules
- perf/js-file-size: the size those duplicated bytes add up to.
- perf/js-libraries: which libraries the page loads and whether any version is vulnerable.
- perf/legacy-js: polyfills, another kind of code shipped more than once.
- perf/inp-hints: the main-thread cost of executing all of it.
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 duplicated library is listed with its versions. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.