Illustrative case study

A fictional business. The figures are a modelled scenario, not a client result.

风 冷气服务Kaze Aircon Services

Air-con servicing, 60 technicians · Singapore · Singapore

Case study · Technical SEO · 5 months

A JavaScript booking app was hiding Singapore's busiest aircon servicer from Google

A client-rendered single-page booking site meant Google saw almost empty pages, so service and estate pages never indexed.

请到柜台Now serving

Indexed pagescounter 05

Issues fixed

  • ●NOW SERVINGA01 fixed · first HTML now carries 14 KB of content
  • ●NOW SERVINGA02 fixed · 46 estates on real URLs
  • ●NOW SERVINGA03 fixed · 2,640 slot URLs out of the crawl
  • ●NOW SERVINGA04 fixed · mobile LCP 1.8 s
  • ●NOW SERVINGA05 fixed · prices readable as text
  • ●NOW SERVINGA06 fixed · one phone format everywhere
01问题The challenge

The busiest counter in Singapore, and Google could not see the queue.

A client-rendered single-page booking site meant Google saw almost empty pages, so service and estate pages never indexed.

Kaze Aircon Services runs 60 technicians across the island and takes most bookings online. The site was a single-page app: lovely to use, but every service and estate page was assembled in the browser. Googlebot fetched each URL, found an empty shell and moved on. Of roughly 460 pages a customer could reach, 38 were indexed.

The work was never about more content. It was about letting Google read what was already there.

What Googlebot got

<html>
 <head>
  <title>Kaze Aircon</title>
 </head>
 <body>
  <div id="app"></div>
  <script src="/main.js">
 </body>
</html>

1.1 KB. No services, no prices, no estates.

What customers got

  • General servicing · $30/unit
  • Chemical wash · $60/unit
  • Gas top-up · from $50
  • 46 estates, same-week slots

Rendered after 3 API calls and 1.4 MB of JavaScript.

02诊断Audit & diagnosis

Seven tickets in the queue, numbered by severity.

  1. A01Critical

    Rendering

    Googlebot received a 1.1 KB shell

    The first HTML response was a single empty <div id="app">. Service copy, prices and estate names arrived after three API calls and a 1.4 MB bundle, so most pages were queued for rendering and dropped as soft-empty.

  2. A02Critical

    Rendering

    Hash-routed URLs

    Estate pages lived at /#/estates/tampines. Google ignores the fragment, so 46 estate pages collapsed into one URL: the homepage.

  3. A03High

    Index bloat

    2,640 booking-slot URLs

    Every calendar slot (?date=…&slot=…) was a crawlable link. Googlebot spent most of its visits on empty calendars instead of service pages.

  4. A04High

    Core Web Vitals

    Mobile LCP 5.2 s, INP 410 ms

    The hero image was requested by JavaScript after hydration; a chat widget and three analytics tags blocked the main thread on mid-range Android phones.

  5. A05Medium

    Schema

    No structured data, prices in images

    No HVACBusiness, Service or Offer markup. The price list was a PNG, so 'chemical wash price' queries had nothing to match.

  6. A06Medium

    Local

    Profile linked to the booking subdomain

    The Business Profile pointed at book.kaze… with a different phone format from three directories, splitting local signals.

  7. A07Medium

    Content

    Swapped-name estate templates

    The estate pages that did render were identical apart from the estate name: no block types, travel times or typical unit brands.

03策略The strategy

Four stalls, one queue.

Built on our Signal plan (Technical SEO). Fix what Google receives first, then make it fast, then stop it wasting visits, then describe every page in machine-readable terms.

Stall 01渲染

JavaScript rendering

Service, price and estate pages pre-rendered at build time, so the first byte carries the content.

  • Moved routes from hash to real paths with 301s
  • Static generation for 9 services × 46 estates
  • Booking calendar kept client-side, behind one button
Stall 02速度

Core Web Vitals

The page paints before the booking app wakes up.

  • Hero image preloaded and served as AVIF
  • Bundle split: 1.4 MB → 380 KB on first load
  • Chat and tags deferred until interaction
Stall 03清理

Index bloat

Crawl budget returned to pages a customer could actually land on.

  • Slot and date parameters made non-crawlable
  • Canonicals and noindex on 2,640 calendar URLs
  • Sitemaps split by service, estate and guide
Stall 04标记

Schema markup

Machine-readable facts for every service and area served.

  • HVACBusiness with areaServed per estate
  • Service + Offer with per-unit prices as text
  • FAQ markup on wash, gas top-up and leak pages
04进度Month by month

5 months on the board.

  1. M1

    诊断

    Raw vs rendered

    Crawled the site twice, with and without JavaScript, and diffed every page. Mapped 2,640 slot URLs and 46 hash-routed estates.

    Indexed

    61

  2. M2

    渲染

    Pre-rendering ships

    Real URLs with 301s from the old fragments. Service and estate pages now arrive as complete HTML; the booking app hydrates only the calendar.

    Indexed

    142

  3. M3

    清理

    Crawl budget back

    Calendar parameters blocked and canonicalised. Googlebot's daily fetches shift from empty slots to service pages within three weeks.

    Indexed

    268

  4. M4

    速度

    Speed and schema

    Bundle split, hero preloaded, tags deferred. HVACBusiness, Service and Offer markup on every page; price list rebuilt as text.

    Indexed

    362

  5. M5

    巩固

    Estate hub

    Estate pages rewritten with block types and travel times, linked from a Singapore coverage hub. Profile and directories aligned.

    Indexed

    410

05成果Results

Every number, called to the counter.

叫号 · Now serving · ticket 001

Organic sessions

001 Organic sessions
+147%
002 Online bookings from organic
+92%
003 LCP (mobile)
5.2s → 1.8s

Modelled scenario for a fictional business — not a client result. Rankings and traffic are never guaranteed; see our Terms.

Indexed pages · one LED ≈ 20 pages

38Base61M1142M2268M3362M4410M5

Organic sessions per month

3,400Base3,720M14,980M26,540M37,710M48,398M5

Online bookings from organic

120Base126M1151M2187M3214M4230M5

LCP (mobile), 75th percentile

0s1s2s3s4s5s6s5.2s → 1.8s

Green zone: under 2.5 s counts as good.

Before · month 0

kazeaircon.sg › #

Kaze Aircon | Loading…

You need to enable JavaScript to run this app.

After · month 5

kazeaircon.sg › services › chemical-wash

Aircon Chemical Wash Singapore · from $60/unit | Kaze

Same-week chemical wash for wall-mounted and cassette units across 46 estates. Gas top-up, leak repair and general servicing, priced per unit.

Servicing in TampinesServicing in Jurong WestServicing in BedokServicing in Punggol

Rankings board

Modelled Google positions in Singapore, before the work and at month 5.

Modelled Google positions before and after, month 5
QueryMonthly searchesBeforeMonth 5
aircon servicing singaporeService9,900not indexed#4
aircon chemical washService4,40041#3
aircon gas top up priceService1,900not indexed#2
aircon water leakingService2,40067#5
aircon general servicing priceService1,30028#1
aircon servicing tampinesEstate880not indexed#1
aircon servicing jurong westEstate720not indexed#2
aircon servicing bedokEstate590not indexed#3
aircon repair woodlandsEstate480not indexed#2
condo aircon servicing punggolEstate320not indexed#1

下一位 · Next in line

Get a plan like this

If your site is built in JavaScript and Google is not seeing it, Signal starts with a raw-versus-rendered crawl of every page.

← All case studies