The project involved a social media intelligence web app where the customers can design and create social media posts, publish them across multiple accounts and regions, and schedule them for specific times.
A bug report flagged a memory issue affecting 3-4 high-MRR clients: their browsers crashed within 1-2 minutes of use. Support mentioned a spike to 2.2GB RAM for a single browser tab and suggested the calendar view was unable to deal with large data. The affected clients could not use the app, creating a combined churn risk of $60K MRR.
The project is a Vue application that uses web components which are made using Stencil.js in an internal components library.
I investigated the issue to isolate the root cause using Chrome DevTools to compare two states of the app: on initial load and after one minute of general exploration.
In the Memory tab, the heap snapshots (a dump of memory at a point in time) revealed many detached elements (elements removed from the DOM but retained in memory) with no parents present. I also saw many spreadsheet instances. The Shadow DOM was retaining these detached nodes, inflating the memory usage.
In the Performance tab, I saw long tasks (red blocks) on the main thread. These tasks blocked rendering and input handling, which explained the UI freezes. They appeared as soon as the app rendered and persisted for most of the session. I disabled the Calendar to test the suggestion by support team; performance was still degraded even without the Calendar enabled (Even though the calender is the core of the whole application). There were also non-visible modals being mounted on the initial load. Even when not shown, they triggered expensive API calls, state updates, and heavy computations.
Heap snapshots exposed detached DOM nodes retained in memory.
Performance traces showed long tasks blocking the main thread.
I reproduced the issue in other accounts with large calendars and could not trigger the crash, this ruled out the hypothesis that calendar can not handle heavy data. I narrowed the scope to only the affected clients and identified a different trigger: they had an unusually large number of tags (internal keywords used in posts). Opening the tags list (1000+ items) caused the application to slow down significantly.
After finding the tags issue, I hit a blocker since I could not test fixes on the client account without pushing code to prod. I also could not make repeated deploys to production. The Project had 3 environments set up: Staging, Feature Branch Environments based on Staging, and Production.
There were 2 kinds of fixes that were finalized after investigation: General and Environment specific.
The Lazy load + Cleanup for modals and other detached elements was a general fix that was implemented for all environments.
The tags list issue was specific to the production environment, so I set up a dedicated production-like environment for feature testing. This let us validate changes against production data without affecting it. In that environment, I enabled list virtualization for the tags list (rendering only visible items instead of the entire dataset).
With these fixes, I reduced memory consumption from 2.2GB to ~200MB, and this did not increase even after continued usage of the application.
Vue.js is used in conjunction Web Components compiled via Stencil.js.
OR
Write To Us At Your Own Convenience