SSWConsulting / SSWConsulting/SSW.Website
💸 Perf - AI for Business Leaders event page - LCP 9.3s, TBT 1,250ms on mobile
Nobody has claimed this yet.
- 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 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, anddd3583a7-*.jsalone is 1,115 KB of it. 65 components importTinaMarkdownfromtinacms/dist/rich-text, whose first line isimport { 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". - The LCP image is lazy-loaded.
blocks-renderer.tsxgrantspriorityto block 0, which isbreadcrumbs(no image). The hero is block 2 and getsloading="lazy"with nofetchpriority. The file is only 78 KB — 9.3 s is entirely "requested late, behind 1.7 MB of JS". - Every one of the 39 blocks pays for
V2ComponentWrapper— agetBoundingClientRect()in a mount effect, its ownresizelistener, a framer-motionuseInViewobserver, and an inlinebackground-image: url("")/url(undefined). 20 of those 39 blocks are empty 45 px spacers. - Site-wide passengers: AOS initialises on every page (unused on this one), Application Insights constructs during the critical window,
minimumCacheTTL: 60makes optimised images near-uncacheable, and 8 Eventbrite CTAs each mount an always-livereact-responsive-modal. - CLS 0.133 is the header — it SSRs with the flag and Contact button, then
useMobileHeaderAppearancestrips them after hydration.header-appearance.tsxdocuments 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
- All 13 sub-issues are closed or explicitly deferred with a reason
- Mobile Lighthouse on
/events/ai-for-business-leadersshows LCP under 2.5 s and TBT under 200 ms - CLS is under 0.1
- The route's initial JS (per
app-build-manifest.json) is under 700 KB uncompressed - Accessibility score is 95 or above
- Before/after Lighthouse runs are attached to this issue
Notes
- Findings are from a static read of the codebase plus the committed
.nextbuild (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,consultingv2and 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
tinacmspackage 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,dd3583a7is inapp-build-manifest.json, so it is not only arriving viadynamic(). 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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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