<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: paulbrowne</title>
    <description>The latest articles on DEV Community by paulbrowne (@paulbrowne).</description>
    <link>https://dev.to/paulbrowne</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F139232%2F05f28146-5443-4050-8bba-53fd92e49c4e.jpeg</url>
      <title>DEV Community: paulbrowne</title>
      <link>https://dev.to/paulbrowne</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paulbrowne"/>
    <language>en</language>
    <item>
      <title>JavaScript in, HTML out: building a simpler static site generator</title>
      <dc:creator>paulbrowne</dc:creator>
      <pubDate>Fri, 14 Aug 2026 10:20:00 +0000</pubDate>
      <link>https://dev.to/paulbrowne/javascript-in-html-out-building-a-simpler-static-site-generator-5ln</link>
      <guid>https://dev.to/paulbrowne/javascript-in-html-out-building-a-simpler-static-site-generator-5ln</guid>
      <description>&lt;p&gt;I build a lot of websites, and at some point I started wondering: why does building a mostly-static website require so much stuff?&lt;/p&gt;

&lt;p&gt;A framework, a bundler, a router, a rendering strategy, hydration, server components, client components, adapters, deployment configuration… all to eventually just send some HTML to a browser.&lt;/p&gt;

&lt;p&gt;So I built ⁠&lt;a href="https://sitelo.dev" rel="noopener noreferrer"&gt;Sitelo&lt;/a&gt; a small, zero-config framework for building fast websites with JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; sitelo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript in. HTML out.
&lt;/h3&gt;

&lt;p&gt;The basic idea behind Sitelo is deliberately boring. Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
  index.ht.js
  about.ht.js
  blog/
    [slug].ht.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The &lt;code&gt;.ht.js&lt;/code&gt; marks a file as a page — Sitelo turns each one into a route.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And Sitelo turns that into a website.&lt;/p&gt;

&lt;p&gt;Your pages (files named &lt;code&gt;*.ht.js&lt;/code&gt; or &lt;code&gt;.html.js&lt;/code&gt;) are JavaScript functions that return HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ht.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
      &amp;lt;title&amp;gt;Home Page&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;h1&amp;gt;Hello world&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
  &amp;lt;/html&amp;gt;
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sitelo build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you get static HTML in &lt;code&gt;dist/&lt;/code&gt; There’s no application runtime required to serve it, no hydration, and no JavaScript shipped to the browser unless you actually ask for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero JavaScript should be the default
&lt;/h3&gt;

&lt;p&gt;A lot of modern frameworks start with a JavaScript application and then give you increasingly sophisticated ways to reduce how much JavaScript reaches the browser.&lt;/p&gt;

&lt;p&gt;Sitelo starts from the opposite direction: &lt;strong&gt;there is no browser JavaScript by default.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your page doesn’t need JavaScript, Sitelo doesn’t send any. If it does, link a script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
      &amp;lt;title&amp;gt;Hello&amp;lt;/title&amp;gt;
      &amp;lt;script src="/js/some-frontend-script.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/head&amp;gt;
    ...
  &amp;lt;/html&amp;gt;
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sitelo sees it and lets Vite bundle it, while the rest of your page-generation code stays on the build side.&lt;/p&gt;

&lt;p&gt;This gives you a very simple mental model:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generate HTML first. Add JavaScript only where the browser actually needs JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  But I still want modern framework features
&lt;/h4&gt;

&lt;p&gt;So do I. Simple shouldn’t mean primitive.&lt;/p&gt;

