Dienstag, 26. August 2025 • 2 minuten zu lesen
A few weeks ago I built a little hack for image loading on Micro.blog. The goal was to serve multiple image sizes to allow browsers to decide which version to load based on screen size, pixel density, and, if supported, even network conditions. The problem was, that image loading somehow always felt a bit janky in my tests.
Inspecting the page didn’t reveal anything obvious, so I asked ChatGPT for help. The real issue turned out to be caching, or rather, the lack of it. Because I had encoded the target URL with urlquery
, the image paths varied slightly and the browser never reused them from cache. While we were at it, I also got a few suggestions for further improvements. I dropped the 1200px variant since my content column is never wider than 640px, reworked the hook so image URLs are now consistent, and added a few extras like cleaner srcset
/sizes
and LCP prioritization for the first image.
The pages now feel noticeably smoother, images appear instantly, and they still only use as much bandwidth as needed. Sometimes it really pays off to take a closer look at your own site and sweat the details. Here is the updated render-image.html
.
{{ $dest := .Destination | safeURL }}
{{ $alt := .Text | default "" }}
{{ $abs := cond (hasPrefix $dest "http") $dest ($dest | absURL) }}
{{ $isMB := or (hasPrefix $abs "https://micro.blog/photos/") (hasPrefix $abs "http://micro.blog/photos/") }}
{{ $src300 := "" }}
{{ $src600 := "" }}
{{ $src900 := "" }}
{{ if $isMB }}
{{ $src300 = (replaceRE `/photos/[^/]+/` "/photos/300x/" $abs) }}
{{ $src600 = (replaceRE `/photos/[^/]+/` "/photos/600x/" $abs) }}
{{ $src900 = (replaceRE `/photos/[^/]+/` "/photos/900x/" $abs) }}
{{ else }}
{{ $encoded := $abs | urlquery }}
{{ $src300 = printf "https://micro.blog/photos/300x/%s" $encoded }}
{{ $src600 = printf "https://micro.blog/photos/600x/%s" $encoded }}
{{ $src900 = printf "https://micro.blog/photos/900x/%s" $encoded }}
{{ end }}
{{ $isFirst := not (.Page.Scratch.Get "lcp_done") }}
{{ if $isFirst }}{{ .Page.Scratch.Set "lcp_done" true }}{{ end }}
<img {{ if $isFirst }}loading="eager" fetchpriority="high"{{ else }}loading="eager" fetchpriority="auto"{{ end }}
decoding="async"
src="{{ $src600 }}"
srcset="{{ $src300 }} 300w, {{ $src600 }} 600w, {{ $src900 }} 900w"
sizes="(max-width: 480px) 100vw,
(max-width: 768px) 80vw,
640px"
alt="{{ $alt }}">
Sonntag, 17. August 2025 • 1 minuten zu lesen
Ich habe meinem Blog übers Wochenende einen neuen Anstrich verpasst, nachdem ich mit dem bisherigen Theme zu viele Kompromisse eingehen musste und viel zu oft tief ins CSS gegriffen habe. Jetzt strahlt hier das mnml-Theme von Jim Mitchell, mit einem klaren, reduzierten Design und vielen direkt eingebauten Features. Ich musste tatsächlich nur noch wenige Kleinigkeiten anpassen.
Neben dem deutlich aufgeräumteren Look, gibt es auch einen überarbeiteten Dark-Mode, eine aktualisierte Fotogalerie sowie ein neues Favicon. Darüber hinaus habe ich die Über-Seite entschlackt und die 404-Seite überarbeitet, für die Sora mir auf meinen Wunsch ein kotzendes Einhorn gezeichnet hat.
Unter der Haube verrichtet natürlich weiterhin das Hugo-basierte Micro.blog seine Dienste.

Samstag, 2. August 2025 • 2 minuten zu lesen
I just updated my Micro.blog theme to deliver images in a more efficient and responsive way. This means faster page loads, lower data usage, and no more compromises on image quality.
The trick? A clever use of Hugo’s Markdown render hooks, which Micro.blog supports via its theme templates. Shoutout to this helpful thread on the Micro.blog Help Forum, which showed me how to combine srcset
, sizes
, and Hugo templating to give the browser more flexibility.
Instead of serving a single fixed-size image, I now provide multiple image sizes. This allows the browser to decide which version to load based on screen size, pixel density, and, if supported, even network conditions.
Here is how it works
Create a file at layouts/_default/_markup/render-image.html
in your Micro.blog theme with the following code:
{{ $path := .Destination }}
<img
src="https://micro.blog/photos/600x/{{ $path }}"
srcset="
https://micro.blog/photos/300x/{{ $path }} 300w,
https://micro.blog/photos/600x/{{ $path }} 600w,
https://micro.blog/photos/900x/{{ $path }} 900w,
https://micro.blog/photos/1200x/{{ $path }} 1200w"
...
/>
What it does:
- Mobile devices get smaller images, desktops get higher-res ones
- High-density screens such as Retina can load sharper versions if needed
- Browsers may choose smaller images on slow connections or in data-saver mode
Previously, I used a bunch of iOS Shortcuts and external tools to downscale images before uploading them, which worked, but was tedious. Now, that extra workflow is no longer necessary. I can upload the full image and let Hugo and the browser do the rest.
The caveat is that most of my older posts won’t benefit from this. To make them compatible, I would have to swap in the original high-resolution files and replace all HTML-based image embeds with Markdown. That is unlikely to happen, unless I’m really bored someday. Nevertheless, it’s a small change with a big impact going forward, and I love it.
Update · 25/08/2025
I noticed a caching issue with my first implementation: images were constantly reloading instead of being served from the browser cache. The culprit was the unnecessary use of urlquery
, which produced slightly different URLs on each render. I removed it, and now caching works as expected, images load instantly once they’ve been fetched the first time.