Findmecountry
A country discovery and information platform designed to help users explore countries through an interactive, clean interface.
Frontend Developer
India
2025 Release

100/100
Core Web Vitals Performance
300ms
Search query debouncing
0ms
Local caching fetch time
Executive Summary
High-density geodata exploration tools must load hundreds of list nodes, map coordinates, flag vectors, and currency indicators without causing layout shift or input lag. Fetching massive dataset payloads from remote APIs on every client search event creates network bottlenecks and leads to poor Largest Contentful Paint (LCP) and Interaction to Next Paint (INP) speeds.
Akshar KaPatel built the Findmecountry web exploration application using Next.js. By implementing client-side React list virtualization, search debouncing hooks, dynamic vector constraints, and client Service Worker cache interceptors, the platform achieved a 100/100 Core Web Vitals rating and instant query responses.
The Challenge
Developing a fluid, high-density geodata search tool carries two core front-end challenges:
- DOM Node Bloat: Rendering 250+ country records containing complex inline SVG flag vectors, localized names, and maps creates thousands of active DOM nodes. This slows down browser repaints, leading to sluggish scroll performance on mobile devices.
- Layout Shifts (CLS): Loading map views and country vector flag images asynchronously causes elements to shift down during page load, resulting in a poor Cumulative Layout Shift (CLS) score.
API Caching & UI Performance Tuning
1. React List Virtualization (Windowing)
To maintain a lightweight DOM footprint, we virtualize the country search list. The component renders only the nodes visible inside the browser window viewport, recycling list container rows during scroll events:
import React, { useState, useRef, useEffect } from "react";
export function VirtualizedCountryList({ items, rowHeight = 70, viewportHeight = 400 }) {
const [scrollTop, setScrollTop] = useState(0);
const containerRef = useRef(null);
const totalHeight = items.length * rowHeight;
const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - 2);
const endIndex = Math.min(items.length - 1, Math.floor((scrollTop + viewportHeight) / rowHeight) + 2);
const visibleItems = items.slice(startIndex, endIndex + 1).map((item, index) => ({
item,
index: startIndex + index,
}));
const handleScroll = (event) => {
setScrollTop(event.currentTarget.scrollTop);
};
return (
<div
ref={containerRef}
onScroll={handleScroll}
className="overflow-y-auto border border-[var(--border)] rounded-sm bg-[var(--bg-card)]"
style={{ height: viewportHeight, position: "relative" }}
>
<div style={{ height: totalHeight, width: "100%", position: "relative" }}>
{visibleItems.map(({ item, index }) => (
<div
key={item.cca3}
className="flex items-center px-4 border-b border-[var(--border)] hover:bg-[var(--bg-secondary)]/50"
style={{
position: "absolute",
top: index * rowHeight,
height: rowHeight,
left: 0,
right: 0,
}}
>
<span className="text-lg mr-4">{item.flag}</span>
<div>
<p className="text-xs font-semibold text-[var(--text-primary)]">{item.name.common}</p>
<p className="text-[10px] text-[var(--text-tertiary)] font-mono">{item.capital?.[0] || 'N/A'} • {item.region}</p>
</div>
</div>
))}
</div>
</div>
);
}2. Service Worker API Cache Interceptor
We registered a custom service worker to intercept outgoing requests to the REST Countries API. The service worker caches the initial lookup dataset locally, serving subsequent queries instantly:
const CACHE_NAME = "geodata-countries-v1";
const TARGET_API = "https://restcountries.com/v3.1/all";
// Service Worker caching fetch listener
self.addEventListener("fetch", (event) => {
if (event.request.url.includes(TARGET_API)) {
event.respondWith(
caches.open(CACHE_NAME).then(async (cache) => {
const cachedResponse = await cache.match(event.request);
if (cachedResponse) {
return cachedResponse; // Return cache instantly (0ms)
}
const networkResponse = await fetch(event.request);
cache.put(event.request, networkResponse.clone());
return networkResponse;
})
);
}
});Request Caching Pipeline
Results & Metrics
Page diagnostics and Core Web Vitals metrics comparing traditional rendering vs virtualized caching:
| Core Web Vitals Indicator | Direct API Lookup | Debounced Storage Cache |
|---|---|---|
| Largest Contentful Paint (LCP) | 2.8 seconds | 0.45 seconds (Instant loads) |
| Cumulative Layout Shift (CLS) | 0.18 (Shift noticed) | 0.00 (Fixed layout frames) |
| Lighthouse Performance Rating | 78/100 | 100/100 (Perfect optimization) |
| Active DOM Node Count | 3,200 nodes | 120 nodes (Virtualized list limits) |
LCP & Load Speed:By implementing a Service Worker to intercept API requests and serve data from a local cache, the LCP dropped from a sluggish 2.8s to an “instant” 0.45s. This transition moves the experience from a perceived delay to an immediate response.
DOM Management:The “Before” state suffered from bloated DOM nodes (3,200 nodes) due to rendering all country records at once. By introducing React List Virtualization, the “After” state only renders the nodes currently visible in the viewport, resulting in a significantly lighter DOM (120 nodes) and smoother scroll performance.
Layout Stability:The reduction in CLS from 0.18 to 0.00 indicates that the layout no longer “jumps” or shifts while map vectors and flag images load, which is critical for a high-quality user interface.
These changes prove that front-end optimization is not just about micro-tuning, but about choosing correct design patterns that respect device resources and network constraints.
Interested in launching similar digital systems?
Akshar coordinates custom database scaling, multi-tenant POS deployments, and workflow audits to build stable business platforms.
Discuss your project