All Docs
FeaturesDepositClearUpdated March 13, 2026

How We Removed 145 KB from the Properties Page Bundle

How We Removed 145 KB from the Properties Page Bundle

Release: v0.1.241 · Category: Frontend Performance · Ref: PERF-05

The Problem

Every user who visited the Properties page was silently downloading the entire Leaflet mapping library — approximately 145 KB minified — even if they never clicked to open the map view.

The root cause was a single line in property-map.tsx:

// ❌ Before — inside a useEffect hook
const L = require('leaflet');

Because require() is a CommonJS runtime call, Next.js's bundler cannot statically analyse the import graph at build time. The result: Leaflet ends up inlined into the main client bundle and shipped to every visitor on page load, unconditionally.

A secondary issue: Leaflet's CSS was being loaded from the unpkg.com CDN by injecting a <link> tag into the document head at runtime:

// ❌ Before — CDN link injection
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://unpkg.com/leaflet/dist/leaflet.css';
document.head.appendChild(link);

This created two additional issues:

  1. External dependency — a CDN outage or network restriction breaks map styling.
  2. FOUC (Flash of Unstyled Content) — the stylesheet arrives after the map renders, causing a visible style jump.

The Fix

1. Dynamic ESM Import

Replacing require() with a dynamic ESM import() inside the useEffect gives Next.js the information it needs to split Leaflet into its own lazy-loaded chunk:

// ✅ After — dynamic import inside useEffect
useEffect(() => {
  const initMap = async () => {
    const L = await import('leaflet');
    // ... map initialisation
  };
  initMap();
}, []);

Next.js now emits Leaflet as a separate JavaScript chunk. That chunk is only requested by the browser when PropertyMap is actually mounted — i.e. when the user opens the map view.

2. Self-Hosted CSS Import

Removing the CDN <link> injection and replacing it with a standard static import lets Next.js bundle the stylesheet alongside the component:

// ✅ After — static CSS import at the top of the file
import 'leaflet/dist/leaflet.css';

Because PropertyMap is already wrapped in next/dynamic with ssr: false in properties-list.tsx, this CSS is only injected when the component chunk is loaded — no CDN round-trip, no FOUC.

Why This Works

Next.js performs static analysis of import statements at build time to build a module graph. Dynamic import() expressions are also understood — they produce split points in the bundle. require() calls, by contrast, are opaque to this analysis: the bundler conservatively includes the module in whichever chunk triggers the require().

The existing architecture already anticipated lazy loading: properties-list.tsx wraps PropertyMap with next/dynamic / React.lazy. The fix in this release completes the picture by ensuring Leaflet itself also participates in that laziness.

Impact

BeforeAfter
Leaflet in initial JS bundle~145 KB (minified)0 KB
Leaflet loaded when map opens
CSS sourceunpkg.com (external)Self-hosted
FOUC on map renderPossibleEliminated
External CDN dependencyYesNo

Files Changed

  • src/app/dashboard/properties/property-map.tsx