Skip to main content
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