Skip to content
New: Customize your theme with an AI assistant. Read the guide

SEO & AI Visibility

Files: src/config/config.toml, page frontmatter · Skill level: Beginner

Homio does the technical SEO for you: page titles and descriptions, canonical links, a sitemap, robots.txt, structured data for a local business, social preview cards, RSS, and files for AI assistants. Your job is to set your address and business details, and to write good titles and descriptions.

First, Set Your Address

[site]
baseUrl = "https://www.your-domain.com" # ships as "https://example.com"

Canonical links, the sitemap, robots.txt, RSS feeds, llms.txt, structured data, and social preview URLs all use it. Until you change it, npm run build prints a yellow [site] warning. Don’t deploy while you see it: every page would point search engines at example.com.

A SITE_URL environment variable on the build takes priority over baseUrl. On Vercel, the included vercel.sh sets one for you. npm run generate-og-image reads only baseUrl.

Search engines and map results read your business from [business] in config.toml: the name, phone, email, address, opening hours, closed dates, and type. Set them once there (see Business Info); every page and the structured data use them.

[business]
type = "LocalBusiness" # LocalBusiness | HomeAndConstructionBusiness | GeneralContractor | HousePainter | Plumber | Electrician | HVACBusiness | RoofingContractor | Locksmith | MovingCompany | ProfessionalService
priceRange = "$$"
[business.address]
show = true # false = service-area business: street and postal code are hidden everywhere
[business.geo] # Optional, improves map results; leave empty if unsure
latitude = ""
longitude = ""

House cleaning has no schema.org type of its own: LocalBusiness is the right choice for it. A handyman or trade business can pick a narrower type from the list. If you serve customers at their homes and don’t want your street shown, set show = false: your service areas then describe where you work.

Titles And Descriptions

Every page and entry accepts these frontmatter fields:

---
title: "Deep clean" # shown on the page
metaTitle: "Deep cleaning, priced by room" # search result title (optional)
description: "A top-to-bottom reset..." # page summary
metaDescription: "Deep cleaning with a room-by-room checklist..." # 140–160 characters (optional)
image: "/images/services/deep-clean.jpg" # social preview image (optional)
canonical: "" # only if the page is a copy of another URL
robots: "" # for example "noindex, follow"
keywords: ["deep cleaning", "house cleaning North Harbor"]
---

Titles get your tagline added: “Deep cleaning, priced by room - Your tagline”. Set it in [site]:

[site]
title = "Homio" # your brand name
description = "..." # the default meta description
tagline = "Cleaning & Home Services Astro & Tailwind CSS Theme" # replace with a short line about your business
taglineSeparator = "" # default is " - "

disableTagline: true turns the tagline off for one page. [seo] author and keywords are the site-wide defaults; [seo] robots ships with max-snippet:-1, max-image-preview:large so search and AI answers can quote your pages and show their images in full. Replace the theme’s sample title, tagline, and description before launch.

Structured Data (JSON-LD)

[seo.schema]
enable = true # JSON-LD on every page: business, website, page, plus services, areas, and prices where they apply
faqPage = true # FAQPage markup on /faq/

Homio builds it from your content, so it always matches what visitors see:

PageStructured data
Every pageYour business (type, name, address, phone, hours, price range, areas served) and the website
Service pagesA Service with its provider, the areas where it is offered, and its from-price
Area pagesThe area with its region and postal codes; with a real office, a branch with its own address and phone
/areas/A list of your area pages
/pricing/An offer per priced plan and a catalog per trade of the rate sheet
/book/A booking action on your business
/faq/FAQPage with every question
Job pagesJobPosting with pay, type, and dates
PostsBlogPosting with the author
Inner pagesBreadcrumbs

Keep the data true: the job posting data is for real openings only, and the sample areas and prices must be replaced before launch. After launch, test a page with Google’s Rich Results Test.

Social Preview Cards

[opengraph]
image = "/images/og-image.jpg" # fallback image, public/images/og-image.jpg
generateImages = true # a 1200×630 card for every page without its own image
theme = "light" # light | dark
twitter = "" # your X (Twitter) handle
  • A page with its own image uses that photo.
  • Every other page gets a card generated during npm run build, with your logo, the section, the page title, and the description, in your fonts and colours.
  • image is the fallback card. Run npm run generate-og-image after you change the brand, colours, or fonts (and after setting baseUrl and [site]) to redraw it in your branding.

