Slow is expensive
Frontend performance affects more than how long a user waits. Unnecessary work also means more network traffic, more backend requests and, in some systems, a higher infrastructure bill. The first problem is usually visible. The second can stay hidden for much longer.
I help teams find where that unnecessary work comes from and turn the findings into changes in the product. My main frontend experience is with Vue and Nuxt, but the problems I investigate are usually much broader: request waterfalls, duplicated data fetching, caching, JavaScript execution, rendering, bundles and the way the frontend interacts with the rest of the system.
How I investigate performance problems
I do not start with a checklist of Lighthouse scores. I start with the application as a user experiences it, then follow the evidence through the browser, the network and the code.
- Real user experience. Core Web Vitals such as Largest Contentful Paint, Interaction to Next Paint and Cumulative Layout Shift are useful signals, but I look at the pages and interactions that actually matter rather than optimising a single score on the home page.
- Requests and waterfalls. I look for chains of requests where one call waits for another, as well as the same data being fetched by several parts of the application.
- Bundles and code splitting. What is loaded on the first visit, what can be deferred, and which dependencies add significant weight without providing enough value in return.
- JavaScript and rendering cost. Repeated computation, unnecessary reactive work, expensive loops and rendering patterns that make the browser do the same work more often than necessary.
- Caching and data reuse. Whether assets and data are cached sensibly, whether query keys describe the data correctly and whether the application is asking the backend for information it already has.
- Real-time updates. When an application receives live events, for example through SignalR, I look at how those events affect cached data. A change to one piece of data should not force unrelated queries to refresh.
- The requirement itself. I also question the request before trying to make it faster. Does the screen need this data at all? Does it need it immediately? The simplest request to optimise is often the one the application never has to make.
From a performance finding to a working fix
A useful audit should lead to a change in the product, not just a report full of recommendations. The most valuable findings are the ones that explain not only what is slow, but why it is happening and what should change.
A good example was a large Vue 2 application being rewritten in Vue 3. The new version was already showing performance problems before release. The causes included nested loops, self-triggering watchers, TanStack Query being used as a global event bus and reactive queries that created request waterfalls.
I centralised the API clients, queries, mutations and query keys in one typed data layer and built an invalidation mechanism driven by SignalR messages. That made the data flow explicit and allowed each real-time event to invalidate only the queries it actually affected.
The result was straightforward: the remaining request waterfalls were eliminated. The details are in the performance rescue case study.
Sometimes the performance problem is outside the browser
Following a request from the browser does not always end at the frontend. It can lead through an API, a cache, a deployment setup or another team's codebase.
On one client platform, developers worked on isolated feature clusters built with Docker and Kubernetes. Each cluster had its own pod containing a translation cache. The platform served 28 applications in four languages, and every translation publish caused each pod to fetch data from a paid API.
I traced the problem to the code that rebuilt the cache. It contained unnecessary loops and fetched the complete translation set after every publish instead of requesting only the translations that had changed.
The fix was implemented by another team, so my role was to diagnose the problem precisely and explain where the unnecessary work came from. The solution was to fetch only the changed translations and keep them in a cache shared by the clusters.
The result was roughly 1,000 requests per publish reduced to 1, with paid API calls falling by 99.9%. It started as a performance investigation rather than a feature request. That is common in real systems: expensive problems often show up as symptoms long before anyone opens a ticket about the underlying cause.
More details are available in the cloud translation cost case study.
Making performance improvements last
Fixing one slow screen is only part of the job. Without some guardrails, the same patterns eventually come back with the next feature.
- Automated checks. Linting and formatting with tools like Biome, ESLint and Prettier, or Oxlint and Oxfmt move as much consistency as possible from code review into tooling.
- Git hooks. Pre-commit and pre-push checks catch simple problems before they reach a pull request, leaving code review focused on behaviour and design.
- A typed data layer. Typed query keys and explicit data access make the flow of data easier to follow and harder to bypass accidentally.
- Dependency and architecture checks. Circular dependencies can interfere with code splitting and unnecessarily increase bundle size. I also use rds, an open-source Rust tool I wrote to detect dependency cycles in Node.js and TypeScript applications.
Performance work in Nuxt
Not every performance problem is about API calls. On a Nuxt 4 and Payload CMS
website I built for an animal shelter, I worked on responsive image delivery
with @nuxt/image and critical CSS, using Lighthouse to measure
the effect of those changes.
Nuxt is one part of my experience, rather than the definition of the service. The same performance principles apply whenever a browser has to download, execute, render or repeatedly request more work than the product actually needs.
Who this is for
This service is useful for teams that:
- have a frontend application that has become noticeably slower as it has grown,
- see increasing network traffic, backend load or infrastructure costs that are difficult to explain,
- are preparing a rewrite and want to avoid carrying the same performance problems into the new version,
- have already tried obvious optimisations and need someone to trace the problem further down the stack.
My main experience is with Vue and Nuxt, but the investigation itself is not tied to one framework. Browsers, HTTP requests, caching, JavaScript execution, rendering and data flow all behave according to the same underlying principles.
If part of your platform is still running on Vue 2, see also Vue 2 to Vue 3 migration. For large multi-application platforms, my micro-frontend experience is also useful, especially when duplicated requests and shared data become a problem across applications.
I work remotely, part-time, on B2B terms. Tell me which part of your application feels slow, and I will reply within one business day.