From f7f20a3872d11f0f4d99df7ecdfb0d4ce124ce82 Mon Sep 17 00:00:00 2001 From: Dylan Piercey Date: Sat, 13 Jun 2026 20:19:57 -0700 Subject: [PATCH] chore: update lazy load docs --- docs/reference/lazy-loading.md | 75 ++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/docs/reference/lazy-loading.md b/docs/reference/lazy-loading.md index 0880d408..86f0b7db 100644 --- a/docs/reference/lazy-loading.md +++ b/docs/reference/lazy-loading.md @@ -23,8 +23,13 @@ Attributes passed to a lazily loaded tag remain reactive while it loads. Once it The value of the `load` attribute is either [`"render"`](#render) or one or more triggers which determine when the tag's JavaScript is loaded. +> [!NOTE] +> The `load` value is read at build time, so it must be a static string and cannot reference reactive variables or other runtime values. A trigger controls only when a tag's JavaScript is fetched, independent of the rendering logic that decides whether the tag is shown. + Most triggers accept a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors) for an element to watch, and some accept additional options using a query string syntax. +The selector is matched with [`document.querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector), so any selector works, not only IDs: `visible#hero`, `visible.hero`, and `visible[data-hero]` each watch their matching element. Because the selector follows immediately after the trigger name, a bare type selector like `section` must be separated from it with a space, as in `visible section`. + > [!NOTE] > If a trigger's selector does not match any element on the page, the tag's JavaScript is loaded immediately (with a warning in development). @@ -47,9 +52,9 @@ The `render` trigger must be used alone and cannot be combined with [multiple tr Loads the tag's JavaScript once the element matching the selector becomes visible in the viewport (via an [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver)). ```marko -import Comments from "" with { load: "visible#comments" } +import Comments from "" with { load: "visible.comments" } - + ``` @@ -57,7 +62,7 @@ import Comments from "" with { load: "visible#comments" } The observer's [`rootMargin`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin) may be configured to begin loading before the element actually enters the viewport. ```marko -import Comments from "" with { load: "visible#comments?rootMargin=100px" } +import Comments from "" with { load: "visible.comments?rootMargin=100px" } ``` ### `idle` @@ -90,14 +95,17 @@ import MobileNav from "" with { load: "media(max-width: 768px)" } This pairs well with tags hidden by CSS breakpoints, so that (for example) desktop users never download mobile-only UI. +> [!TIP] +> To apply a media trigger everywhere a tag is used rather than repeating it at each import, wrap the tag in a [facade](#facade-tags). + ### Events A trigger beginning with `on` loads the tag's JavaScript the first time the matching event fires on the element matching the selector. ```marko -import EmojiPicker from "" with { load: "on-focus#message" } +import EmojiPicker from "" with { load: "on-focus[name=message]" } - + ``` @@ -114,28 +122,51 @@ import ChatWidget from "" with { load: "on-mouseover#chat | idle?ti ``` +## Facade Tags + +A `load` import applies only at the import site, so every consumer of a tag must opt in to lazy loading individually. A tag can instead be made _always_ lazy by wrapping it in a facade: a small tag that lazily imports the real implementation and forwards its input. + +Placing that implementation in a nested [`tags/` directory](./custom-tag.md#relative-custom-tags) keeps it private to the facade, so the rest of the application can only reach the lazy version. + +```text +tags/ + location-map/ + index.marko + tags/ + map-impl.marko +``` + +The facade is discovered as ``, while `` lives in its own nested `tags/` directory and is only resolvable from within the facade. + +```marko +/* tags/location-map/index.marko */ +import MapImpl from "" with { load: "render" } + + +``` + +`` can now be used anywhere in the application and its JavaScript is always split into a separate bundle, with no `load` attribute required at the call site. Any [trigger](#triggers) may be used in the facade, so a heavy below-the-fold widget might default to `visible` rather than `render`. + ## Placeholders & Errors In the browser, a lazily loaded tag behaves like [async content](./core-tag.md#await): while its JavaScript is loading, a [``](./core-tag.md#try) ancestor displays its [`@placeholder`](./core-tag.md#placeholder) content, and if loading fails the error is handled by the nearest [`@catch`](./core-tag.md#catch). ```marko -import Comments from "" with { load: "on-click#show-comments" } - - -Show Comments - - - - - - <@placeholder> - Loading comments... - - - <@catch|err|> - Failed to load comments: ${err.message} - - +import PriceChart from "" with { load: "visible.chart" } + + + + + + <@placeholder> + Loading chart... + + + <@catch|err|> + Failed to load chart: ${err.message} + + + ``` ## Bundler Support