10 React Performance Tips for Thailand's Mobile-First Market
6 May 2026 · by Yunmin Shin
Why React Performance Matters More in Bangkok Than a Devtools Benchmark Suggests
A React app that feels snappy on a developer's MacBook over office wifi can degrade badly under real conditions: mid-range Android hardware, congested 4G, and pages loaded with photography — clinic before-and-afters, restaurant menu shots, real estate listing galleries. Thai mobile users are the majority of traffic for nearly every local business site we build, and they're disproportionately on budget devices with weaker CPUs and less RAM than the phones most performance advice assumes.
Optimizing React performance here isn't premature optimization — it's the difference between a patient booking an appointment and a patient giving up while a treatment gallery stutters into view. Here are ten specific, actionable improvements for a Next.js React application.
1. Use Server Components for Data-Heavy Pages
Move components that fetch and display data to the server. They ship zero client JavaScript and skip the browser-to-API round trip entirely. A clinic's treatment list, a restaurant's menu, a real estate listing page — all of this is data the server already has; there's no reason to make a mid-range Android phone fetch and render it client-side. This is the single highest-impact change available in the App Router era.
2. Memoize Expensive Computations with useMemo
Wrap genuinely expensive calculations in useMemo — filtering a real estate site's full listing set by price range and district, for example, or sorting a restaurant's menu by category and availability. Don't reach for it on cheap operations; unnecessary useMemo adds comparison overhead without any benefit, and on slower CPUs that overhead is more noticeable, not less.
3. Stabilize Callbacks with useCallback
Functions passed as props are recreated on every render, which forces child re-renders. Wrap them in useCallback with the correct dependency array — this matters most when the child is a heavy component like an image gallery or a booking calendar wrapped in React.memo. On a budget Android device, an avoidable re-render of a large image grid is exactly the kind of jank a patient scrolling through clinic results actually notices.
4. Prevent Re-Renders with React.memo
Wrap pure functional components in React.memo so they skip re-rendering when props haven't changed. Combine with useCallback for callback props. Be selective — wrapping every small component adds its own overhead. Where it pays off consistently is exactly the components mentioned above: gallery items, listing cards, menu rows — anything rendered in a list.
5. Lazy-Load Heavy Components
Anything not visible on initial load — a booking modal, a map picker, a chart in an admin dashboard — should load lazily:
const BookingModal = dynamic(() => import("@/components/BookingModal"), { ssr: false });
Next.js dynamic() handles code splitting automatically. The modal's JavaScript only downloads when a user actually opens it, which matters a lot when that user is on a LINE LIFF in-app browser — LINE's embedded webview runs a more constrained JavaScript engine than a full Chrome browser, and every kilobyte of unnecessary upfront JS is felt more there than in a standalone browser tab.
6. Virtualize Long Lists
Rendering 500 real estate listings or a restaurant's full multi-page menu as 500 DOM nodes is wasteful and slow to scroll. Use @tanstack/react-virtual to render only the rows currently in the viewport:
const rowVirtualizer = useVirtualizer({
count: listings.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 180, // approximate listing card height
});
This keeps DOM size small and scrolling smooth — critical when your listing card includes a photo, price, and district badge, and your typical visitor is scrolling one-handed on a mid-range phone.
7. Optimize Images Rigorously
Use Next.js <Image> for every image, always with explicit width and height to prevent layout shift. This is non-negotiable for photo-heavy Bangkok business sites — a clinic's before/after gallery or a real estate listing's photo set is often the largest payload on the page. Use the priority prop only for genuinely above-the-fold images (a hero shot, a logo); everything else should lazy-load as the user scrolls, especially on a 4G connection that's already working hard.
8. Audit Your Bundle Size
Install @next/bundle-analyzer and run it against your production build. Look for unexpectedly large packages — date libraries, icon sets, and calendar/booking-widget libraries are common culprits on the kind of scheduling-heavy sites we build. Replace moment.js with date-fns, import only the icons you actually use rather than an entire icon set, and check that any third-party booking-calendar component isn't dragging in a full moment.js dependency of its own under the hood.
9. Prefetch Navigation Links
Next.js <Link> prefetches linked pages by default in production. Make sure real navigation — from a homepage to a treatments page, from a listing grid to an individual property page — actually uses <Link> rather than plain <a> tags. For programmatic navigation to a frequently visited page (a booking confirmation screen, for instance), call router.prefetch() explicitly.
10. Defer Non-Critical Third-Party Scripts
Google Analytics, a LINE chat widget, Facebook Pixel, and marketing tracking scripts often add 300-500ms to Time to Interactive combined — a real cost on a phone that's already spending its CPU budget rendering your content. Load them with next/script using strategy="lazyOnload" so users get your actual content — the booking form, the menu, the listing — immediately, and tracking initializes after the page is already interactive.
How Do You Verify Any of This Actually Worked?
Don't trust a Lighthouse run on office wifi. Throttle Chrome DevTools to "Slow 4G" and "4x CPU slowdown" to approximate a mid-range Android device on a congested Bangkok connection, and where possible, test on an actual budget Android phone. The gap between what looks fast on a developer machine and what a real patient or diner experiences is consistently larger than teams expect.
Ready to Build Something Fast?
Get a free quote. We reply within 24 hours.
Ready to build something fast and scalable?
Get a free project quote. We reply within 24 hours.
Get a Free Quote →