Sitemap, Robots, And Hidden Pages

  • /sitemap-index.xml lists every page. Leave a page out with excludeFromSitemap: true, or every page in a folder with [seo.sitemap] exclude (it ships with book/thanks). Draft pages are removed automatically.
  • /robots.txt is built from [seo.robotsTxt]. Add paths crawlers must skip to disallow.

These pages ship hidden from search engines (noindex):

PageWhy
/home-cleaning/, /home-commercial/, /home-handyman/Demo homepages. Remove robots and excludeFromSitemap from the one you make your home
/elements/The layout gallery, a reference for you
/book/thanks/The thank-you page after a booking without JavaScript
404 pagesNot real pages
Blog category and tag pages with fewer than 2 postsThin pages

AI Search And AI Assistants

People ask AI assistants “who cleans homes near me”. Homio lets them read and cite your site, and gives you control over each kind of AI crawler.

1. Choose which AI crawlers may visit

[seo.robotsTxt]
aiSearch = true # AI search and answer engines that fetch pages to cite them (ChatGPT search, Claude, Perplexity, ...)
aiTraining = true # crawlers that collect content to train AI models (GPTBot, ClaudeBot, Google-Extended, CCBot, ...)
contentSignals = false # true adds a Content-Signal line (contentsignals.org) with the two choices above

Each switch writes robots.txt rules for a list of crawlers in src/lib/utils/aiCrawlers.ts. aiSearch = false removes your site from those engines’ answers; aiTraining = false opts out of training only. Neither affects Google Search or AI Overviews, which use the normal Googlebot and follow your regular search rules. contentSignals ships off because Lighthouse doesn’t know the line yet and lowers the SEO score.

2. llms.txt and Markdown copies

[seo.ai]
llmsTxt = true # /llms.txt (index) and /llms-full.txt (all text), per language
markdownPages = true # a Markdown copy of each service, area, project, post, job, pricing, FAQ, and guarantee page
  • /llms.txt is a Markdown map of your site that some AI tools read; /llms-full.txt holds the text of every page. Both open with your business facts from [business], then your services with prices and checklists, prices, areas with ZIP codes, and pages. Each language gets its own (/fr/llms.txt).
  • With markdownPages, a service at /services/deep-cleaning/ is also served as /services/deep-cleaning.md. The included public/_headers (Netlify, Cloudflare Pages) and vercel.json serve these as text and mark the .md copies noindex, so search engines don’t index them in place of your pages.
  • Leave a page out with excludeFromAi: true. Pages marked noindex and drafts are left out automatically.

Search engines don’t document using llms.txt, so treat it as a convenience for AI tools, not as an SEO setting.

3. The Copy page and Ask links on posts

[settings.pageActions]
enable = true
footer = false # a footer row with AI links and llms.txt on every page (off for most local businesses)
providers = ["chatgpt", "claude", "perplexity", "grok"] # empty hides the "Ask" links

Blog posts get a Copy page menu: copy the post as Markdown, open the Markdown copy, or ask an assistant about it.

IndexNow (Bing And Others)

IndexNow tells Bing, Yandex, Seznam, and Naver which pages changed, so they update faster. Bing also feeds ChatGPT search and Copilot. It ships off.

  1. Set a key of 8 to 128 letters, numbers, or dashes in config.toml: indexNowKey = "your-key" under [seo.ai]. The build publishes it at /<key>.txt.
  2. Deploy.
  3. After each deploy, run npm run indexnow. It sends the pages whose sitemap date changed in the last 7 days. npm run indexnow -- --all sends every page (first launch), --days=30 a longer period, and --dry-run lists the pages without sending.

Do It With AI

Improve SEO for my business. For every content file with a title in src/content/**/english/,
write a metaDescription of 140–160 characters from the page content, and a metaTitle under 60
characters where the title is vague. Don't change other fields. Then do the same in French. Set
[site] title, tagline, and description, and [seo] keywords in src/config/config.toml for
[BUSINESS, SERVICES, TOWNS], and check that [site] baseUrl is "[MY URL]".
Type something to search...