Site settings
settings is the first argument to baseline(). It carries site identity: title, tagline, canonical URL, indexability, languages, and any extra <head> items you want injected on every page.
Settings flow through state.settings and surface in templates as _pageContext.site (and as settings in the data cascade when you keep them in _data/settings.js).
The settings shape
export default {
title: 'Your site',
tagline: 'A short site-wide line.',
description: 'What the site is, for pages that do not say.',
url: 'https://www.example.com',
noindex: false,
defaultLanguage: 'en',
languages: { en: {}, fr: {} },
head: {
link: [],
script: [],
meta: [],
style: []
},
seo: {
preserveQueryParams: false,
ogImage: null,
openGraph: {},
twitter: {}
}
};
await eleventyConfig.addPlugin(baseline(settings));
Every key is optional. Baseline logs a warning if settings.url is missing, and then the head emits no canonical, no Open Graph or Twitter tags and no JSON-LD, rather than guessing an origin. The sitemap is the one exception: it still renders, with relative <loc> values.
A value that is not an absolute http(s) URL is dropped and behaves exactly like a missing one, with the warning naming the value it ignored. A path on the origin is accepted, but see deploying under a subpath before putting one there.
settings.url is the only place Baseline reads a site origin from. It anchors canonicals, sitemap entries, structured data, and the link rewriting HtmlBasePlugin does in your pages. Keep it origin-only and put subpaths in pathPrefix. Which environment variable it comes from is your choice, since nothing in the plugin reads the environment for it.
Keys
| Key | Type | What it does |
|---|---|---|
title |
string |
Site title. Used by the head module to compose <title> (Page title – Site title) and by the navigator template. |
tagline |
string |
Short site-wide line, used as the home page's title suffix. It is not a description fallback; description is. |
description |
string |
Site-wide description. The last rung of the <meta name="description"> chain, used when a page supplies no seo.description, description or excerpt of its own. |
url |
string |
Absolute site URL including protocol. Resolves canonical URLs, sitemap entries, Open Graph tags and the JSON-LD graph. Baseline warns at startup if it is missing, and warns again naming the value if it is not an absolute http(s) URL. The image shortcode does not read it: it emits root-relative /media/ paths, and making one absolute is yours to do. |
noindex |
boolean |
Default false. When true, every page renders <meta name="robots" content="noindex, nofollow">, canonicals are dropped, and the sitemap renders with no entries in it. Useful for staging. A page can also set noindex: true in its own front matter. |
defaultLocale |
string |
BCP 47 default locale ('en-US'), and the preferred key. Sets the site's default language and the fallback page.locale for pages whose language has no locale of its own. This or defaultLanguage is required for multilingual activation. |
defaultLanguage |
string |
Short language code ('en'), a writer-side alias for defaultLocale: set it when you do not need a region and Baseline derives the locale from it. Either key serves every consumer. All of them assume default-language URLs are unprefixed and every other language sits under /<lang>/; writing those permalinks is still yours. |
The languages map
object or string[]. Object form { en: {}, fr: {} }, or array shorthand ['en', 'fr'] which is normalised to objects with empty entries. Invalid entries are dropped, and logged when verbose: true. Required for multilingual activation.
Baseline reads six keys off an entry:
| Key | What it does |
|---|---|
title, tagline, description |
Per-language overrides of the site-level values, read into _pageContext.site for pages in that language. |
locale |
The BCP 47 tag ('en-US') that becomes page.locale for pages in that language. |
languageName |
The language's name in its own language. Becomes the label on each entry in page.translations and in collections.translationsMap, which is what a language switcher renders. Falls back to the language code. |
homeLabel |
The label for the first breadcrumb crumb in that language. Falls back to Home. |
export default {
languages: {
en: { title: 'Your site', tagline: 'A short line.', locale: 'en-US', languageName: 'English', homeLabel: 'Home' },
fr: { title: 'Votre site', tagline: 'Une ligne courte.', locale: 'fr-FR', languageName: 'Français', homeLabel: 'Accueil' }
}
};
Anything else you put on an entry rides through untouched and is yours to read as settings.languages[lang]. contentDir, the folder that language's content sits in, is the convention worth knowing: the plugin never reads it, and it exists so a switcher, or anything else that needs to find a language's content, has one place to ask.
The head extras
object. Additive <head> entries injected on every page. Four arrays, one per element type, and each entry is an object whose keys become attributes on the rendered tag.
export default {
head: {
link: [
{ rel: 'stylesheet', href: '/assets/css/index.css' },
{ rel: 'icon', href: '/favicon.svg' }
],
script: [{ src: '/assets/js/index.js', defer: true }],
meta: [{ name: 'theme-color', content: '#0a0a0a' }],
style: []
}
};
Pages can add their own entries through the same shape in front matter; Baseline merges and deduplicates. See the head module for the merge rules.
This is also where you wire the asset pipeline into your pages. Baseline compiles
assets/css/index.cssandassets/js/index.jsto/assets/css/index.cssand/assets/js/index.jsat the site root, but it does not auto-inject them. Declare alinkand ascriptentry as above. The default docs site does exactly this.
Output filenames are unhashed today. Cache-busting via hashed filenames is on the roadmap, and the entrypoint declaration shape may change to accommodate it.
In practice link, script and meta carry almost everything you need. style is supported for parity but rarely used; inline <style> blocks usually belong in your CSS pipeline rather than the head extras list.
The seo defaults
object. Site-wide SEO defaults, feeding the resolved seo namespace and the payload the head module emits: canonical, Open Graph, Twitter Card, JSON-LD. All optional.
export default {
seo: {
// keep the query string on canonical URLs (default: strip it)
preserveQueryParams: false,
// default share card; the url must be absolute (it lands in the JSON-LD graph)
ogImage: { url: 'https://www.example.com/og.jpg', width: 1200, height: 630, alt: '' },
// default Open Graph and Twitter values, overridable per page
openGraph: { type: 'website' },
twitter: { card: 'summary_large_image', site: '@yoursite', creator: '@you' }
}
};
Four keys live inside those last two objects:
| Key | Default | What it does |
|---|---|---|
openGraph.type |
website |
The og:type for pages that are not articles. A page with type: article projects article regardless. |
twitter.card |
summary_large_image |
The twitter:card for every page. |
twitter.site |
none | The site's handle. Overridable per page with seo.twitterSite. |
twitter.creator |
none | The account the whole site is credited to. No per-page override, so it is site-wide by design. |
The share image default lives here, in settings.seo, because it is page presentation: the same shelf as og:type and twitter:card. Identity images, an organisation logo or a person's photo, live in the schema cascade key instead, because they are properties of the entity.
This default is a social fallback and stays one. It fills og:image and twitter:image on every page, and it does not become the page's primaryImageOfPage in the graph, which would be a claim that a single site-wide card is the image representing each page. Set ogImage on a page and that page gets both. A site-wide card alone gets you the share image and no graph claim.
Keep the url absolute. It lands in the JSON-LD graph, inside a <script> that Eleventy's HtmlBasePlugin never walks, so a root-relative path ships exactly as you wrote it while the @id beside it is absolute. Baseline warns once per relative url rather than rewriting it.
A page that should carry no share image at all sets ogImage: false. That declines it outright and does not inherit the site default.
SEO front matter
Beyond settings.seo, a page carries its own SEO through front-matter keys. Baseline reads them into the resolved SEO values and the JSON-LD graph the head module emits. Each is optional; a page with none still gets a complete, valid graph from the site defaults.
| Key | Type | What it does |
|---|---|---|
ogImage |
string, object or false |
Per-page share card, and the only source of the graph's primaryImageOfPage. Overrides settings.seo.ogImage for og:image. Object form { url, width, height, alt } lets the graph emit a sized ImageObject; a bare string emits a url-only og:image and no node. false declines a share image on this page, so nothing is emitted and the site default is not inherited. |
canonical |
string |
Explicit canonical URL. Without it the canonical is the page's own absolute URL. |
preserveQueryParams |
boolean |
Keep the query string on this page's canonical. Overrides the site default. The fragment is always stripped. |
pageType |
string |
Overrides the WebPage node's schema.org @type (e.g. AboutPage, CollectionPage). |
articleType |
string |
Overrides the Article node's @type (e.g. BlogPosting). Takes effect only when the page is an article (type: article). |
topics |
string or string[] |
Keyword classification. Emitted as schema.org keywords on the WebPage and Article nodes, and as og:article:tag on article pages. Separate from Eleventy's native tags, which Baseline leaves alone. Bring your own taxonomy: if you want topic-listing pages, build a collection from topics yourself. |
sectionLabel |
string |
Display name for the page's section. On an article page it becomes schema.org articleSection and og:article:section; elsewhere nothing reads it. Usually set once in a directory data file rather than per page. |
datePublished |
string |
Publish date for the structured data. Defaults to the page's date. Sets datePublished on the WebPage and Article nodes. |
dateModified |
string |
Last-modified date for the structured data. Resolves front matter first, then the git last-commit date, then the publish date. Sets dateModified. |
Under a seo: key
The keys above sit at the top level of the front matter. A second set sits inside a seo: object, and those are the ones that overrule a specific tag rather than describe the page:
---
title: 'About us'
seo:
ogTitle: 'The people behind the thing'
twitterCard: 'summary'
---
| Key | Type | What it does |
|---|---|---|
seo.title |
string |
Overrules the page title everywhere the title is composed, <title> and the graph included. title in front matter is the usual place to set this. |
seo.description |
string |
The same for the description. First rung of the <meta name="description"> chain. |
seo.ogTitle |
string |
og:title alone, leaving <title> and the graph as they were. |
seo.ogDescription |
string |
og:description alone. |
seo.ogType |
string |
og:type for this page, over everything else, including the article that a page with type: article would otherwise project. |
seo.twitterCard |
string |
twitter:card. Defaults to summary_large_image from settings.seo.twitter.card. |
seo.twitterSite |
string |
twitter:site for this page, over the site-wide handle. |
seo.twitterTitle |
string |
twitter:title, emitted only when it differs from the Open Graph title. Twitter reads the OG tags otherwise, so a duplicate is noise. |
seo.twitterDescription |
string |
twitter:description, on the same terms. |
seo.twitterImage |
string |
twitter:image, on the same terms. |
seo.canonical |
string |
The seo: spelling of canonical above. Either place works, and this one wins. |
seo.noindex |
boolean |
The seo: spelling of noindex. Either place works, and this one wins, including when it sets false against a bare noindex: true inherited from a directory data file. |
seo.ogImage |
string |
The seo: spelling of ogImage. Here the top-level one wins, and only this one takes a bare url, so ogImage is the better place for both. |
The Twitter three are deliberately quiet: Twitter falls back to the Open Graph tags when a twitter: equivalent is missing, so Baseline emits one only where you have said something different.
Identity authoring, your organisation and author profile plus any extra schema.org nodes, lives in the schema cascade key rather than per-page front matter.
Multilingual activation
Three things must be set together: options.multilingual: true, settings.defaultLocale or settings.defaultLanguage, and a non-empty settings.languages. Nothing is inferred. If any of the three is missing, the multilang module skips activation and logs why.
See the multilang module for what activation enables (per-language collections, hreflang, translation filters).
Where settings surface
| Surface | Reads |
|---|---|
_pageContext.site |
title, tagline, description, url, noindex (per-language overrides if present) |
_pageContext.meta |
composed <title>, default description from description, robots from noindex |
| Head module | head.{link, script, meta, style} extras |
seo namespace |
url; seo defaults: canonical policy, share image, Open Graph and Twitter values |
| Sitemap module | url, noindex (empty sitemap when true); defaultLanguage and languages for per-lang sitemaps |
| Multilang module | defaultLocale or defaultLanguage, languages |
| Breadcrumbs | languages[lang].homeLabel for the first crumb |
See also
- Plugin entrypoint - the
optionsargument and what Baseline registers. - Page context - how settings are normalised per page.
- Head module - head extras merge rules.
- Multilang module - activation and per-language behaviour.