The Woman Who Chased My Images Out of Her Shop
The Woman Who Chased My Images Out of Her Shop
I’d been standing under the awning of a hardware store on Geylang Road for maybe forty-five minutes. It was raining the way it rains in Singapore in November — not a drizzle, not a storm, just a steady, soaking sheet that makes everything feel damp even when you’re under cover. My phone was dying and I was trying to get a photo of this old shophouse facade, the kind with the peeling pastel paint and the wooden shutters that still have the original iron hinges. Every time I framed the shot, a bus would pass and shake the frame, or a taxi would splash water onto the curb, or someone would walk through the exact spot I was trying to keep empty.
Then a woman opened the shop door — not the hardware store, the one next to it, a little place selling dried goods and tins of biscuits — and she waved at me. Not a friendly wave. The kind of wave you give someone who’s about to do something stupid. She said something in Hokkien that I didn’t catch, then in English: “You stop. You stop taking picture.”
I lowered my phone. She pointed at the shophouse I’d been trying to photograph. “That one. My auntie house. You take picture, you put online, then people come take picture, then my auntie cannot sleep. No more picture.”
I apologized and walked away. I didn’t get the photo.
Twelve Seconds at Raffles Place MRT
When I started building sites for small businesses in Singapore — kopitiams, a little tech repair shop in Toa Payoh, a yoga studio in Tiong Bahru — I did what most people do. I uploaded the full-resolution JPEGs straight from my camera. The files were around 4-5 MB each. The pages looked fine on my laptop because I had fiber internet and a fast local connection.
But I tested one of the sites on a 3G connection at the MRT station at Raffles Place one afternoon. The page loaded in stages: first the header, then a white gap, then the menu, then slowly, painfully, the images would start filling in from top to bottom. It took almost twelve seconds for the page to be fully rendered. I watched a woman next to me tap her screen three times on the “Book Now” button before she gave up and walked to the train.
That was the moment I started paying attention to lazy loading. Not because some blog told me it was a good practice. Because I literally watched someone lose interest in a site I had built.
The fix isn’t complicated, but it’s also not something WordPress does well out of the box. By default, WordPress loads every image on a page when the page loads, even if that image is three scrolls below where the user is. If you have a gallery with 20 photos and the user only sees the first two, the browser has already downloaded all 20. That’s wasted bandwidth, wasted time, and a slower experience for everyone — especially on mobile data.
Lazy loading flips that logic. The browser loads only the images that are visible in the current viewport (the part of the screen the user can actually see). As the user scrolls, new images load just before they come into view. The user never waits for images they aren’t looking at.
The Bakery Logo That Wouldn’t Load
I should say upfront: I am not a developer. I can read code the way I can read French — slowly, with guesses, and a lot of context clues. So when I say I implemented lazy loading on a WordPress site, I’m not talking about writing a custom JavaScript library from scratch. I’m talking about the options that are actually available to someone who runs a WordPress site and wants this to work without breaking their theme.
The Native Way (No Plugin)
As of WordPress 5.5, which came out in August 2020, WordPress has built-in lazy loading for images. It adds a loading="lazy" attribute to every image on the site automatically. This is the simplest option — it works, it’s free, and it doesn’t require any additional code.
But there’s a catch. It only applies to images that are added through the WordPress editor — the ones in the post content. It doesn’t apply to images in your theme’s header, footer, sidebar widgets, or custom fields. If your site has a logo in the header (a common case), that logo gets the loading="lazy" attribute too, which means it might load slowly on the initial page view. That’s bad — you want your logo to load immediately because it’s part of the first thing the user sees.
I noticed this on a client’s site — a little bakery in Katong. The logo was taking an extra two seconds to appear because WordPress was treating it like any other image. I had to manually add loading="eager" to the logo’s <img> tag in the theme files to override the default. If that sentence sounded complicated, that’s because it is — for someone who doesn’t poke around in theme files regularly.
The Plugin Route (What I Actually Recommend)
I ended up using a plugin called Lazy Load by WP Rocket. It used to be a separate plugin but now it’s been absorbed into their caching plugin. I wasn’t thrilled about that because I don’t like paying for caching plugins on small-budget sites. But the lazy loading mechanism itself is good — it uses a technique called “intersection observer” (the browser checks whether the image is in view, not whether the page has finished loading) and it works on images, iframes, videos, and even background images in CSS.
The plugin also lets you exclude specific images from lazy loading. That’s the key feature. I excluded the bakery’s logo, a map embed on the “Contact Us” page, and the hero image on the homepage. Everything else got lazy-loaded.
There are free alternatives — a3 Lazy Load and Lazy Load — Optimize Images both work. I tested both on a staging site for a friend’s cafe in Chinatown. a3 Lazy Load was fine, but it added about 300 KB of JavaScript to the page load. For a site that was already trying to slim down, that felt counterproductive. Lazy Load — Optimize Images was more lightweight but didn’t have the exclusion feature built in unless you used their premium version.
In the end, I went with the WP Rocket solution because the bakery’s budget allowed for it (they were paying for the whole caching bundle anyway) and the exclusion feature saved me from having to manually edit theme files.
14 Seconds on a Redmi Note
Before lazy loading, the bakery’s homepage had 14 images — the logo, a hero image, and 12 product photos in a grid. Total image weight was about 3.2 MB. The page took 6.8 seconds to fully load on a simulated 3G connection using Chrome’s DevTools.
After enabling lazy loading, the initial page weight dropped to about 800 KB — the logo, the hero image, and the first four product photos (the ones visible in the viewport without scrolling). The other eight images loaded as the user scrolled. The time to first render dropped to 2.1 seconds. The time to fully loaded (when a user scrolled through the whole page) was about the same — 6.2 seconds — but the user didn’t feel the wait because images were loading just ahead of where they were looking, not all at once.
I tested this on a few phones: my own OnePlus (Android, Chrome), a friend’s iPhone 11 (Safari), and a cheap Redmi Note that I bought specifically for testing (about S$180, runs Android Go). The Redmi Note was the most telling — the unoptimized site took 14 seconds to finish loading. With lazy loading, it was down to 4.5 seconds for the visible content. That’s a site that someone on a budget phone could actually use without wanting to throw the phone at a wall.
I should mention: these tests were done on a weekday afternoon, from a coffee shop in the Katong area, using a 4G dongle that gave me about 15 Mbps download speed. Not ideal, but that’s the point. Real-world conditions are never ideal.
The Grid That Rearranged Itself
Lazy loading can break certain things. The main one is the “jump” effect — when an image finally loads and pushes the content below it further down the page. If the user is already reading a paragraph below the image, suddenly that paragraph shifts down by 200 pixels. They lose their place. It’s disorienting.
I ran into this on the bakery’s product grid. The images were all different aspect ratios — some square, some landscape, some portrait. When the lazy loading kicked in, the grid would suddenly rearrange itself as each image loaded with its correct dimensions. It looked messy. A customer at the bakery told me she’d seen it happen on her phone and thought the site was “broken.”
The fix is simple but requires a bit of HTML: you need to specify the width and height attributes on the <img> tags, even if you’re using responsive images. Modern WordPress does this automatically for images added through the media library, but if you’re using custom fields or a page builder that doesn’t include those attributes, you’ll need to add them manually. The browser reserves the space for the image before it loads, so the page doesn’t jump.
On the bakery’s site, I had to go into the theme’s template files and add width="300" height="300" to the product images in the grid loop. Not elegant, but it worked. The grid stayed stable. The customer stopped complaining.
Another thing: lazy loading should never apply to the first image in the viewport. That’s the one the user sees immediately when the page loads. Lazy loading it would just introduce an unnecessary delay for no benefit. Most good lazy loading plugins handle this automatically — they check the image’s position relative to the viewport and skip lazy loading if it’s in the top 100 pixels or so. But it’s worth double-checking.
500 Milliseconds and the Woman Who Tapped Three Times
The bakery’s site is still not fast. It uses a shared hosting plan from a provider in Singapore (I won’t name them, but you can guess the one with the orange logo), and the server response time is mediocre — around 500-600 ms. Lazy loading didn’t fix that. What it did fix was the user’s perception of speed. The site feels faster because the visible content loads quickly, even if the total page weight hasn’t changed much.
I think about the woman in Geylang who chased me away from her auntie’s shophouse. She was right to be annoyed. That building wasn’t mine to photograph for my own purposes without context or permission. But the reason I was standing there in the rain was that I was trying to find interesting images for a site that didn’t yet exist — a site that would eventually load those images so slowly that someone would give up and walk away.
Lazy loading isn’t magic. It’s just one of those small adjustments that turns a frustrating experience into a tolerable one. And on a slow connection, on a cheap phone, in a country where people spend a lot of time on their phones in transit, tolerable is the difference between someone staying on your site and someone going to your competitor’s.
The best person to test this on is not a developer with a fiber connection. It’s the woman I saw at Raffles Place MRT, tapping her screen three times before giving up. If your site works for her, it’ll work for everyone.
📷 Photos: Team Nocoloco (Unsplash), Team Nocoloco (Unsplash)