SSWConsulting / SSWConsulting/SSW.Website

💸 Perf - AI for Business Leaders event page - LCP 9.3s, TBT 1,250ms on mobile

Open
#4,894 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Type: Refactor
Dominant language
HTML
Stars
14
Forks
10
Avg merge
13h 51m
Merged PRs (30d)
38

Description

Pain

Mobile Lighthouse 13.4 on https://www.ssw.com.au/events/ai-for-business-leaders (Moto G Power, Slow 4G, 23 Jul 2026) is poor:

Metric Value Score contribution
First Contentful Paint 2.8 s +6
Largest Contentful Paint 9.3 s +0
Total Blocking Time 1,250 ms +6
Cumulative Layout Shift 0.133 +20
Speed Index 4.1 s +8

Accessibility is 81. Total page weight 2,867 KiB. Lighthouse also flags: unused JS 1,064 KiB, JS execution 2.9 s, main-thread work 3.9 s, 17 long tasks, forced reflow, inefficient cache lifetimes (246 KiB), legacy JavaScript (41 KiB).

TL;DR of the root causes

The page itself is simple — 39 blocks of only 6 component types (20 × spacer, 8 × imageTextBlock, 4 × cardCarousel, 2 × accordionBlock, 1 × logoCarousel, 1 × breadcrumbs). The cost is almost entirely framework and shared-component overhead, not content:

  1. 1.1 MB of TinaCMS's MDX parser ships in the initial bundle of every page. From .next/app-build-manifest.json, this route's entry is 1.70 MB of JS, and dd3583a7-*.js alone is 1,115 KB of it. 65 components import TinaMarkdown from tinacms/dist/rich-text, whose first line is import { sanitizeUrl } from "@tinacms/mdx" — a 1.97 MB package (remark/mdast/slate/acorn) pulled in for one URL sanitiser. This accounts for essentially all of Lighthouse's "unused JavaScript — 1,064 KiB".
  2. The LCP image is lazy-loaded. blocks-renderer.tsx grants priority to block 0, which is breadcrumbs (no image). The hero is block 2 and gets loading="lazy" with no fetchpriority. The file is only 78 KB — 9.3 s is entirely "requested late, behind 1.7 MB of JS".
  3. Every one of the 39 blocks pays for V2ComponentWrapper — a getBoundingClientRect() in a mount effect, its own resize listener, a framer-motion useInView observer, and an inline background-image: url("") / url(undefined). 20 of those 39 blocks are empty 45 px spacers.
  4. Site-wide passengers: AOS initialises on every page (unused on this one), Application Insights constructs during the critical window, minimumCacheTTL: 60 makes optimised images near-uncacheable, and 8 Eventbrite CTAs each mount an always-live react-responsive-modal.
  5. CLS 0.133 is the header — it SSRs with the flag and Contact button, then useMobileHeaderAppearance strips them after hydration. header-appearance.tsx documents this and its fix.

Order of work

Items are sequenced by impact-per-risk, and grouped so PRs touching the same files land together.

Wave # Issue Why here
0 — Baseline 1 #4895 — Verify the react-icons fix (#4887) is deployed Changes the measurement baseline; do before anything else so later gains are attributable
1 — Dominant wins 2 #4896 — Keep the 1.1 MB TinaCMS MDX parser out of the client bundle Single biggest lever: ~65% of the route's JS, and most of TBT / JS-execution
3 #4897 — Give the LCP image priority Should take LCP from 9.3 s to low single digits; a content tick-box plus a 3-line renderer change
2 — Main-thread cleanup 4 #4898 — Stop V2ComponentWrapper forcing a reflow per block Fixes "forced reflow"; 39 instances on this page
5 #4899 — Make Spacer a plain div Same file family as #4 — land together to avoid rebase churn
6 #4900 — Early-return ButtonRow before its layout hooks Same cleanup pass
7 #4901 — Stop loading AOS on pages that don't use it Same cleanup pass
3 — Config + third parties 8 #4902 — Raise minimumCacheTTL One-line change, fully isolated — can be picked up at any point
9 #4903 — Defer Application Insights init Isolated
10 #4904 — Mount the Eventbrite Popup only when open Isolated
11 #4905 — Reduce the logo Marquee repeat count Isolated
4 — CLS + a11y 12 #4906 — Resolve mobile header appearance server-side Fixes CLS 0.133, but needs a middleware change — highest blast radius, so last
13 #4907 — Accessibility fixes (heading order, marquee clones, icon labelling) Independent of the perf work; addresses the 81 a11y score

Re-run Lighthouse after wave 1 and again after wave 2 — waves 3 and 4 should be measured against the improved baseline, not this one.

Acceptance criteria

  1. All 13 sub-issues are closed or explicitly deferred with a reason
  2. Mobile Lighthouse on /events/ai-for-business-leaders shows LCP under 2.5 s and TBT under 200 ms
  3. CLS is under 0.1
  4. The route's initial JS (per app-build-manifest.json) is under 700 KB uncompressed
  5. Accessibility score is 95 or above
  6. Before/after Lighthouse runs are attached to this issue

Notes

  • Findings are from a static read of the codebase plus the committed .next build (22 Jul 15:27), cross-checked against the Lighthouse run. The accessibility items in #4907 are code-inferred — run axe on the page to confirm the full list before starting.
  • Every fix below is shared infrastructure: this page is the worst case, but wins apply to all eventsv2, consultingv2 and v3 pages.

Related

  • #4889 — the equivalent homepage PageSpeed PBI, with sub-issues #4890–#4893. Several findings there apply site-wide.
  • #4892 overlaps with #4896 but blames a different import chain: 9 schema files value-importing the full tinacms package for admin form helpers. That is a real and separate cause — both need fixing. Note also that #4892 states these chunks don't appear in the route's static manifest; for the events route, dd3583a7 is in app-build-manifest.json, so it is not only arriving via dynamic(). Whoever picks up #4896 should read #4892 first and the two should probably be done together.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Treat this as an umbrella performance investigation rather than a single edit. Read blocks-renderer.tsx, header-appearance.tsx, and the route entry in .next/app-build-manifest.json, then review sub-issues #4895–#4907 and related #4892. Done means the sub-issues are resolved or deferred with reasons and the specified Lighthouse, bundle-size, CLS, and accessibility targets are measured and documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nextjs
Domain
frontend, performance, web-dev
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.