Date
Author
georges-gomes

Optimizing Swyx's personal site

On January 31th 2023, 15 days ago, Swyx updated his personal website for 2023.

Following Swyx mantra: Pick Up What They Put Down, I decided to see if I can improve the performance of his website using jampack πŸ˜‹

Who is Swyx?

Shawn Wang (aka @swyx) is a Writer, Speaker and Developer Advocate.

He is best known for his essay about learning in public that includes β€œPick Up What They Put Down”.

You can learn more about him in his bio.

If you don’t follow him yet, you should πŸ‘

Swyx’s Personal site

https://swyx.io

Screenshot of swyx website in light mode

The 2022 version of the website was a rewrite in SvelteKit and TailwindCSS.

This 2023 millesime is an upgrade to SvelteKit 1.0 released in Mid-December 2022.

Let’s build our own copy

I’m gonna build my own copy of the Swyx’s site and host it on Netlify. This way I’m not impacted by any of what could be going on in production.

Checking performance

Original on https://swyx.io

SpeedIndex: 4.603ms
LCP: 5.108s
CLS: 0
TBT: 1.315s
76 requests
Download: 2.575MB

My copy on https://swyxdotio-original.netlify.app

SpeedIndex: 4.308s
LCP: 4.897s
CLS: 0
TBT: 1.263s
76 requests
Download: 2.575MB

Looks close enough.

Request waterfall

Requests waterfall from WebPageTest.org

Observations

  • LCP is bad at 4.8s. Should be below 2.5s for β€œGOOD” score.
  • CLS is at 0. Perfect.
  • TBT is at a woping 1.2s - It should be below 0.2s I think πŸ€”
  • The HMTL page is a massive 4MB uncompressed (1.3MB compressed). This looks way to big. Why?
  • It take a full 3 seconds for the assets (CSS and JS) to start downloading… Why?

Okay! Let’s go optimizing!

First surprise

There is no .html files on the output build πŸ˜‚. jampack is not going to do much without HTML files 🫠. jampack can minify the 60 images present in the output build but that’s about it.

There is no .html because SvelteKit is configured to use the Netlify adapter to render in functions. This is actually not a static site… πŸ€¦β€β™‚οΈ

I will not give up so easily!

Let’s turn it into a static site!

Apparently, I can swap @sveltejs/adapter-netlify with @sveltejs/adapter-static and have a static site on the output.

20 minutes later

Not an easy task - some things are specifically dynamic routes.

After a long battle with SvelteKit, I focused by efforts on rendering just the home page.

Performance result

SpeedIndex: 1.224s
LCP: 2.508s
CLS: 0
TBT: 1.123s
76 requests
Download: 2.575MB
Request Waterfall after turning prerendering

Observations

  • LCP is very close to 2.5s.
  • CLS is at 0. Perfect.
  • TBT is unchanged.

I guess this had to be expected. Without browser cache (as used here in benchmarks), the original site had to generate the HTML page by functions. Kickstarting the function and generating a 4MB HTML is not a blazing fast task.

The new static version of the page shows tremendous performance benefits.

Let’s run jampack out-of-the-box

Install jampack v0.9.1

npm i -D @divriots/jampack

Add to build

"build": "vite build && jampack ./build",

jampack output

 PASS 1 - Optimizing
β–Ά index.html
  βœ” <img> [1/1]
Done: 90.38ms

 PASS 2 - Compressing the rest
βœ” 69 files | 15.30 MB β†’ 14.66 MB | -647.94 KB
Done: 1.123s

 Summary
