SharePointVault ~/gotchas/spfx ⌕ Search 404 pages, error messages, scripts…Ctrl K ★ Star
M↓ README.mdM↓ load-a-lazy-chunk-from-the-console-via-the-webpack-runtime.md
sharepoint-vault›gotchas›spfx›load-a-lazy-chunk-from-the-console-via-the-webpack-runtime.mdhistoryedit on GitHub
● GOTCHAspfx· 3 min read

Load an SPFx bundle's lazy chunk from the browser console — no clicking through the UI

applies to: SharePoint Online, SPFx bundles built with webpack 5 (verified on SPFx 1.22.2)reviewed: 2026-11-11#spfx#webpack#lazy-loading#chunks#smoke-test#sri#debugging

A lazy chunk is only tested when something calls import(), and the feature behind it may be expensive or have side effects (an AI call, an import that writes, an email). You do not need the feature: a webpack 5 bundle that has lazy chunks keeps a global self.webpackJsonp_<id> array (the chunks register themselves into it), and pushing [[fakeId], {}, callback] into it makes webpack call your callback with the bundle's own __webpack_require__. req.e(chunkId) then loads the chunk through the real runtime — same URL, same public path, same integrity and crossOrigin hooks — and only registers its modules; no module code runs.

Líně načítaný chunk je otestovaný, jen když něco zavolá import(), a funkce za ním může být drahá nebo mít vedlejší účinky (volání AI, import, který zapisuje, e-mail). Funkci nepotřebuješ: bundle z webpacku 5, který má líně načítané chunky, drží globální pole self.webpackJsonp_<id> (chunky se do něj samy registrují) a když do něj pushneš [[falešnéId], {}, callback], webpack zavolá tvůj callback s vlastním __webpack_require__ bundlu. req.e(chunkId) pak načte chunk skutečným runtime – stejná adresa, stejný public path, stejné háčky integrity a crossOrigin – a jen zaregistruje jeho moduly; kód modulů se nespustí.

When to use it

  • Post-release smoke test of a component whose chunks moved to a new folder (see the related gotcha below) — every chunk.* must answer 200.
  • Checking that a build step really puts integrity on chunk <script> tags and that the browser accepts the hash.

Snippet

Run it in the console of a page where the component has already loaded its entry bundle.

// 1) Find the bundle's JSONP array: open any chunk file of the bundle — it starts with
//    (self.webpackJsonp_<32 hex>=self.webpackJsonp_<32 hex>||[]).push([[<chunkId>], …
const g = 'webpackJsonp_0123456789abcdef0123456789abcdef';   // ← from the chunk file
const chunkId = 774;                                            // ← the [[…]] number in that file

// 2) Borrow the bundle's own __webpack_require__ (the third element is a runtime callback)
let req = null;
self[g].push([['probe' + Date.now()], {}, r => { req = r; }]);

// 3) Load the chunk through the real runtime
try { await req.e(chunkId); console.log('chunk ' + chunkId + ' loaded'); }
catch (e) { console.error('chunk ' + chunkId + ' failed:', e); }   // ChunkLoadError = 404, CSP or SRI

// 4) Inspect what the runtime inserted (webpack may remove the tag after load — read it right away)
[...document.querySelectorAll('script[src*="chunk."]')]
  .map(s => s.src + ' integrity=' + (s.integrity || '–') + ' crossorigin=' + s.crossOrigin);

self[g] does not exist until the entry bundle has run — load the component first (open the page, select a list item for a command set, …).

Proving the browser enforces the hash

With a chunk that loaded with integrity, add the same file once more with a hash nothing can match — the browser must refuse it:

await new Promise(res => {
  const s = document.createElement('script');
  s.crossOrigin = 'anonymous';
  s.integrity = 'sha384-' + btoa(String.fromCharCode(...new Uint8Array(48)));  // valid form, never matches
  s.src = '<chunk url>?neg=' + Date.now();
  s.onload = () => res('loaded — NOT enforced');
  s.onerror = () => res('blocked — enforced');
  document.head.appendChild(s);
});

Use a well-formed digest: a malformed integrity value is ignored by the browser (SRI spec) and the script runs.

Notes

  • Use a fresh fake id on every push: webpack calls the callback only when at least one id in the pushed entry is not installed yet, then marks it installed. Nothing ever requests that id, so it is harmless.
  • req.e(id) loads exactly one chunk. An import() whose target is split across several chunks compiles to Promise.all([req.e(a), req.e(b)]), so search the entry bundle for the call site (.e(774)) to see which ids belong together and load each of them.
  • The modules stay unevaluated until something calls req(moduleId).
  • In SPFx production builds the chunk ids are numbers; a named chunk (webpackChunkName) keeps its name only in the file name (chunk.qa-admin_<hash>.js registers as id 999), so read the id from the file.
  • The global's name comes from webpack's output.chunkLoadingGlobal; SPFx sets it per bundle to webpackJsonp_<hash>.
  • Related: A library component loaded from your own manifest fetches its lazy chunks from that manifest's folder — the failure this check catches.
Was this helpful?ask or add your experience on GitHub →
Saved you an hour? A star on GitHub helps other SharePoint people find the vault.★ Star on GitHub
last reviewed: 2026-11-11last updated: 2026-11-11historyedit on GitHubview source