I cut FightCamp's mobile load time from 10 seconds to 2 and dropped the mobile bounce rate by 39%, recovering more than half the visitors who used to leave before the page finished rendering. The site's growth after the pandemic had outpaced its web infrastructure: mobile was over 70% of traffic, those users sat through 10-plus-second loads, Lighthouse scores were stuck in the 30s and 40s, and the content team filed around ten engineering requests a week just to update marketing pages. I owned the fixes end to end, from how images and video were delivered to the build pipeline and the self-serve CMS that got marketing off the engineering backlog.
Where the time was going
I found the problems on an R&D Friday and got the go-ahead to dig in. Lighthouse, Chrome DevTools, and a bundle analyzer kept pointing at the same culprits: HD images served at full size no matter the viewport, videos auto-downloading 500-800MB on page load, duplicate libraries bloating a 650-800KB bundle, no lazy loading anywhere, and missing dimensions that left layouts shifting as content arrived.
The fixes
Images and video were the bulk of the payload, so they went first. I built one centralized image component backed by Storyblok with fallback providers, wired up viewport-dependent srcset so a phone never downloaded a desktop image, and added lazy loading with explicit dimensions so layouts stopped shifting. For video I wrote a lazy-loading component on the Intersection Observer API that kept off-screen videos from playing or downloading, and served multiple S3-hosted quality levels that cut payloads from 500-800MB to under 300MB on MP4 and 150MB on WebM.
The bundle came next. I tracked down and removed duplicate libraries, turned on webpack tree-shaking and dynamic imports, and tuned the Nuxt.js production build, which brought it from 650-800KB down to around 300KB.
The last piece fixed the ten-requests-a-week problem at its source. I pulled the shared patterns out of seven marketing pages, extended the UI component library with 10-plus CMS-connected components documented in Storybook and backed by Jest, and integrated Storyblok so marketing could publish without an engineer in the loop. On top of it I built configurable A/B-test components wired to Google Optimize, so marketing ran their own quarterly copy, image, video, and offer tests.
The results
Before
8-12s
After
1.8-2.5s
Before
15-20s
After
5-8s
Before
3-4s
After
0.8-1.3s
Before
3-6s
After
0.6-1.25s
Before
11-14s
After
4-6s
Before
32-43
After
~80
Before
650-800KB
After
250-300KB
Before
60-75%
After
37-45%
Before
~10/week
After
~2/week
What I took from it
Performance turned out to be a business metric: that 39% drop in bounce fed straight into acquisition during a critical growth phase. And the highest-leverage work wasn't the load-time tuning at all; it was the CMS that took marketing off the engineering backlog and sped both teams up at once.
