---
title: Configure your content
description: Describe your sites and collections in hollware.config.ts, with a schema, routes, components and a style guide.
section: guides
order: 1
---

# Configure your content

`hollware.config.ts` is the source of truth for what your content is. Agents read it before they write, and Hollware checks every write against it. The Worker loads it at startup, so a config with a mistake stops the Worker with a message that says what to change.

## Sites and collections

A deployment serves one site or more. A site has its locales and its collections; a collection is a kind of entry, such as posts or pages.

```ts
import { contracts, defineCollection, defineComponent, defineConfig } from '@hollware/core'
import * as z from 'zod'

const posts = defineCollection({
  schema: z.object({
    title: z.string().min(1),
    description: z.string().min(1).max(160),
    date: z.iso.date(),
    tags: z.array(z.string()).min(1),
    translationGroup: z.string(),
  }),
  routes: { en: '/blog/:slug/', es: '/es/blog/:slug/' },
  translations: { groupBy: 'translationGroup' },
  tags: 'tags',
  sortBy: 'date',
  styleGuide: 'Short paragraphs, one idea per section.',
})

export default defineConfig({
  sites: {
    blog: {
      locales: ['en', 'es'],
      defaultLocale: 'en',
      url: 'https://example.com',
      collections: { posts },
    },
  },
})
```

## The schema

`schema` is the frontmatter of an entry, as a Zod schema or any Standard Schema with a JSON Schema. A draft whose frontmatter does not fit is saved with an error in its verdict, and cannot be published until it fits.

- `translations.groupBy` names the field that ties the entries of one post in each locale.
- `tags` names the field that holds the tags, which `list_entries` filters by.
- `sortBy` names a date field: lists are newest first by it. Without it, by the date of first publication.

## Routes

`routes` gives the path of an entry in each locale, with `:slug`. Links between entries are checked against them, and every locale of the site needs a route.

## Components

The body of an entry is Markdown with components, written as `::name{prop="value"}`. Each collection declares the components it allows, with the schema of their props; any other component is an error.

```ts
components: {
  callout: defineComponent({
    props: z.strictObject({ type: z.enum(['note', 'tip', 'warning']) }),
    description: 'A note beside the flow of the text.',
    example: '::callout{type="tip"}\nKeep slugs short.\n::',
  }),
},
```

The description and the example are for agents: `describe_site` returns them.

## Contracts

`contracts` adds rules to a collection. Hollware ships some, and you can write your own; see [Contracts](/docs/contracts/).

```ts
contracts: [
  contracts.translationPair({ severity: 'warning' }),
  contracts.internalLinksResolve(),
  contracts.imagesHaveAlt(),
],
```

## Style guides

`styleGuide`, on a site and on a collection, is Markdown for whoever writes. Hollware does not check it; agents read it with `describe_site`. Keep it short and concrete.
