tidypress
On this page

Configuration

The shape of tidypress.config.ts — collections, nav, and site metadata.

TidyPress reads one config file at the publish root: site/tidypress.config.ts. That file defines how writing, work, and the docs collection are routed—not the prose itself.

site/tidypress.config.ts

Only name is required. Keep the file small until the site needs more shape.

import { defineConfig } from 'tidypress'
 
export default defineConfig({
  name: 'my-project',
  description: 'A publishing framework for Git-native authorship.',
  nav: [
    { label: 'docs', href: '/docs' },
    { label: 'writing', href: '/writing' },
  ],
  collections: {
    docs: { enabled: true, basePath: '/docs', label: 'docs' },
    writing: { enabled: true, basePath: '/writing', kind: 'writing', label: 'writing' },
  },
  siteUrl: 'https://example.com',
})

Site metadata

name: 'my-project',
description: 'A publishing framework for Git-native authorship.',
siteUrl: 'https://example.com',

name appears in titles and the header. description is used for metadata and homepage copy. siteUrl is used for canonical URLs, sitemap output, RSS, and social metadata.

Pass it at init when you know the domain:

npx tidypress init --site-url https://yoursite.example

tidypress doctor and tidypress build warn when siteUrl is still the https://example.com placeholder. Until it is set, builds omit absolute canonical/OG URLs and the Astro sitemap (no fake example.com hosts). See Conventions.

Use nav for header links.

nav: [
  { label: 'docs', href: '/docs' },
  { label: 'writing', href: '/writing' },
  { label: 'GitHub', href: 'https://github.com/you/project' },
],

Navigation is strict by default. Internal href values are validated against built routes.

If a site has links that are generated outside TidyPress, use relaxed mode:

navPolicy: {
  mode: 'relaxed',
}

Community links for the docs site live in CONTRIBUTING.md on GitHub (bug reports, feature requests, security). The product site footer can point to those URLs via footer.main link slots.

The page footer has two bands:

BandPositionPurpose
Main (main)TopTwo plain-text slots (start / end). Hidden when both are empty and the site has only one locale.
SubBottomIcon/text links on the left; attribution on the right.

The main band is the future extension point for richer blocks (menus, grids, newsletter, banners, and so on). Today it is text only. Markup uses data-footer-zone and data-footer-slot attributes so later releases can hang structured blocks off the same layout.

Config shapes

footer accepts either:

  1. Link array — shorthand for sub-footer links only.
  2. Object — full control over main band, links, copyright, and product credit.
footer: [
  { label: 'GitHub', href: 'https://github.com/you/project', icon: 'github' },
],
footer: {
  main: {
    start: 'Acme Labs',
    end: 'Questions? hello@example.com',
  },
  copyright: '© {year} {name}',
  links: [
    { label: 'GitHub', href: 'https://github.com/you/project', icon: 'github' },
    { label: 'RSS', href: '/writing/rss.xml', icon: 'rss', external: false },
  ],
},

aside is accepted as an alias for main.end.

Defaults

Unless you override them:

FieldDefault
copyright© {year} {name} ({year} = build year, {name} = name)
showCredittrue — appends a product credit link on the attribution line
creditprefix: ', Made with ', label: 'tidypress', href: 'https://tidypress.pages.dev/'
linksIf you omit GitHub, one is added using repository.url when set, otherwise https://github.com/you
mainEmpty — main band hidden until you set start / end or enable multi-locale i18n

You do not need showCredit: true in config; it is already on. Set showCredit: false only when you want to hide the product credit segment.

Attribution renders as one line, for example: © 2026 my-project, Made with tidypress.

Field reference

main — top band text slots. Each slot is plain string text or an array of inline links (shown with · separators).

KeyTypeDescription
startstring | FooterMainLink[]Left slot.
endstring | FooterMainLink[]Right slot. Language switcher renders here when i18n.locales has more than one entry.

FooterMainLink fields: label, href, optional external: false for same-tab internal paths.

