跳到正文
DEV Community· kabariyashyam10-ui·· 3 小時前AI 評分48

如何使用 Next.js App Router 建立非門頁式本地登陸頁面

Building Local Landing Pages That Are Not Doorway Pages (Next.js App Router)

AI 導讀

文章分享如何使用 Next.js App Router 為 AI 接待員產品建立有實用本地資訊、而非只替換城市名稱的登陸頁面。做法包括按城市儲存本地資料、以單一動態路由靜態生成頁面,並讓結構化資料符合頁面內容及實際營業地點。作者建議只建立有具體內容的城市頁面,六個有實質資訊的頁面勝過六十個單薄頁面。

正文

If you've ever built a site for a local business, you've probably been asked for "a page for every city." The lazy version — one template with the city name swapped — is exactly what Google calls a doorway page, and it tends to get ignored or demoted.

I recently built city pages for an AI receptionist product in Canada, and tried to make each one genuinely useful. Here's the approach, with Next.js (App Router) code.

1. Model the local facts, not just the city name

The difference between a doorway page and a useful local page is information a visitor in that city actually needs. So the data model holds real local detail:

export const CITIES = [
  {
    slug: 'edmonton',
    city: 'Edmonton',
    province: 'Alberta',
    areaCodes: ['780', '587', '825', '368'],
    timeZone: 'Mountain Time',
    intro: 'Long winters, early dark and a phone that rings hardest when you are busiest…',
    pressures: [
      { h: 'Furnace and frozen-pipe season', p: 'No-heat calls can come at any hour from October to April…' },
    ],
    nearby: ['St. Albert', 'Sherwood Park', 'Spruce Grove', 'Leduc'],
    faqs: [
      { q: 'Does it work with my 780 number?', a: 'Yes — you keep your existing number…' },
    ],
  },
  // …each city has its own area codes, time zone, local problems and FAQs
];

Every field changes the page in a way a local reader would notice: the right area codes (including new overlays), the right time zone, problems specific to the local climate and economy, and questions people there actually ask.

2. One dynamic route, statically generated

// app/ai-receptionist/[city]/page.js
export function generateStaticParams() {
  return CITIES.map((c) => ({ city: c.slug }));
}

export async function generateMetadata({ params }) {
  const { city } = await params; // params is a Promise in recent Next.js
  const c = cityBySlug(city);
  return {
    title: c.seoTitle || `AI Receptionist in ${c.city} | Brand`,
    description: c.seoDescription || `…${c.areaCodes[0]} number…`,
    alternates: { canonical: `/ai-receptionist/${c.slug}` },
  };
}

Nesting the pages under the main service (/ai-receptionist/edmonton) tells search engines they're local depth for one topic, not a pile of unrelated pages.

Note the optional seoTitle: for the one city you want to rank first, match the exact query people type ("AI Receptionist Edmonton") rather than a generic pattern.

3. Structured data that matches what's on the page

Each page outputs a Service with areaServed set to the city. The home-city page — where the business is actually located — also outputs LocalBusiness with the real address and hours. Don't claim a physical presence in cities where you don't have one; only the genuine location gets that markup.

4. Show proof of being local, where it's true

On the home-city page I added a block with the real phone number, hours, a link to the Google Business Profile and the surrounding towns served. You can see it on the Edmonton AI receptionist page. Other cities get honest wording instead ("setup is done by phone or video").

5. Link the pages like a real site would

  • The main service page links to every city.
  • Each city page links back to the main page and to the other cities.
  • Related pages (local SEO, web design for the same city) link to the city page with descriptive anchors.

6. Keep the set small

Six cities with real content beat sixty thin ones. Add a city only when you can write something specific about it.

Takeaways

  • Store local facts in data; let the template render them.
  • Statically generate, nest under the parent topic, and canonicalize.
  • Match structured data to the visible page — and to reality.
  • Quality over quantity: a page should pass the test "would a local person find this useful?"

Happy to answer questions about the implementation in the comments.

來源:DEV Community · dev.to