<h1>
	Lazy loading JS by visibility
</h1>

<p>
	Here’s a neat trick.
</p>

<p>
	<a href="https://twitter.com/FredKSchott">Fred K. Schott</a>
	gives an impressive presentation of
	<a href="https://www.youtube.com/watch?v=mgkwZqVkrwo">Astro</a>,
	a framework-agnostic architecture for building web sites and web apps.
</p>

<iframe
	title="YouTube video player"
	width="560"
	height="315"
	src="https://www.youtube-nocookie.com/embed/mgkwZqVkrwo?rel=0&controls=0&fs=0&start=2356&end=2373"
	allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
	allowfullscreen
></iframe>

<blockquote>
	<p>
		We add a script to the end of your page.
		It’s inline. It essentially runs almost immediately,
		and that is what is going to create that
		<a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">Intersection Observer</a>.
		It’s going to do a dynamic import of the things that you need,
		and it’s going to call React render on that component.
	</p>
</blockquote>

<p>
	During the presentation, Fred demonstrates dynamically loading JS using
	<a href="https://jasonformat.com/islands-architecture/">"Islands Architecture"</a>,
	a strategy
	where small HTML placeholders are progressively upgraded with dynamic or interactive content as-needed.
</p>

<p>
	The demonstration involves
	waiting on the viewport visibility of a <code>div</code> placeholder before loading some JS.
</p>

<p>
	The entire setup contributes less than 200 bytes to the page when gzipped.
</p>

<pre><code>&lt;div data-astro-id="3459833264469372"&gt;&lt;/div&gt;
&lt;script type="module"&gt;

<b -com>/* scaffolding put before the code */</b>
<mark>((o=new IntersectionObserver((([{isIntersecting,target}])=>{isIntersecting&&(o.disconnect(),</mark>

<b -com>/* code run when the section is visible */</b>
<b -var>Promise</b>.<b -ent>all</b>([
	<b -key>import</b>(<b -str>'https://cdn.skypack.dev/react'</b>),
	<b -key>import</b>(<b -str>'https://cdn.skypack.dev/react-dom'</b>),
]).<b -ent>then</b>( ([
	{ <b -con>default</b>: <b -var>React</b> },
	{ <b -con>default</b>: <b -var>ReactDOM</b> },
]) => <b -var>ReactDOM</b>.<b -ent>render</b>(
	<b -var>React</b>.<b -ent>createElement</b>(<b -str>'strong'</b>, {},
		<b -str>'This was rendered with React!'</b>,
	),
	<b -ent>target</b>,
) )

<b -com>/* scaffolding put after the code */</b>
<mark>)})))=>{o.observe(document.querySelector('[data-astro-id="3459833264469372"]'))})()</mark>
&lt;/script&gt;</code></pre>

<p>
	Neat trick, right?
</p>

<p>
	It can be used once, or multiple times in different places.
	JS can be loaded by <a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/observe">element visibility</a>,
	a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia">media query</a>,
	a <a href="https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe">container query</a>,
	any <a href="https://developer.mozilla.org/en-US/docs/Web/Events">event</a>,
	any <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver/observe">asset</a>,
	any <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback">idle period</a>,
	or any mix of these,
	and any other condition that’s helpful.
</p>

<p>
	Below is a live demonstration.
</p>
<p style="margin:25vh 0">👇</p>

<div data-astro-id="3459833264469372">&nbsp;</div>
<script type="module">
((
	o = new IntersectionObserver(
		([{ isIntersecting, target }]) => {
			if (isIntersecting) {
				o.disconnect()
				Promise.all([
					import('https://cdn.skypack.dev/react'),
					import('https://cdn.skypack.dev/react-dom')
				]).then(
					([{ default: React }, { default: ReactDOM }]) => ReactDOM.render(
						React.createElement('strong', {},
							'This was rendered with React!'
						),
						target
					)
				)
			}
		}
	),
	f = document.createDocumentFragment(),
) => o.observe(
	document.querySelector('[data-astro-id="3459833264469372"]')
))()
</script>

<p style="margin:25vh 0">👆</p>

<p>
	Above is a live demonstration.
</p>

<hr />

<p>
	That’s all.
</p>

<p>
	It just seems like a neat trick.
</p>
* {
	box-sizing: border-box;
}

body {
	align-items: center;
	display: flex;
	font: 125%/1.5 system-ui, -apple-system;
	flex-flow: column wrap;
	margin: 1em 1em 4em;
	overflow-wrap: anywhere;
	tab-size: 2;
	-webkit-text-size-adjust: none;
}

body > * {
	max-width: 34rem;
}

blockquote {
	box-shadow: inset .75em 0 0 #039be5;
	font: italic 125%/1.5 Roboto, serif;
	margin-inline-start: 0;
	padding-left: 2em;
}

@media (max-width: 640px) {
	blockquote {
		box-shadow: inset .5em 0 0 #039be5;
	}
}

pre, code {
	font: 85%/inherit SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
}

pre, p code {
	background: #f6f8fa;
	border-radius: 6px;
}

p code {
	padding: .25em .365em;
}

pre {
	display: flex;
	padding: 1em;
	overflow: scroll;
	max-width: 100%;
}

pre > code {
	display: block;
	min-width: 60em;
}

pre b {
	font-weight: normal;
}

iframe {
	border: 0;
	width: 100%;
}

strong {
	background-color: #84ff6f;
	border-radius: 1em;
	padding: 1em;
}

hr {
	background:
		url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 150 640 80'%3E%3Cpath d='M130 192c6-7 4-18 0-21-9-6-17 6-13 18 4 13 14 21 28 24 29 5 88-27 126-37 27-8 44 3 54 16-19-14-36-9-54-6-41 8-82 40-127 35-21-3-35-24-34-40l1-4c3-10 7-14 12-15 11-3 23 18 7 30z' /%3E%3Cpath d='M120 199c-8-7-16-18-24-23-79-53-120 18-79 39 6 3 19 5 28-8-11 9-24 6-29 1-14-12-9-29 7-36 26-10 49 0 70 15 14 9 18 13 30 18-7 0-3-8-10-13l7 7zM487 190c-6 8-4 19 1 22 9 6 18-6 14-18-5-13-15-22-30-24-31-6-94 27-134 37-29 7-47-4-57-16 20 13 38 9 57 5 43-7 87-39 134-34 22 3 38 24 37 40l-1 4c-3 10-8 14-13 15-12 3-24-18-8-31z' /%3E%3Cpath d='M497 179c9 8 18 19 26 24 83 52 126-18 83-39-7-3-20-5-30 8 12-9 25-6 31-1 14 11 9 28-8 36-27 10-52-1-73-15-15-9-19-13-32-18 7 0 4 8 10 12l-7-7z' /%3E%3C/svg%3E") 0
		/ 100% auto no-repeat
	;
	border: 0;
	height: 5em;
	margin-block-end: 3em;
	margin-block-start: 3em;
	width: 100%;
}

[-var] { color: #e36209; }
[-ent] { color: #6f42c1; }
[-key] { color: #d73a49; }
[-str] { color: #136203; }
[-con] { color: #005cc5; }
[-com] { color: #6a737d; }
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.