SEO Optimization
Control titles, descriptions, and meta tags for search engines and social previews. Jamdesk auto-generates sitemaps and Open Graph images.
Set titles, descriptions, and metadata in frontmatter to control how your docs show up in search engines and social previews.
What Jamdesk Does Automatically
Optimizing Your Content
Write Effective Frontmatter
---
title: User Authentication # Under 60 characters
description: Set up OAuth, JWT, and session-based authentication # 120-160 characters
---
Front-load keywords. "Authentication setup" is better than "How to set up authentication."
Page Titles
- Keep under 60 characters, counting the site name your title format adds. Longer titles get truncated in search results
- Include your primary keyword near the beginning
- Make each title unique across your docs
By default, the browser tab and search result title is Page title — Site name. Change it with seo.titleTemplate in docs.json. Put %s where the page title goes:
{
"seo": {
"titleTemplate": "%s | Acme"
}
}
A page titled Getting started then shows Getting started | Acme. Use "%s" alone to leave the site name out. A template without %s fails the build. jamdesk validate shows the error. Link previews (og:title and twitter:title) still show the page title alone.
Some pages skip the template. A page with no title shows your name followed by "Documentation", such as Acme Documentation, or only your name if it already ends in "Docs" or "Documentation". A page whose title is the same as your name shows that title alone.
jamdesk validate and jamdesk dev accept seo.titleTemplate from CLI version 1.1.220. Upgrade with npm i -g jamdesk@latest.
Descriptions
- Aim for 120-160 characters
- Summarize what the reader will learn
- Include relevant keywords naturally
Auto-generated fallback. Jamdesk extracts the first prose paragraph from your page content (up to 155 characters) when description is missing from frontmatter. Headings, code blocks, images, and MDX components are skipped. That text fills <meta name="description">, Open Graph, and Twitter cards. An explicit description still gives the best results.
Controlling Indexing
Site-Wide Settings
Set default robot behavior in docs.json:
{
"seo": {
"metatags": {
"robots": "index, follow"
}
}
}Per-Page Control
Override indexing for specific pages in frontmatter:
---
title: Internal Notes
noindex: true
---
Use noindex for:
- Draft or work-in-progress pages
- Internal documentation
- Deprecated content you're keeping for reference
Search Indexing vs. AI Ingestion
robots metadata and noindex control search engines: whether a page appears in Google and your sitemap.xml. They have no effect on the llms.txt and llms-full.txt files that AI tools read. Set seo.ai.llmsTxt to false to stop publishing those (see Turning Off llms.txt). The two controls are independent: a page can be indexed by search engines but excluded from AI ingestion, or the reverse.
Canonical URLs
Set a canonical if your docs are reachable at multiple URLs:
---
title: Getting Started
canonical: https://docs.example.com/getting-started
---
You can also set a site-wide canonical base in docs.json. Jamdesk appends each page's
path to it:
{
"seo": {
"metatags": {
"canonical": "https://docs.acme.com"
}
}
}Social Previews & Open Graph
Jamdesk generates a branded 1200×630 social card for every page. Override any
social tag in frontmatter with flat top-level keys or a nested seo: block.
The flat top-level value wins when the same key is set both ways.
---
title: API Reference
description: REST API endpoints and authentication
"og:title": API Reference — Acme
"og:description": Everything you need to call the Acme API
"og:image": /images/api-social-card.png
"twitter:card": summary_large_image
"twitter:creator": "@acme"
keywords: ["api", "rest", "authentication"]
canonical: https://docs.acme.com/api-reference
---Supported tags
| Group | Tags |
|---|---|
| Open Graph | og:title, og:description, og:image, og:image:width, og:image:height, og:image:alt, og:url, og:type, og:site_name, og:locale, og:video, og:audio |
| Article | og:type: article with article:published_time, article:modified_time, article:author, article:section, article:tag |
| Twitter / X | twitter:card, twitter:title, twitter:description, twitter:image, twitter:image:alt, twitter:site, twitter:creator, twitter:player, app-card tags |
| Other | keywords, author, robots, googlebot, google-site-verification, theme-color, plus any custom tag (place custom tags under seo:) |
Custom OG image dimensions. og:image:width and og:image:height only apply when
customImage is "replace". Set them in the same place as
the og:image they describe, either the page frontmatter or seo.metatags. The generated
card is always 1200×630.
Custom tags. Arbitrary meta tags (e.g. x-pinterest) are emitted as <meta name="...">.
Put them under the seo: block. Only recognized SEO keys are picked up when placed flat.
Twitter / X card type
The twitter:card tag controls which layout X (and other platforms) use when your link is shared:
| Value | What it looks like |
|---|---|
summary | Small square thumbnail on the left, title + description beside it. Compact. |
summary_large_image | Large full-width image on top, title + description below. |
Use summary_large_image for a branded 1200×630 card. It renders the image at full width.
Site-wide default image
Set a fallback social image for every page in docs.json. Any page that sets its own og:image overrides it:
{
"seo": {
"metatags": {
"og:image": "https://docs.acme.com/images/default-card.png"
}
}
}Preview before you publish. Paste the page URL into the OpenGraph Preview tool after a build. It shows how the card renders on each platform and validates the Open Graph tags. It also checks image dimensions and explains how to fix problems.
Customize the social card
Your generated card keeps its current look unless you add seo.socialCard to docs.json.
Set only the keys you want to change:
{
"seo": {
"socialCard": {
"theme": "dark",
"brandColor": true,
"accentBar": false,
"siteName": false,
"customImage": "replace"
}
}
}| Key | Default | Effect |
|---|---|---|
theme | "light" | "dark" uses light text on a dark background and your logo.dark image. |
brandColor | false | true colors the bottom bar and the section badge with colors.primary. A dark card uses colors.light if you set it. If you have no colors, the card uses your theme's accent color. false keeps the default red. |
accentBar | true | false removes the colored bar along the bottom of the card. |
siteName | true | false hides the site name under the logo. Use it when your logo already shows the name. |
customImage | "background" | "background" draws the page title on top of your og:image. "replace" uses your og:image as the whole card. |
The card can only draw SVG, PNG and JPG logos. Jamdesk converts PNG and JPG logo files in your
repository to WebP at build time. The card then usually shows your favicon instead and
keeps the site name, even with siteName: false. Use an SVG logo to show your full logo on the
card. Use an SVG logo.dark for a dark card.
The card can't draw text inside an SVG logo. That text comes out blank. Convert the text to outlines (paths) in your design tool and export the SVG again. In Figma, select the text and use Flatten. In Illustrator, use Type > Create Outlines. The build shows a suggestion when it finds text in the card's SVG logo. Logos set to a full URL (https://...) aren't checked.
The badge text falls back to the normal text color when your brand color doesn't stand out against the card. The bar keeps your brand color unless that color nearly matches the card background. Then the bar is gray.
By default, Jamdesk draws the page title on top of your og:image. Set
"customImage": "replace" to show an image that has its own text or design as it is.
Pick the theme that matches an og:image used as the card background: dark for a
dark image, light for a light one.
In replace mode, Jamdesk draws no title on the image. An og:image in seo.metatags
applies to every page. A page that sets its own og:image uses that one instead.
Only pages with neither get a generated card. Use a 1200×630 PNG or JPG. Some platforms
don't show WebP or SVG images.
Social sites cache link previews. A link that was already shared can keep the old card for a while after you change these settings. Check the new card with the OpenGraph Preview tool.
Older versions of the Jamdesk CLI reject seo.socialCard and seo.titleTemplate in
jamdesk validate and jamdesk dev. Upgrade with npm i -g jamdesk@latest.
Sitemap & Robots.txt
Jamdesk generates sitemap.xml and robots.txt on every build.
| File | Purpose |
|---|---|
sitemap.xml | Lists all pages with last-modified dates for search engines |
robots.txt | Allows all crawlers and points them to the sitemap |
Where to find them
The URLs depend on whether your docs live at a root domain or under a /docs subpath:
If your docs are at the root of your domain (e.g., docs.acme.com or acme.jamdesk.app):
https://docs.acme.com/sitemap.xml
https://docs.acme.com/robots.txtWhat's included in the sitemap
- All published pages (excluding those with
noindexorhiddenfrontmatter) - Last-modified dates from frontmatter when available
- Weekly change frequency
Excluding pages from the sitemap
Add noindex to frontmatter to keep a page out of the sitemap and search engines:
---
title: Internal Notes
noindex: true
---
Pages with hidden: true are excluded too.
JSON-LD Structured Data
Every page includes schema.org structured data as a <script type="application/ld+json"> tag with two schemas:
WebSite: your site name, URL, and description (fromdocs.json).BreadcrumbList: navigation path from Home to the current page, derived from yournavigationconfig.
No configuration needed. Search engines use this for rich results like breadcrumb trails in search listings.
Verify your markup. Paste any page URL into Google's Rich Results Test to confirm the structured data is detected.
IndexNow
Jamdesk submits changed page URLs to IndexNow after each build. Bing, Yandex, and other participating search engines learn about the changes without waiting for their next crawl cycle.
- Fires after every successful build
- Submits only pages that changed
- Non-blocking. It never delays your build
- No configuration required
Best Practices
Run through this checklist before you publish:
Pre-publish checklist
- Unique titles: every page has a distinct, descriptive title under 60 characters.
- Accurate descriptions: descriptions summarize the page in 120-160 characters.
- Logical headings: headings follow a clean hierarchy: one H1, then H2 → H3.
- Descriptive links: internal links use meaningful anchor text, never "click here."
- Image alt text: every image has alt text for accessibility and image search.
- Social image: set a custom
og:imageon key pages, or rely on the auto-generated card. Verify it with the OpenGraph Preview tool.
