Scripts
classic <script src> in <head> without defer/async (high), synchronous external scripts in the middle of <body>, document.write(), scripts placed before stylesheets.
GitHub Packages · MIT · Node ≥ 18
Find the CSS and JavaScript that block first paint in your built site, with the defer, async, media or critical-CSS fix for each.
Find render-blocking resources in a built static site: head scripts without defer/async, heavy blocking CSS, @import chains, third-party blocking resources without preconnect, document.write, parser-blocking body scripts, print CSS without media, missing critical CSS. SARIF, GitHub Action. Zero deps.
1 Point the @mrlouiswright scope at GitHub Packages in .npmrc
@mrlouiswright:registry=https://npm.pkg.github.com2 Install
npm i -D @mrlouiswright/render-pulsePublished to GitHub Packages, not npmjs.com. GitHub Packages needs a GitHub token with read:packages to install, even for public packages: add //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} to .npmrc or run npm login --registry=https://npm.pkg.github.com.
$ npx render-pulse dist --base-url https://example.comrender-pulse — https://example.com1 page(s) at https://example.com HIGH index.html:2 /: <script src="/js/jquery.js"> (0.0 KB gzip) in <head> blocks rendering [render-blocking-script]HIGH index.html:7 /: <script src="https://tags.example.org/t.js"> in <head> blocks rendering [render-blocking-script]MEDIUM css/all.css css/all.css uses @import [render-css-import]MEDIUM index.html /: render-blocking resource from https://fonts.example.org without <link rel="preconnect"> [render-third-party-blocking]MEDIUM index.html /: render-blocking resource from https://tags.example.org without <link rel="preconnect"> [render-third-party-blocking]MEDIUM index.html /: 56.2 KB gzip (195.4 KB raw) of render-blocking CSS (budget 50.0 KB) [render-blocking-css-heavy]
Nothing is painted until the browser has downloaded and parsed every render-blocking stylesheet and synchronous script in “<head>”. One forgotten “<script src>” without defer, a 200 kB framework stylesheet or an @import chain can add seconds to First Contentful Paint and LCP on mobile. render-pulse reads your static build output, works out which resources are actually on the critical path (respecting defer, async, “type=module”, media, disabled and alternate stylesheets) and reports each one with a concrete fix.
CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies. Works on any static build output (Astro, Next.js “output: 'export'”, Eleventy, Hugo, Jekyll, Docusaurus, VitePress, plain HTML) and runs offline: point it at the folder your build writes. By Louis Wright.
classic <script src> in <head> without defer/async (high), synchronous external scripts in the middle of <body>, document.write(), scripts placed before stylesheets.
gzip size of all render-blocking CSS per page against a budget (default 50 kB), too many blocking stylesheets, @import in CSS files or inline styles, print stylesheets without media="print", and a hint to inline critical CSS when blocking CSS exceeds the 14 kB first round trip.
render-blocking resources from other origins without <link rel="preconnect">.
media="print" onload="this.media='all'", module scripts, JSON-LD and template scripts are not reported.
# Example
npx render-pulse dist# Example
npx render-pulse public --max-css-kb 30 --fail-on medium# Example
npx render-pulse _site --format sarif > render.sarifpermissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
render-pulse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with: { persist-credentials: false }
- run: npm ci && npm run build # produce the static site first
- uses: MrLouisWright/render-pulse@v1
with:
path: dist
fail-on: high
sarif: render-pulse.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: render-pulse.sarif }
| Flag | Description |
|---|---|
| Site | |
--base-url <url> | Public URL of the site, e.g. https://example.com (default: inferred from canonical/og:url) |
--include <globs> | Only check these pages (file or URL path globs, comma-separated) |
--exclude <globs> | Skip these pages |
--clean-urls | Pages are served without .html (about.html -> /about) |
| Render | |
--max-css-kb <n> | Render-blocking CSS budget per page, gzip KB (default 50) |
| Common | |
-c, --config <file> | JSON config (default: .render-pulse.json or "renderPulse" in package.json) |
-f, --format <fmt> | text (default) | json | sarif | markdown |
--json <file> | Also write a JSON report |
--sarif <file> | Also write SARIF 2.1.0 |
--markdown <file> | Also write Markdown (e.g. for a job summary) |
--fail-on <sev> | critical | high | medium | low | info | none (default: high) |
--list-rules | Print rules and default severities |
--no-fixes | Hide fix suggestions in text output |
--no-color | Disable colours |
Full reference, config file format and API in the README.
Support this project
I build free, open-source tools that help developers ship safer websites. Every coffee helps keep them maintained, documented and free for everyone.
Buy me a coffee (opens Stripe in a new tab)Secure checkout by Stripe