&lt;p&gt;Sitelo has file-based routing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/about.ht.js          → /about
src/blog/[slug].ht.js    → /blog/:slug
src/docs/[...path].ht.js → /docs/*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has build-time data loading and dynamic routes, supports JavaScript and TypeScript, and if you like JSX, you can use .jsx and .tsx too.&lt;/p&gt;

&lt;p&gt;Sitelo uses Vite internally, so CSS, TypeScript and browser JavaScript still get the modern tooling you’d expect — you just don’t need to configure Vite yourself.&lt;/p&gt;

&lt;h4&gt;
  
  
  Static doesn’t mean everything has to be static
&lt;/h4&gt;

&lt;p&gt;One of my favourite parts of Sitelo is &lt;strong&gt;server islands&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Most of a page can remain static:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────┐
│                             │
│       Static HTML           │
│                             │
│     ┌───────────────┐       │
│     │ Server island │       │
│     └───────────────┘       │
│                             │
│       Static HTML           │
│                             │
└─────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Individual regions can then be rendered on the server at request time.&lt;/p&gt;

&lt;p&gt;That means you don’t have to turn your entire site into a server-rendered application just because one part of one page needs dynamic content. The site stays static where it can be, with small dynamic pieces where they’re actually needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Batteries included, without becoming a kitchen sink
&lt;/h4&gt;

&lt;p&gt;There are a bunch of small things that every real website eventually needs, and Sitelo handles those too.&lt;/p&gt;

&lt;p&gt;You can generate things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;404.html&lt;/li&gt;
&lt;li&gt;sitemap.xml&lt;/li&gt;
&lt;li&gt;RSS feeds&lt;/li&gt;
&lt;li&gt;static search with Pagefind&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also deployment configurations for Netlify, Vercel, Cloudflare Pages and AWS Amplify.&lt;/p&gt;

&lt;p&gt;The dev server includes a small toolbar showing things like the current route, source file, parameters and island count.&lt;/p&gt;

&lt;p&gt;The goal isn’t to add every possible feature. It’s to remove the repetitive work between:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I want to build a website”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Here’s the website.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  It’s also designed for coding with AI
&lt;/h4&gt;

&lt;p&gt;There’s another reason I wanted Sitelo to stay small and predictable: AI coding agents work particularly well when the rules of a project are simple.&lt;/p&gt;

&lt;p&gt;They’re considerably less useful when they have to reason about seven layers of framework abstractions before changing an &amp;lt;h1&amp;gt;.&lt;/p&gt;

&lt;p&gt;Sitelo can generate llms.txt and project rules that explain the framework to coding agents. More importantly, the framework itself has a small mental model:&lt;/p&gt;

&lt;h5&gt;
  
  
  Files are routes. Functions generate HTML. Browser code is explicit.
&lt;/h5&gt;

&lt;p&gt;That makes the project easier to reason about, whether the code is being written by a human or an AI agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not Astro?
&lt;/h3&gt;

&lt;p&gt;Astro, Next.js, Eleventy, Hugo and the many other tools in this space are great at what they do. Sitelo isn’t an attempt to prove that they’re bad or replace them.&lt;/p&gt;

&lt;p&gt;It’s an attempt to explore a smaller point on the framework spectrum.&lt;/p&gt;

&lt;p&gt;I wanted something where I could:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;write JavaScript → generate HTML → deploy it anywhere.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the same time, I still wanted file-based routing, dynamic routes, data loading, Vite, TypeScript, JSX, search and the occasional bit of server rendering — without starting every project by making a bunch of architectural decisions.&lt;/p&gt;

&lt;p&gt;That’s the space Sitelo is trying to occupy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who is it for?
&lt;/h3&gt;

&lt;p&gt;I think Sitelo makes the most sense for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;documentation&lt;/li&gt;
&lt;li&gt;blogs&lt;/li&gt;
&lt;li&gt;marketing sites&lt;/li&gt;
&lt;li&gt;portfolios&lt;/li&gt;
&lt;li&gt;company websites&lt;/li&gt;
&lt;li&gt;content-heavy sites&lt;/li&gt;
&lt;li&gt;sites backed by a CMS or REST API&lt;/li&gt;
&lt;li&gt;developers who like JavaScript but don’t necessarily want a JavaScript &lt;em&gt;application&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re building something that behaves primarily like an application, use an application framework.&lt;/p&gt;

&lt;p&gt;But if you’re building a &lt;strong&gt;website&lt;/strong&gt;, maybe start with HTML.&lt;/p&gt;

&lt;p&gt;Sitelo is open source and MIT licensed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sitelo.dev/docs" rel="noopener noreferrer"&gt;sitelo docs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/sitelo" rel="noopener noreferrer"&gt;npm sitelo&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/Paul-Browne/sitelo" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’d love to see what people build with it — and if you find something that makes Sitelo more complicated than it needs to be, open an issue.&lt;/p&gt;

&lt;p&gt;The whole point is to keep it boring, in the best possible way.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>static</category>
    </item>
    <item>
      <title>inline imports FTW!</title>
      <dc:creator>paulbrowne</dc:creator>
      <pubDate>Thu, 06 Mar 2025 16:00:00 +0000</pubDate>
      <link>https://dev.to/paulbrowne/inline-imports-ftw-1o3e</link>
      <guid>https://dev.to/paulbrowne/inline-imports-ftw-1o3e</guid>
      <description>&lt;h2&gt;
  
  
  event-import-then-function
&lt;/h2&gt;

&lt;h5&gt;
  
  
  html
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;onchange=&lt;/span&gt;&lt;span class="s"&gt;"import('/js/form.js').then(M =&amp;gt; M.default(this))"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;onchange=&lt;/span&gt;&lt;span class="s"&gt;"import('/js/form.js').then(M =&amp;gt; M.reverse(this))"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;onchange=&lt;/span&gt;&lt;span class="s"&gt;"import('/js/form.js').then(M =&amp;gt; M.upperCase(this))"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
  note: the M is just short for Module
&lt;/h6&gt;

&lt;h5&gt;
  
  
  form.js
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form.js loaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reverse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upperCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example &lt;code&gt;form.js&lt;/code&gt; will be &lt;em&gt;loaded&lt;/em&gt; only once, no matter how many times the onchange events are triggered. So you'll only ever see one "form.js loaded" in the console.&lt;/p&gt;

&lt;p&gt;Since the loading of form.js is deferred until &lt;em&gt;after&lt;/em&gt; the onchange event has been triggered, there will be a slight delay whilst the browser fetches it.&lt;/p&gt;

&lt;p&gt;If you want to avoid this delay you can always prefetch the resource by adding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/form.js"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to the html. This will of course fetch the file regardless if the user ever needs it.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>import</category>
      <category>vanillajs</category>
    </item>
    <item>
      <title>Lazyload Scripts like Images</title>
      <dc:creator>paulbrowne</dc:creator>
      <pubDate>Sat, 05 Oct 2024 10:57:40 +0000</pubDate>
      <link>https://dev.to/paulbrowne/lazyload-scripts-like-images-30e0</link>
      <guid>https://dev.to/paulbrowne/lazyload-scripts-like-images-30e0</guid>
      <description>&lt;p&gt;One of the best improvements to html over the recent years was the &lt;code&gt;loading="lazy"&lt;/code&gt; attribute that you can add to images (also iframes) that will tell the browsers to not load the image until it is in the viewport.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/images/your-image.png"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Very simple, very useful. But wouldn't it be great if you could do this for scripts as well. So that you could lazily load your components, only if/when they are actually needed...&lt;/p&gt;

&lt;p&gt;Well, one other feature the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element has, is the ability to run a script &lt;em&gt;after&lt;/em&gt; the image loads (or doesn't load) with the &lt;code&gt;onload&lt;/code&gt; and &lt;code&gt;onerror&lt;/code&gt; attributes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/images/your-image.png"&lt;/span&gt; 
        &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
        &lt;span class="na"&gt;onload=&lt;/span&gt;&lt;span class="s"&gt;"() =&amp;gt; console.log('image loaded')"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;onload&lt;/code&gt; "callback" will only be fired when that image is loaded, and if the image is been lazily loaded, then it will fire only when the image is in the viewport. Et voilà! A Lazily loaded script.&lt;/p&gt;

&lt;p&gt;Unfortunately, like this, it isn't much use. Firstly you'll have a unwanted image on your page, and secondly, you'll need to inline the javascript you want to run, which kinda defeats the purpose of lazy loading. So, lets make some changes to improve on this.&lt;/p&gt;

&lt;p&gt;The image itself can be anything, or, more importantly, nothing. As I mentioned earlier there is the &lt;code&gt;onerror&lt;/code&gt; callback, which - as the name would suggest - will fire when the image &lt;em&gt;doesn't&lt;/em&gt; load. &lt;/p&gt;

&lt;p&gt;This doesn't mean you need to point the &lt;code&gt;src&lt;/code&gt; to a non-existent image, that would result in a console full of red 404 errors about missing images, and nobody wants that. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;onerror&lt;/code&gt; callback also fires if the &lt;code&gt;src&lt;/code&gt; image is not actually an image, and the easiest way to do that is to "badly encode" an image using the &lt;code&gt;data:&lt;/code&gt; format. This also has the benefit of not filling the console with warnings of missing images 👍&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"data:,"&lt;/span&gt; 
        &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
        &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"() =&amp;gt; console.log('image not loaded')"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will still result in the page having the "broken image" thumbnail, but we'll get to that.&lt;/p&gt;

&lt;p&gt;Ok, But we still need to inline the javascript we want to run, so how do we fix that?&lt;/p&gt;

&lt;p&gt;Well, now that &lt;a href="https://caniuse.com/?search=es%20module" rel="noopener noreferrer"&gt;ES module support&lt;/a&gt; is almost universal, we can use the very powerful &lt;strong&gt;event-import-then-default&lt;/strong&gt; javascript loading technique to load a script after an event has fired, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"data:,"&lt;/span&gt; 
        &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
        &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"import('/js/some-component.js').then(_ =&amp;gt; _.default(this))"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: This also works for &lt;code&gt;onclick&lt;/code&gt;, &lt;code&gt;onchange&lt;/code&gt;, etc. events&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Note: The underscores are just shorthand way to access the Module, you could also write &lt;code&gt;.then(Module =&amp;gt; Module.default(this))&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ok, so what is going on here!?&lt;/p&gt;

&lt;p&gt;First lets take a look at what some-component might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// some-component.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;div class="whatever"&amp;gt;
            &amp;lt;p&amp;gt;Hello world!&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, you might have noticed that in the &lt;code&gt;onerror&lt;/code&gt; callback, I passed &lt;code&gt;this&lt;/code&gt; as an argument to the default export. The reason I did &lt;em&gt;this&lt;/em&gt; (excuse the pun 😁) was to give the script the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; that called it, since in &lt;em&gt;this&lt;/em&gt; (I did it again 🤦) context &lt;code&gt;this = &amp;lt;img&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now you can simply &lt;code&gt;element.outerHTML&lt;/code&gt; to replace the broken image with your html markup and there you have it, lazyloaded scripts! 😱&lt;/p&gt;

&lt;h4&gt;
  
  
  Caching, and Passing Arguments
&lt;/h4&gt;

&lt;p&gt;If, you are using this technique more than once on a page, then you'll need to pass a "cache-busting" index, or random number to the &lt;code&gt;data:,&lt;/code&gt; eg, something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"data:,abc123"&lt;/span&gt; 
        &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
        &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"import('/js/some-component.js').then(_ =&amp;gt; _.default(this))"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"data:,xyz789"&lt;/span&gt; 
        &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
        &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"import('/js/some-other-component.js').then(_ =&amp;gt; _.default(this))"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The string after the ":," can be anything, just so long as they are different.&lt;/p&gt;

&lt;p&gt;A very simple way to pass params to the function would be to use the &lt;code&gt;data-something&lt;/code&gt; attribute in the html like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"data:,"&lt;/span&gt; 
        &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
        &lt;span class="na"&gt;data-message=&lt;/span&gt;&lt;span class="s"&gt;"hello world"&lt;/span&gt;
        &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"import('/js/some-component.js').then(_ =&amp;gt; _.default(this))"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we are passing the &lt;code&gt;this&lt;/code&gt; to the function, you can access the &lt;code&gt;data&lt;/code&gt; attributes like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;div class="whatever"&amp;gt;
            &amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please let me know what you think in the comments! ❤️&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>lazyload</category>
    </item>
  </channel>
</rss>
