Config Reference
File: src/config/config.toml · Skill level: Beginner
config.toml holds every site-wide setting: your business details, the header and footer layouts, the forms, the estimator and booking rules, SEO, and more. It is grouped into tables: a line in [brackets] starts a table, and every key = value line below it belongs to that table until the next one. Most keys have a comment beside them; this page explains them all in one place, with the value Homio ships.
Values marked # sample or PLACEHOLDER in the file must be replaced before you launch.
Site Identity
Table: [site]
| Key | Ships as | What it does |
|---|---|---|
title | "Homio" | Brand name. {brand} in the footer copyright, structured data, social cards, and the business name when [business] name is empty. The home page title comes from its own metaTitle |
description | A theme description | Default description for search results and social previews |
tagline | A theme tagline | Added to every page title (“Pricing - your tagline”) and social titles. Replace it with a short line about your business. A page turns it off with disableTagline: true |
taglineSeparator | "" | Text between page title and tagline. Empty uses " - " |
baseUrl | "https://example.com" | Placeholder. Your live address. Canonical links, the sitemap, robots.txt, RSS feeds, llms.txt, and social URLs are built from it, and npm run build warns while it is example.com. A SITE_URL environment variable on the build overrides it |
logo | "" | Path to a logo image that replaces the built-in mark (SVG or PNG). Empty draws the mark from src/lib/brandMark.ts |
logoText | "homio" | Text next to the mark, shown exactly as typed |
logoWidth, logoHeight | "28px" | Logo image size. Used only when logo is set |
trailingSlash | true | URLs end with / (/pricing/). Restart the dev server after a change |
[site.favicon] path | "/images/favicons" | Where the generated favicons go. Leave it as it is |
[site.favicon] image | "/images/favicon.svg" | Source image for npm run generate-favicons (src/assets/images/favicon.svg) |
Details: Site Identity & Logo.
Business Details
Tables: [business], [business.address], [business.geo], [business.hours]
One source for your company name, phone, email, address, and opening hours. The header, the mobile action bar, the footer, the contact page, the area pages, the booking form, the structured data, and llms.txt all read them from here.
| Key | Ships as | What it does |
|---|---|---|
name | "" | Business name. Empty uses site.title |
legalName | "" | Optional legal name for structured data |
type | "LocalBusiness" | The schema.org business type |
phone | "+15550100199" (sample) | Phone for call links, in E.164 format. Empty hides every call link |
phoneDisplay | "(555) 010-0199" (sample) | How the phone is written on the page |
sms, smsDisplay | sample numbers | Optional number for “Text” links. Empty hides them |
email | "[email protected]" | Placeholder. Your email |
priceRange | "$$" | Shown in structured data |
timezone | "America/New_York" | Time zone for “Open now” and booking dates |
closedDates | two sample holidays | Days with no bookings (YYYY-MM-DD) |
[business.address] | sample address | show, street, locality, region, postalCode, country. show = false hides the street and postal code everywhere |
[business.geo] | empty | Optional latitude and longitude |
[business.hours] | sample hours | mon to sun, 24-hour "HH:MM-HH:MM". Empty is closed |
Every key, its format, and where it appears: Business Info.
Search Engines
Tables: [seo], [seo.schema], [seo.robotsTxt], [seo.ai], [seo.sitemap]
| Key | Ships as | What it does |
|---|---|---|
author | "" | Default author meta tag. Empty uses the business name |
keywords | 8 samples | Default keywords for pages without their own keywords. Use your services and towns |
robots | "index, follow, max-snippet:-1, ..." | Default robots meta tag. Lets search and AI answers quote pages and show images in full |
[seo.schema] enable | true | Structured data (JSON-LD) on every page: business, website, page, and services, areas, and prices where they apply |
[seo.schema] faqPage | true | FAQ markup on /faq/ |
[seo.robotsTxt] enable | true | Builds /robots.txt |
[seo.robotsTxt] disallow | [] | Paths crawlers must skip |
[seo.robotsTxt] aiSearch | true | false asks AI answer engines (ChatGPT search, Claude, Perplexity, and others) not to fetch your pages |
[seo.robotsTxt] aiTraining | true | false asks crawlers that collect content to train AI models to stay away |
[seo.robotsTxt] contentSignals | false | true adds a Content-Signal line with the two choices above |
[seo.ai] llmsTxt | true | Builds /llms.txt and /llms-full.txt per language, with your business facts first |
[seo.ai] markdownPages | true | A Markdown copy of each service, area, project, post, job, pricing, FAQ, and guarantee page at <page>.md |
[seo.ai] indexNowKey | "" | An 8 to 128 character key turns on IndexNow; run npm run indexnow after each deploy |
[seo.sitemap] enable | true | Builds /sitemap-index.xml |
[seo.sitemap] exclude | ["book/thanks"] | Folder names to leave out of the sitemap |
Details: SEO & AI Visibility.
General Settings
Table: [settings]
| Key | Ships as | What it does |
|---|---|---|
blogFolder | "blog" | Folder name of the blog. Changing it also means renaming the folders in src/content/ and src/pages/[...lang]/ and the collection in src/content.config.ts. Most sites keep it |
servicesFolder | "services" | The same, for services |
blogPagination | 6 | Posts per blog list page |
pagination | 8 | Items per page on other paginated lists |
stickyHeader | true | Keeps the header pinned while scrolling (the announcement bar scrolls away) |
contactFormAction | "https://formsubmit.co/[email protected]" | Placeholder. Where the contact form (and, by default, the booking form) sends its data. npm run dev and build warn until you change it |
contactFormProvider | "formsubmit.co" | "formsubmit.co", "formspree", or "netlify" |
footerDescription | "" | Footer description. Empty uses footer.description from src/i18n/<language>.json |
Details: Forms.
Header And Footer Layouts
Table: [settings.layout]
| Key | Ships as | Values |
|---|---|---|
header | "default" | default, simple, centered, utility |
footer | "default" | default, minimal, large, columns |
A page’s headerLayout or footerLayout frontmatter wins. See Header & Mobile Bar and Footer & CTA Band.
Smooth Scroll
Table: [settings.smoothScroll]
Smooth scrolling powered by Lenis. It ships off (enable = false): the site uses the browser’s normal scrolling. respectReducedMotion keeps it off for visitors who ask for less motion; duration (1.1, higher feels slower) and easing (expo, linear, easeOutQuad, easeOutCubic) set the feel. Leave smoothWheel, syncTouch, wheelMultiplier, touchMultiplier, anchors, autoRaf, and stopInertiaOnNavigate as they are unless you know Lenis.
Light And Dark Mode
Table: [settings.colorScheme]
| Key | Ships as | What it does |
|---|---|---|
default | "system" | system (follows the visitor’s device), light, or dark, until a visitor picks one |
switcher | true | Shows the light/dark button in the header, the mobile menu, and the footer |
Details: Colors & Fonts.
Cookie Banner
Table: [settings.cookieConsent]
| Key | Ships as | What it does |
|---|---|---|
enable | true | Shows the cookie banner and holds data-consent scripts until visitors choose |
revision | 1 | Raise it by 1 when your cookie use changes, to ask every visitor again |
The banner’s copy is in src/content/sections/english/cookie-consent.md (and french/). If you turn the banner off, also set enable to false on the “Cookie settings” item in the Legal column of the footer menus. Details: Cookies & Analytics.
Page Actions And AI Links
Table: [settings.pageActions]
| Key | Ships as | What it does |
|---|---|---|
enable | true | The Copy page menu on blog posts: copy as Markdown, open the Markdown copy, or ask an assistant. Needs [seo.ai] markdownPages |
footer | false | A footer row with AI links and llms.txt on every page. Off for most local businesses |
providers | ["chatgpt", "claude", "perplexity", "grok"] | Assistants offered in “Ask”. Empty hides the “Ask” links |
Animations
Table: [settings.animation]
Content fades in with a small rise as it scrolls into view, once.
| Key | Ships as | What it does |
|---|---|---|
enable | true | Turns the reveal animations on or off |
respectReducedMotion | true | Shows content without animation for visitors who ask for less motion |
easing | "cubic-bezier(0.215, 0.61, 0.355, 1)" | The animation curve |
duration | "600ms" | Reveal duration |
distance | "0.75rem" | How far content rises while fading in |
rootMargin, threshold | "0px 0px -8% 0px", 0.14 | How early an element starts animating |
Details: Animations.
Estimator
Table: [settings.estimator]
The instant estimator on the pricing page, the service pages, and the estimate sections. Rates, discounts, and add-ons are not here: they are in src/content/pricing/.
| Key | Ships as | What it does |
|---|---|---|
enable | true | Turns the estimator on |
currency | "USD" | ISO 4217 currency code, one for every language |
currencyDisplay | "symbol" | symbol ($ or $US), narrowSymbol ($ in every language), or code (USD) |
bookUrl | "/book/" | Where “Book this visit” goes, with the selection in the address |
ticketPrefix | "EST" | Code on the estimate ticket, for example EST-4K7Q2 |
animateTotal | true | Counts between totals. Always off for visitors who ask for less motion |
Details: Pricing & Estimator.
Booking Request Form
Table: [settings.booking]
The form on /book/. It posts through the same provider as the contact form (contactFormProvider); its copy is in src/content/sections/english/booking-section.md.
| Key | Ships as | What it does |
|---|---|---|
enable | true | Turns the booking form on |
formAction | "" | Endpoint for booking requests. Empty uses contactFormAction. Set it to send bookings to another inbox or form |
thankYouUrl | "/book/thanks/" | Where the form lands when JavaScript is off |
minLeadDays | 1 | Earliest date: today plus this many days |
maxDays | 60 | Latest date: today plus this many days |
zipPattern | "^\\d{5}$" | Postal code check (a regular expression). UK example: "^[A-Za-z0-9 ]{5,8}$" |
checkArea | true | Tells visitors whether their ZIP code is in one of your service areas |
Details: Booking Request.
Layout Switcher (Demo Only)
Table: [settings.layoutSwitcher]
enable = true adds a floating Change layout button that previews every layout of the section in view. It is for the theme’s demo site. Keep enable = false on your own site: the pages then render only the layouts chosen in your content files. To change how a section looks, set options.layout: see Section Layouts.
Copyright
Table: [settings.copyright]
enable (true) shows the copyright line in the footer. text ("") overrides footer.copyright from the i18n files for every language. In both, {year} becomes the year of the build and {brand} the site.title.
Newsletter
Table: [settings.subscription]
The Mailchimp signup used by the newsletter section on the blog, the default and large footers, and the CTA band’s newsletter layout.
| Key | Ships as | What it does |
|---|---|---|
enable | true | false hides every signup form and the newsletter section |
title | "" | Empty uses subscription.title from the i18n files |
note | "" | The consent line under the field. Empty uses subscription.note |
formAction | a placeholder URL | Placeholder. Your Mailchimp embedded form action. Change each & in the copied URL to &. npm run dev shows a warning inside each form until it is set |
mailchimpTagValue | "" | Optional Mailchimp tag id added to every signup |
Languages
Table: [settings.multilingual]
| Key | Ships as | What it does |
|---|---|---|
enable | true | false builds only the default language. Then run npm run remove-multilingual to delete the French files |
defaultLanguage | "en" | Language served without a prefix |
disableLanguages | [""] | Languages to skip, for example ["fr"] |
showDefaultLangInUrl | false | true adds the prefix to the default language too (/en/pricing/) |
Restart the dev server after any change here. Details: Internationalization.
Header Button
Table: [settings.navigationButton]
The main button at the right of the header. The mobile action bar uses the same button.
| Key | Ships as | What it does |
|---|---|---|
enable | true | Shows the button |
label | "" | Empty uses navigation.buttonLabel from the i18n files (“Get a price”). A value here is used in every language |
url | "/book/" | Where it goes. The button is hidden on that page itself |
Mobile Action Bar
Table: [settings.mobileActionBar]
The bar fixed to the bottom of the screen on phones (below 768 px), with Call and the header’s main button.
| Key | Ships as | What it does |
|---|---|---|
enable | true | Shows the bar |
showCall | true | The Call button, with [business] phone. Hidden when the phone is empty |
showText | false | A Text button. Needs [business] sms |
hideOn | ["/book/", "/contact/"] | Path prefixes where the bar is left out (without the language prefix) |
hideNearFooter | true | The bar slides away while the footer is visible |
Details: Header & Mobile Bar.
Secondary Header Link
Table: [settings.navigationSecondaryLink]
A text link before the header button, in every header layout from 1320 px wide. It ships off (enable = false). label ("") uses navigation.secondaryLabel (“Contact”); url is "/contact/".
Code Blocks And Tables Of Contents
Tables: [settings.markup.codeBlocks], [settings.markup.tableOfContents]
[settings.markup.codeBlocks] theme:auto(follows light and dark mode, the default) ordark(a dark code window in both modes), for fenced code in Markdown.[settings.markup.tableOfContents]: the “On this page” list on blog posts and text pages such as the privacy policy. It ships withenable = true,startLevel = 1,endLevel = 5,ordered = false, andexpanded = false.
Social Previews
Table: [opengraph]
| Key | Ships as | What it does |
|---|---|---|
image | "/images/og-image.jpg" | Shared preview image (public/images/og-image.jpg), used when a page has no image and no generated card |
generateImages | true | During npm run build, renders a 1200×630 card (logo, section, title, description) for every page without an image |
theme | "light" | light or dark palette for the generated cards and npm run generate-og-image |
ogLocale | "en_US" | Fallback locale. Each language sets its own ogLocale in src/config/language.json |
ogType | "website" | Open Graph type |
twitter, twitterCard | "", "summary_large_image" | X (Twitter) handle and card type |
Details: Site Identity & Logo.
Custom Head Scripts
Table: [head]
content is pasted into the <head> of every page. Use it for analytics, verification tags, or extra stylesheets. With the cookie banner on, change each tracking <script> to <script type="text/plain" data-consent="analytics"> (or "marketing") so it waits for consent; the comment above [head] shows an example. See Cookies & Analytics.
The Other Files Insrc/config/
| File | What it holds | Page |
|---|---|---|
menu.en.json, menu.fr.json | The header menu (with the Services and Areas dropdowns) and the footer link columns | Menus |
language.json | The languages: name, code, content folder, order, and ogLocale | Internationalization |
fonts.json | The three fonts: body, headings, and numbers | Colors & Fonts |
social.json | Social links and their icons (main list) | Footer & CTA Band |
Colors are not in src/config/: they are tokens in src/styles/theme.css.
Do It With AI
Explain each table of src/config/config.toml in one line, then list every value marked"# sample" or "PLACEHOLDER" that I must replace before launch, and the current value of each.Don't change anything yet.