React · Case Study
Advanced React in the Wild: Fixing a Slow Travel Booking Experience at Scale
A full production-style case study showing how a travel technology company reduced JavaScript weight, improved Core Web Vitals, fixed slow filter interactions, and rebuilt critical React architecture without rewriting the entire platform.
ClientWayfareIQ
IndustryTravel Technology
Project typeReact Performance Optimization and Front-End Architecture Recovery
Duration14 weeks
Overview
Project: React Performance Optimization and Front-End Architecture Recovery
Duration: 14 weeks
WayfareIQ is a travel booking platform used by customers to compare flights, hotels, airport transfers, and bundled holiday packages. The company had grown quickly over four years, and the front-end product had expanded from a simple booking interface into a complex React application with personalized recommendations, interactive maps, filters, loyalty features, promotional banners, saved trips, multilingual content, and checkout flows. The product looked modern, but the experience was becoming slower with every release. Internal teams initially believed the main issue was backend search latency, but real-user data showed that much of the frustration came from the browser itself: JavaScript execution, hydration, long tasks, excessive React re-renders, and expensive UI updates.
The core problem
The React application had become too client-heavy. Critical pages shipped too much JavaScript, hydrated too many components immediately, and stored too much UI state in broad shared providers. Search result pages became especially slow because one user action could trigger large parts of the page to re-render. Filters, sorting, map previews, hotel cards, price badges, and recommendation modules were all connected too tightly.
Issues we addressed
Business signals
- Mobile users were abandoning hotel search pages before applying filters.
- Checkout completion was weaker on mobile than desktop.
- SEO landing pages were losing performance quality because LCP and INP were poor.
- Customer support received complaints that the site felt frozen during search and checkout.
- Product managers were hesitant to add new travel widgets because every new feature increased JavaScript weight.
- Engineering teams lacked a clear way to prove whether a release improved or damaged performance.
Technical signals
- Initial JavaScript payload on search pages was too large.
- The hotel results page rendered too many cards at once.
- Global React Context providers caused excessive re-renders.
- Large third-party scripts ran during hydration and early user interaction.
- The map view loaded even when most users never opened it.
- Date picker and checkout validation libraries added unnecessary bundle weight.
- Filtering and sorting logic ran synchronously on the main thread.
- Some analytics events fired during high-priority user interactions.
- Images were not consistently sized or prioritized.
- Layout shifts happened when prices, banners, and availability labels loaded late.
- The team relied too much on lab testing and not enough on real-user monitoring.
Baseline & measurement
Metrics C L S: 0.19 on package detail pages
I N P: 438ms at p75 on hotel search results
L C P: 4.9s at p75 on mobile landing pages
Checkout Input Delay: Noticeable typing lag on low-end mobile devices
Average Hydration Time: 3.1s on mobile search pages
Hotel Card Render Count: Several hundred cards rendered during initial page load
Filter Interaction Delay: 320ms to 700ms depending on result count
Initial Java Script Payload: 920KB compressed on hotel search pages
Pages Measured
- Homepage
- Destination landing pages
- Hotel search results
- Flight search results
- Package detail page
- Checkout flow
Primary Audience: Mobile users on mid-range Android devices and iOS Safari
Measurement Window: 30 days before optimization
Discovery & diagnosis
The team started by proving where the real bottlenecks were. Instead of assuming the backend was slow, engineers analyzed browser traces, React render behavior, route-level JavaScript, hydration cost, and real-user Web Vitals.
What we inspected
-
Title: Real-user monitoring setup
Description: The team added route-level tracking for INP, LCP, CLS, long tasks, hydration time, and interaction-specific delays. This allowed engineers to see which user actions were actually hurting customers.
-
Title: React Profiler investigation
Description: Profiling showed that filter changes caused unnecessary re-renders across the search layout. Hotel cards, map previews, sticky headers, recommendation panels, and tracking wrappers were re-rendering even when their visible data did not change.
-
Title: Bundle analysis
Description: The bundle report revealed heavy dependencies used on routes where they were not needed. The map module, date picker, icon library, loyalty modal, analytics tools, and recommendation widgets were all contributing to initial load cost.
-
Title: Third-party script audit
Description: Several marketing and analytics scripts were executing during the same time React was hydrating the page. This created main-thread pressure and delayed user interactions.
-
Title: Interaction trace review
Description: Chrome DevTools traces showed long tasks after filter clicks, date changes, and checkout form input. The interface appeared visually loaded, but the browser was still busy executing JavaScript.
The challenge
The biggest challenge was improving performance without pausing feature delivery or rebuilding the platform from scratch. WayfareIQ had active marketing campaigns, SEO landing pages, partner integrations, and a checkout funnel that could not be taken offline. The engineering team needed to make the existing React and Next.js application faster while keeping the same business functionality. The work had to improve real mobile performance, not just Lighthouse scores on developer machines.
Approach
The solution was not a full rewrite. The team kept React and Next.js, but changed how the application loaded, rendered, hydrated, and updated. The work focused on reducing unnecessary JavaScript, isolating expensive React state, delaying non-critical UI, and improving real-user interaction responsiveness.
Strategy
- Ship less JavaScript on first load.
- Render critical content earlier.
- Hydrate only what needs immediate interaction.
- Move state closer to where it is used.
- Prevent unnecessary React re-renders.
- Virtualize long result lists.
- Delay third-party scripts.
- Measure every improvement with real-user data.
- Add performance budgets to prevent regression.
Implementation playbook
Phase1 Title: Route-level rendering cleanup
Actions
- Moved destination landing pages to server-rendered or statically generated routes.
- Reduced client-only rendering for content that did not need immediate interactivity.
- Split page shells from interactive widgets.
- Delayed below-the-fold sections such as recommendations, partner banners, and travel guides.
- Added Suspense boundaries around non-critical modules.
Description: The team separated pages by intent. SEO-focused destination pages were made more server-rendered and static where possible, while highly interactive booking pages kept client-side behavior only where it was necessary.
Phase2 Title: JavaScript payload reduction
Actions
- Lazy-loaded the map module only when a user opened map view.
- Replaced a heavy date-picker package with a smaller internal component.
- Removed duplicate formatting and utility libraries.
- Changed broad icon imports to direct icon imports.
- Split checkout validation by step instead of loading all rules upfront.
- Moved the loyalty modal out of the default route bundle.
- Added CI checks to block JavaScript bundle growth above agreed thresholds.
Description: The team removed unnecessary JavaScript from the first page load. Several features were important, but they did not need to load immediately for every user.
Phase3 Title: React state and rendering optimization
Actions
- Split large Context providers into smaller, focused providers.
- Moved filter state closer to the filter panel and result list.
- Used memoized selectors for derived search data.
- Stabilized object and function references passed to hotel cards.
- Used React.memo only on components proven expensive by profiling.
- Removed unnecessary wrapper components that caused render cascades.
- Separated urgent UI feedback from slower result recalculation.
Description: The original search page had too much shared state. Filter state, currency state, user session state, map state, and UI preference state were placed too high in the tree. This caused unrelated components to update together.
Phase4 Title: Search result list virtualization
Actions
- Virtualized the hotel results list so only visible cards rendered.
- Reduced DOM complexity inside each hotel card.
- Moved secondary details such as amenities and cancellation policies behind expandable sections.
- Lazy-loaded hotel card images as they approached the viewport.
- Reserved layout space for images and pricing blocks to reduce layout shift.
Description: The hotel search page often displayed hundreds of results. Rendering every card at once was unnecessary and expensive.
Phase5 Title: INP-focused interaction improvements
Actions
- Made filter buttons respond visually before recalculating results.
- Deferred expensive sorting operations after immediate UI feedback.
- Debounced rapid filter changes.
- Moved analytics calls outside critical interaction paths.
- Reduced synchronous work inside date selection.
- Changed checkout validation from every keystroke to blur and step submission where appropriate.
- Precomputed common filter counts on the server.
Description: The team focused heavily on Interaction to Next Paint because users complained that the interface felt delayed even after the page appeared loaded.
Phase6 Title: Third-party script governance
Actions
- Delayed non-essential analytics until after the page became interactive.
- Removed one heatmap tool from checkout after proving it added cost without enough business value.
- Loaded affiliate tracking only on routes where it was required.
- Moved scripts behind a lightweight event queue.
- Required performance review before adding new third-party tools.
Description: The team created a strict policy for scripts because marketing tools were silently damaging performance.
Phase7 Title: Image and layout stability improvements
Actions
- Used responsive image sizes for destination and hotel images.
- Prioritized the actual LCP image on landing pages.
- Reserved space for promotional banners and price blocks.
- Compressed legacy image assets.
- Prevented late-loading availability badges from shifting layout.
- Removed client-side layout changes after pricing data loaded.
Description: Large travel images were important for conversion, but they were hurting LCP and CLS when loaded incorrectly.
Results
- INP improved from 438ms to 178ms at p75 on hotel search pages.
- LCP improved from 4.9s to 2.4s at p75 on mobile landing pages.
- CLS improved from 0.19 to 0.05 on package detail pages.
- Initial JavaScript payload on hotel search pages dropped from 920KB to 372KB compressed.
- Average hydration time on mobile search pages dropped from 3.1s to 1.3s.
- Filter interaction delay dropped from 320-700ms to mostly under 150ms.
- Map-related JavaScript was removed from the default search page load.
- Checkout typing lag was reduced by changing validation timing and removing synchronous work.
- Hotel result rendering became stable even with large result sets.
- Performance regressions became easier to detect before release because CI bundle budgets were added.
- Mobile users experienced a faster booking journey, especially on slower devices and networks.
Business impact
The performance work improved more than technical scores. It made the booking experience feel more trustworthy. Users could search, filter, compare, and checkout without the interface freezing during key decisions.
Outcomes
- Higher engagement with hotel filters on mobile.
- Lower frustration during checkout form completion.
- Improved SEO readiness for destination landing pages.
- More confidence for product teams shipping new features.
- Reduced engineering time spent investigating repeated performance complaints.
- A stronger internal culture around measuring front-end performance before and after releases.
Before & after
| Area | Before | After |
|---|---|---|
| User Experience | The site looked complete, but it felt heavy. Search pages appeared loaded before they were truly ready. Users could tap filters, date pickers, and checkout inputs, but the response was often delayed. On mobile, this made the product feel unreliable. | Pages loaded faster, interactions responded sooner, and search results updated more smoothly. Users no longer had to wait for every non-critical feature to hydrate before using the main booking flow. |
| Business Experience | Marketing and product teams were adding features, but each feature increased page cost. The site was becoming more powerful and less usable at the same time. | The company could continue adding travel features without blindly increasing JavaScript cost. Performance became a product quality metric, not an afterthought. |
| Engineering Experience | Developers had difficulty understanding which components caused slowdowns. Shared state was too broad, third-party scripts were poorly controlled, and bundle growth was not visible during normal development. | The React architecture became easier to reason about. State ownership was clearer, expensive components were isolated, and performance became part of the release process. |
Engineering decisions
-
Do not rewrite the entire React app.
A rewrite would have been risky and slow. Profiling showed that targeted architectural fixes could solve the biggest problems faster.
-
Reduce JavaScript before adding memoization everywhere.
The biggest bottleneck was not only render frequency. The browser was overloaded by too much initial JavaScript and hydration work.
-
Virtualize search results.
Rendering hundreds of complex hotel cards upfront created unnecessary DOM and React work.
-
Delay third-party scripts.
Analytics and marketing scripts were competing with React during the most important loading and interaction moments.
-
Use real-user monitoring as the source of truth.
Lab scores helped during debugging, but real mobile users showed where the actual business pain existed.
Lessons learned
- React performance problems are often architecture problems, not library problems.
- A page can look loaded while still being unresponsive.
- INP problems usually come from too much work during user interactions.
- Large Context providers can quietly damage performance across an entire page.
- Third-party scripts need ownership and rules.
- Virtualization is essential when rendering large interactive result lists.
- Server rendering and static generation should be used where interactivity is not immediately needed.
- Performance budgets are necessary because bundle size grows slowly until it becomes a serious product problem.
- Real-user monitoring is more valuable than relying only on local Lighthouse tests.
Role: Head of Product Engineering
Quote: The biggest win was not just that the pages became faster. The team finally understood why they were slow. We kept our React stack, avoided a risky rewrite, and created a performance process that now protects every release.
Person: Elena Markovic
Company: WayfareIQ
Summary
WayfareIQ's React performance project showed that a mature web application does not always need a full rebuild to become fast again. By measuring real user pain, reducing JavaScript, improving rendering strategy, isolating React state, virtualizing large lists, and controlling third-party scripts, the team turned a heavy booking experience into a faster and more reliable product. The result was a stronger front-end architecture, better Core Web Vitals, smoother mobile interactions, and a development process that made future performance regressions harder to ship.
About the Author
Author icon By Rahul B.
- ✓ Verified Expert
Experience icon 9 years of experience
My name is Rahul B. and I have over 9 years of experience in the tech industry. I specialize in the following technologies: AngularJS, Symfony, PHP, Vue.js, JavaScript, etc.. I hold a degree in Bachelor of Engineering (BEng). Some of the notable projects I've worked on include: Utside, Alcolm Planner, ComedyCrowdTV, Create secure stripe integration, Norah.AI, etc.. I am based in Rajkot, India. I've successfully completed 6 projects while developing at Softaims.
I possess comprehensive technical expertise across the entire solution lifecycle, from user interfaces and information management to system architecture and deployment pipelines. This end-to-end perspective allows me to build solutions that are harmonious and efficient across all functional layers.
I excel at managing technical health and ensuring that every component of the system adheres to the highest standards of performance and security. Working at Softaims, I ensure that integration is seamless and the overall architecture is sound and well-defined.
My commitment is to taking full ownership of project delivery, moving quickly and decisively to resolve issues and deliver high-quality features that meet or exceed the client's commercial objectives.