main: {
  start: [
    { label: 'Improve these docs', href: 'https://github.com/you/project/tree/main/apps/site/src/content/docs' },
    { label: 'Share a feature idea', href: 'https://github.com/you/project/issues/new' },
  ],
  end: 'Questions? hello@example.com',
},

copyrightstring on the sub-footer attribution line. Supports {year} and {name} tokens.

showCreditboolean. Default true. Set false to omit the product credit segment (copyright still shows).

credit — partial override of the product credit segment. Used only when showCredit is true.

KeyTypeDefault
prefixstring', Made with '
labelstringtidypress
hrefstringhttps://tidypress.pages.dev/

linksFooterItem[] in the sub-footer (left). Each item:

KeyTypeDescription
labelstringRequired. Visible text for text links; screen-reader label for icon links.
hrefstringRequired.
iconFooterItemIconWhen set, renders a built-in SVG icon instead of text.
externalbooleanfalse keeps same-tab navigation for internal paths. Icon links default to external/new tab unless external: false.

Set icon to one of:

github, x, linkedin, discord, youtube, instagram, bluesky, facebook, reddit, twitch, mastodon, slack, telegram, tiktok, npm, rss, email

Text links (no icon) render as plain text in the sub-footer.

Repository URL

repository.url feeds the default GitHub footer link when links does not already include a github icon:

