docs: Lazy loading examples#2600
Conversation
|
pkg.pr.new packages benchmark commit |
📊 Bundle Size Comparison
👀 Notable resultsStatic test results:No major changes. Dynamic test results:No major changes. 📋 All resultsClick to reveal the results table (355 entries).
If you wish to run a comparison for other, slower bundlers, run the 'Tree-shake test' from the GitHub Actions menu. |
Resolution Time Benchmark---
config:
themeVariables:
xyChart:
plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
title "Random Branching (🔴 PR | 🔵 main | 🟢 release)"
x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
y-axis "time (ms)"
line [0.94, 1.92, 4.54, 6.53, 7.51, 11.40, 22.57, 25.52]
line [0.95, 1.97, 4.04, 6.72, 7.49, 10.22, 20.41, 24.17]
line [0.99, 2.06, 4.07, 6.28, 7.55, 11.84, 22.95, 26.57]
---
config:
themeVariables:
xyChart:
plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
title "Linear Recursion (🔴 PR | 🔵 main | 🟢 release)"
x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
y-axis "time (ms)"
line [0.33, 0.71, 0.71, 0.87, 1.16, 1.20, 1.44, 1.58]
line [0.35, 0.52, 0.72, 0.90, 1.18, 1.23, 1.53, 1.68]
line [0.31, 0.59, 0.75, 0.89, 1.21, 1.24, 1.46, 1.64]
---
config:
themeVariables:
xyChart:
plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
title "Full Tree (🔴 PR | 🔵 main | 🟢 release)"
x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
y-axis "time (ms)"
line [0.86, 2.19, 4.14, 6.81, 12.94, 27.35, 57.50, 117.17]
line [0.89, 1.86, 4.41, 6.20, 12.43, 26.25, 56.77, 120.58]
line [0.80, 2.00, 3.48, 6.59, 13.16, 27.20, 54.84, 116.14]
|
8a1b9c8 to
40314bb
Compare
There was a problem hiding this comment.
Important
The Suspense boundary was removed from ExamplePage but useAtomValue(sourceAtom) in ExampleView now suspends outside any boundary — this could leave users staring at a blank page if no boundary exists higher up in the tree.
Reviewed changes — lazily loads example source files and sandbox module type definitions to reduce the initial bundle shipped to the browser, and precomputes API-detection and pnpm workspace parsing at build time via comptime.
- Lazy-load example source files —
import.meta.globfor.ts/.tsxfiles dropseager: true, producing() => Promise<string>loaders wrapped in asourceAtomJotai async atom. - Precompute
usedApisat build time viacomptime—usedApis.tseagerly loads example source during comptime evaluation and ships the computedRecord<string, string[]>to the browser instead of scanning file content at runtime. - Precompute
pnpmWorkspaceYamlat build time viacomptime—pnpmWorkspace.tsreads and parsespnpm-workspace.yamlduring comptime evaluation, keeping theyamlparser out of the browser bundle. - Lazy-load
CodeEditorwithReact.lazyand wrap it in a<Suspense>fallback showing a loading placeholder. SANDBOX_MODULESobject →sandboxModulesAtomasync atom — type definitions fromnode_modulesand package sources are now loaded on demand instead of eagerly at module init.ExampleSrcFile→PendingExampleSrcFileintermediate type withgetContent/getTsnotoverContentdeferred loaders, resolved byresolveExampleSrcFile.- Add
comptime@0.1.0with a custom Vite patch — the patch adds?rawquery support and runner-recreation logic needed for comptime evaluation to work with raw file imports.
⚠️ useAtomValue(sourceAtom) suspends without a Suspense boundary
ExamplePage.tsx removed its <Suspense> wrapper. ExampleView.tsx now calls useAtomValue(sourceAtom) — an async Jotai atom — at the component top level, before the new <Suspense> that wraps CodeEditor. When sourceAtom hasn't resolved yet, React will suspend ExampleView, but there is no Suspense boundary between useAtomValue and the ExamplePage return value. If no Suspense boundary exists higher in the component tree (e.g. nothing in the Astro layout), the app will show a blank or broken page.
Technical details
## Affected sites
- `apps/typegpu-docs/src/components/ExamplePage.tsx`:52 — `Suspense` removed
- `apps/typegpu-docs/src/components/ExampleView.tsx`:81 — `useAtomValue(sourceAtom)` called before the new Suspense boundary at line 158
## Required outcome
- Ensure a Suspense boundary exists between the point where `useAtomValue(sourceAtom)` suspends and the nearest parent component render. Either restore the `Suspense` in `ExamplePage.tsx` or wrap `useAtomValue(sourceAtom)` plus the component body in a `<Suspense>` with a reasonable fallback (e.g. the existing "Loading..." string).
## Suggested approach
The simplest fix: restore `<Suspense fallback="Loading...">` around `{content}` in `ExamplePage.tsx` while keeping the inner `<Suspense>` around `CodeEditor` for the lazy-import fallback. The two Suspense boundaries serve different purposes — the outer one catches `sourceAtom` resolution, the inner one catches the dynamic `import()` for Monaco.
## Open questions for the human
- Does the Astro React integration or root layout already provide a Suspense boundary above `ExamplePage`? If so the outer boundary is already present and this is a false alarm — worth verifying before making changes.DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the "Review fixes" commit addresses the prior review's inline feedback by passing resolved exampleSource directly instead of re-fetching via store.get().
- Pass resolved
exampleSourcetoopenInStackBlitz—ExampleViewnow destructuressourceAtomfrom props, callsuseAtomValue(sourceAtom), and passes the resolvedExampleSourcedirectly, removing theuseStore/store.get()indirection. - Extract
pnpmWorkspace.tsfromopenInStackBlitz.ts— YAML parsing and validation moved to a separatecomptime-based module, keeping theyamlparser out of the browser bundle and cleaning upopenInStackBlitz's imports.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
23fc10b to
2d64fc8
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the "Review fixes" commit replaces the store.get() re-fetch pattern in openInStackBlitz with direct value passing of the already-resolved exampleSource.
- Pass resolved
exampleSourcetoopenInStackBlitz—ExampleViewdestructuresexampleSourcefromuseAtomValue(sourceAtom)and passes it directly, removing theuseStore/store.get()indirection. - Remove unused imports —
useStoreandStoreimports dropped fromExampleView.tsxandopenInStackBlitz.ts.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
2d64fc8 to
cbfb2ad
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the "Review fixes" commit consolidates the lazy-loading architecture by converting SANDBOX_MODULES to an async sandboxModulesAtom, threading resolved sandbox modules through CodeEditor, the translator, and tgslExecutor, and extracting lazy globs into dedicated modules.
- Convert
SANDBOX_MODULEStosandboxModulesAtom— sandbox module type definitions now resolve asynchronously via a Jotai atom, keeping?rawglob evaluation out of the initial module graph. - Thread sandbox modules through consumers —
CodeEditor,editorConfig,tgslExecutor, andtranslatorStorereceive resolvedRecord<string, SandboxModuleDefinition>instead of reading a static export. - Extract
importers.tsandpathToExampleKey.ts— lazyimport.meta.globdeclarations and key-mapping logic are separated fromexampleContent.tsfor clarity. - Build-time
comptimeprecomputation —usedApis.tsandpnpmWorkspace.tsevaluate import globs and YAML parsing at build time, shipping only the computed data to the browser. - Lazy-load
CodeEditorviaReact.lazy— Monaco editor is dynamically imported with a<Suspense>fallback inExampleView. - Add
comptime@0.1.0with a Vite patch — the patch adds?rawquery support, module-runner recreation, and dev-server-awareness to comptime's Vite plugin.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

Changes:
typegpuand all packages that the editor needs for proper type inference.comptimepackage (I had to patch it up a bit to work with Astro).4G Throttling:
Screen.Recording.2026-06-16.at.16.01.42.mov
Screen.Recording.2026-06-16.at.15.58.17.mov