╔══════════════╀════════════╀═══════════╀════════════╀════════════╗
β•‘ Action       β”‚ Compressed β”‚  Original β”‚ Compressed β”‚       Gain β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ jpg->jpg     β”‚      1 / 1 β”‚  24.98 KB β”‚   21.69 KB β”‚   -3.30 KB β•‘
β•‘ .js          β”‚    29 / 32 β”‚ 228.31 KB β”‚  226.59 KB β”‚   -1.72 KB β•‘
β•‘ .css         β”‚      3 / 3 β”‚  45.04 KB β”‚   42.96 KB β”‚   -2.08 KB β•‘
β•‘ .png         β”‚     8 / 13 β”‚   1.85 MB β”‚    1.84 MB β”‚  -10.61 KB β•‘
β•‘ .jpeg        β”‚      5 / 5 β”‚ 587.34 KB β”‚  444.34 KB β”‚ -143.00 KB β•‘
β•‘ .jpg         β”‚      5 / 5 β”‚   3.06 MB β”‚    2.58 MB β”‚ -486.99 KB β•‘
β•‘ .html        β”‚      1 / 1 β”‚   3.93 MB β”‚    3.93 MB β”‚  -242.00 B β•‘
β•‘ .xml         β”‚      0 / 1 β”‚  246.00 B β”‚   246.00 B β”‚            β•‘
β•‘ .ico         β”‚      0 / 1 β”‚  14.73 KB β”‚   14.73 KB β”‚            β•‘
β•‘ .txt         β”‚      0 / 1 β”‚  191.00 B β”‚   191.00 B β”‚            β•‘
β•‘ .webmanifest β”‚      0 / 1 β”‚  426.00 B β”‚   426.00 B β”‚            β•‘
β•‘ .json        β”‚      0 / 2 β”‚   3.77 MB β”‚    3.77 MB β”‚            β•‘
β•‘ .md          β”‚      0 / 1 β”‚   89.00 B β”‚    89.00 B β”‚            β•‘
β•‘ .gif         β”‚      0 / 1 β”‚   1.81 MB β”‚    1.81 MB β”‚            β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Total        β”‚    52 / 69 β”‚  15.30 MB β”‚   14.66 MB β”‚ -647.94 KB β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•

Only one index.html processed and a few assets minified.

Not much to do on this page. The avatar image of Shawn has been transformed to progressive JPEG to optimise CLS - shaving 3.3KB in the process. The rest is in situe compression and minification.

Performance result

SpeedIndex: 1.321s
LCP: 2.449s
CLS: 0
TBT: 1.150s
77 requests
Download: 2.592MB
Request Waterfall after turning prerendering

Observations

  • Not a tremendous improvement over pure static.
  • But we manage to get LCP below 2.5s :)
  • Mobile phone users with low bandwidth will benefit most from the progressive JPEG.
  • There are way too many youtube.com requests from 4 embed after the fold. We should make them load lazy.

Let’s add lazy <iframe> to jampack

The Youtube embeds are <iframe>s. They are located way below the fold. There is no point in loading them eagerly. Let’s free some resources by loading them lazy.

Jampack release v0.9.3 now adds loading="lazy" to iframes below the fold.

Performance results

Request Waterfall after iframe lazy

🀯🀯🀯🀯 Absolutely no impact at all.

<iframe>s are still loaded eagerly.

But Why?!

Because SvelteKit re-hydrate the whole page that has been pre-rendered and replaces ALL content.

<iframe> are no longer lazy when Javascript is loaded. Everything that jampack setup to load in lazy is reverted immediately to load eagerly.

But I don’t need Javascript for this page!

Let’s try to turn hydration off for this page.

New Performance results

SpeedIndex: 1.321s
LCP: 0.677s
CLS: 0
TBT: 1.204s
56 requests
Download: 1.250MB
Request Waterfall after no hydration

WOW now we are talking!

Observations

  • By removing Javascript, the light/dark mode switch is broken.
  • Otherwise the entire page is as the original.
  • The browser keep loading YouTube data because it has nothing else to do!
  • The HTML page is reduced from 4MB uncompressed to 20KB uncompressed (6KB compressed).
  • Interesting to see how SvelteKit hydration is taxing the HTML page.

Close thoughts

That’s enough for today πŸ˜…

Try yourself:

Reminder: Only the homepage works properly

I’m sad I couldn’t work with more pages. There are very good performance improvements in the blog post pages. Specially with images. Indeed, the blog uses GitHub issues as a CMS. It’s very clever but images are very large and slow. jampack would have done miracles here.

I will probably come back to it later when I’m able to build more static pages with SvelteKit.

Jampacked website code branch: https://github.com/divriots/swyxdotio/tree/jampack

Released

  • jampack 0.9.2
  • jampack 0.9.3