tidypress
On this page

Configuration

The practical shape of docs/tidypress.config.ts.

TidyPress reads one config file:

docs/tidypress.config.ts

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

import { defineConfig } from 'tidypress/config'
 
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, and social metadata.

Use nav for header links and footer for lower-page links.

nav: [
  { label: 'docs', href: '/docs' },
  { label: 'writing', href: '/writing' },
  { label: 'GitHub', href: 'https://github.com/you/project', target: '_blank' },
],
footer: [
  { label: 'GitHub', href: 'https://github.com/you/project' },
  { label: 'RSS', href: '/writing/rss.xml', icon: 'rss', external: false },
],

RSS is generated at <writing basePath>/rss.xml (for example /writing/rss.xml). The footer can link to it explicitly, or omit the link and rely on the default RSS icon when writing is enabled.

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

Footer links render as text by default. Set icon to render an icon link instead:

footer: [
  { 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 },
]

Supported footer icons: github, x, linkedin, discord, youtube, instagram, bluesky, facebook, reddit, twitch, mastodon, slack, telegram, tiktok, npm, rss, and email.

Internal links are strict by default. If a site has links that are generated outside TidyPress, use relaxed mode:

navPolicy: {
  mode: 'relaxed',
}

Collections

Collections map content folders to route families.

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

Kinds:

KindUse for
omitted on docsthe main docs collection
contentdocs-like sections
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 (for example works)Content folder docs/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 docs/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
docs-writingdocs + writing
customdocs + writing + a playbooks content collection
npx tidypress init --preset blog

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' },
},
docs/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 docs/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: 'docs/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 tidypress context.