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.
Business Details For Search
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 pagemetaTitle: "Deep cleaning, priced by room" # search result title (optional)description: "A top-to-bottom reset..." # page summarymetaDescription: "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 URLrobots: "" # 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:
| Page | Structured data |
|---|---|
| Every page | Your business (type, name, address, phone, hours, price range, areas served) and the website |
| Service pages | A Service with its provider, the areas where it is offered, and its from-price |
| Area pages | The 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 pages | JobPosting with pay, type, and dates |
| Posts | BlogPosting with the author |
| Inner pages | Breadcrumbs |
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
imageuses 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. imageis the fallback card. Runnpm run generate-og-imageafter you change the brand, colours, or fonts (and after settingbaseUrland[site]) to redraw it in your branding.
Sitemap, Robots, And Hidden Pages
/sitemap-index.xmllists every page. Leave a page out withexcludeFromSitemap: true, or every page in a folder with[seo.sitemap] exclude(it ships withbook/thanks). Draft pages are removed automatically./robots.txtis built from[seo.robotsTxt]. Add paths crawlers must skip todisallow.
These pages ship hidden from search engines (noindex):
| Page | Why |
|---|---|
/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 pages | Not real pages |
| Blog category and tag pages with fewer than 2 posts | Thin 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 aboveEach 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.txtis a Markdown map of your site that some AI tools read;/llms-full.txtholds 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 includedpublic/_headers(Netlify, Cloudflare Pages) andvercel.jsonserve these as text and mark the.mdcopiesnoindex, so search engines don’t index them in place of your pages. - Leave a page out with
excludeFromAi: true. Pages markednoindexand 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" linksBlog 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.
- 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. - Deploy.
- After each deploy, run
npm run indexnow. It sends the pages whose sitemap date changed in the last 7 days.npm run indexnow -- --allsends every page (first launch),--days=30a longer period, and--dry-runlists 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 60characters 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]".