repository: {
  url: 'https://github.com/you/project',
  branch: 'main',
  editPath: 'site/src/content',

editPath is relative to the repo root and should cover all collections you want “edit on GitHub” links for. See Repository links.

Writing and RSS

When the writing collection is enabled, TidyPress generates a feed at <writing basePath>/rss.xml. With the default path, that is /writing/rss.xml. Add a footer link when you want the RSS icon in the sub-footer:

{ label: 'RSS', href: '/writing/rss.xml', icon: 'rss', external: false },

i18n

With multiple locales, a language switcher appears in main.end (footer main band). Customize its accessible name via i18n.strings.<locale>.languageLabel. See i18n.

Examples

Hide product credit (copyright only):

footer: {
  showCredit: false,
  links: [{ label: 'GitHub', href: 'https://github.com/you/project', icon: 'github' }],
},

Custom product credit:

footer: {
  credit: {
    prefix: ' · Built with ',
    label: 'TidyPress',
    href: 'https://tidypress.pages.dev/',
  },
},

Icon and text links:

footer: {
  links: [
    { label: 'GitHub', href: 'https://github.com/you/project', icon: 'github' },
    { label: 'X', href: 'https://x.com/you', icon: 'x' },
    { label: 'Privacy', href: '/privacy', external: false },
  ],
},

Collections

Collections map folders under src/content/ to route families. The collection key docs is the docs collection at /docs/…; it is not the publish root folder site/ from init.

collections: {
  writing: {
    enabled: true,
    basePath: '/writing',
    kind: 'writing',
    label: 'writing',
  },
  docs: {
    enabled: false,
    basePath: '/docs',
    label: 'docs',
  },
  guides: {
    enabled: true,
    basePath: '/guides',
    kind: 'content',
    label: 'guides',
  },
}

Kinds:

KindUse for
omitted on docsthe docs collection, sidebar-ordered routes
contentreference shelves, playbooks, ADRs
writingdated posts
projectsproject cards and optional project pages
pagestandalone pages

Example projects collection:

projects: {
  enabled: true,
  basePath: '/projects',
  kind: 'projects',
  label: 'projects',
},

Collection key, URL, and label

Three settings control different surfaces. They are not renamed automatically for each other.

SettingControls
Collection key worksContent folder site/src/content/works/ and home.order entries
basePathPublic URLs (/works, /works/sample)
labelUI copy on indexes and the homepage section title
kindEngine behavior (schema, layout, routing). Must be one of the kinds in the table above

There is no kind: 'works'. Use kind: 'projects' for project cards and optional project pages, then pick any key and paths you want.

Rename a projects showcase to works everywhere visitors see it:

nav: [
  { label: 'writing', href: '/writing' },
  { label: 'works', href: '/works' },
],
home: {
  order: ['writing', 'works'],
},
collections: {
  docs: { enabled: false, basePath: '/docs', label: 'docs' },
  writing: { enabled: true, basePath: '/writing', kind: 'writing', label: 'writing' },
  works: {
    enabled: true,
    basePath: '/works',
    kind: 'projects',
    label: 'works',
  },
},
capabilities: {
  disable: ['docs'],
},

Place markdown in site/src/content/works/. Match nav href values to each collection basePath.

Init presets

tidypress init seeds content and config. default is an alias for lab.

PresetShape
labwriting + projects, docs off
blogwriting only, docs and pages off
personahero, projects, writing, about page
body-of-workworks, projects, writing, reference, process, pages; docs disabled
body-of-work-docsbody-of-work + enabled docs
docs-writingdocs + writing
customdocs + writing + a playbooks content collection
npx tidypress init --preset blog --site-url https://yoursite.example

See Examples for runnable copies under examples/.

Capabilities

Starter collections (docs, writing, pages) respect capabilities.disable and capabilities.enable after per-collection enabled flags. The blog preset disables docs and pages so empty /pages/ routes are not generated.

capabilities: {
  disable: ['docs', 'pages'],
},

See Advanced configuration for the full capability list.

sections is the legacy shim.

Hero bar

The home hero bar is opt-in. Set enabled: true to show role, pronunciation, lead, and links above homepage previews:

hero: {
  enabled: true,
  role: 'Engineer',
  pronunciation: 'your-name',
  lead: 'Short bio on the home page.',
  image: '/images/portrait.jpg',
  links: [
    { label: 'Email', href: 'mailto:you@example.com' },
    { label: 'GitHub', href: 'https://github.com/you', external: true },
  ],
},

Omit hero or leave enabled unset/false to hide the bar. The persona init preset enables a starter hero.

Persona / CV sites

The persona preset gives you a hero, projects, optional writing, and an /about page. Experience, education, and skills belong in markdown on that page or in writing posts — not in structured config blocks.

TidyPress does not ship résumé schema (no job blocks, degree blocks, or employment location fields). That keeps the product markdown-first and avoids vague HR metadata (hybrid, remote, present, and similar).

When you need a CV section, use normal headings on /about:

## Experience
 
**Company — Role** · 2022–2024
 
What you shipped.
 
## Education
 
**School — Degree** · 2020

Use projects for highlights and writing for essays; link out with url + linkOnly when a row is only a link.

Pages

Two related mechanisms — do not confuse them:

MechanismPurpose
pages: [...] in configRegister root routes and optional nav labels
collections.pages with kind: 'page'Astro collection under src/content/pages/; includes a /pages/ index when enabled

For a single About or CV page at /about, use a file in src/content/pages/ and list it in config:

pages: [{ slug: 'about', navLabel: 'about' }],
collections: {
  pages: { enabled: true, kind: 'page' },
},
site/src/content/pages/about.md -> /about

Lab and blog presets disable the pages collection so an empty /pages/ route is not generated. Enable pages only when you add root pages (persona enables it for /about).

Legacy shorthand (slug only):

pages: ['about', { slug: 'work', navLabel: 'work' }],

Branding

branding: {
  icon: '/favicon.svg',
  favicon: '/favicon-white.svg',
}

Place files in site/public/.

Search is powered by Pagefind and generated during tidypress build.

search: {
  exclude: ['docs/internal/*', 'writing/drafts/*'],
}

Exclude one page with frontmatter:

---
search: false
---

Docs pages can show an “Edit this page” link:

repository: {
  url: 'https://github.com/you/project',
  branch: 'main',
  editPath: 'site/src/content',
}

Presentation settings

Keep presentation separate from the base config:

  • Site layout covers sidebar groups, chapter navigation, homepage previews, indexes, tags, and icons.
  • Theme covers typography, theme mode, code highlighting, and custom tokens.
  • Advanced configuration covers i18n, versions, analytics, capabilities, and build/llms.txt.