---
title: Astro
description: Read what you publish from an Astro site with @hollware/astro, at request time or at build time, and render it with your own Astro components.
section: frameworks
order: 2
---

# Astro

`@hollware/astro` gives Astro's content layer two loaders, and two components that render what they load.

```sh
npm install @hollware/astro
```

## Read at request time

A **live collection** reads at request time, so what you publish shows on the next request, with no rebuild. On Cloudflare, bind your Hollware Worker in the site's `wrangler.jsonc`:

```jsonc
"services": [{ "binding": "HOLLWARE", "service": "my-cms", "entrypoint": "Hollware" }]
```

```ts
// src/live.config.ts
import { env } from 'cloudflare:workers'
import { defineLiveCollection } from 'astro/content/config'
import { hollwareBinding, hollwareLiveLoader } from '@hollware/astro'

const blog = defineLiveCollection({
  loader: hollwareLiveLoader({
    site: 'my-site',
    collection: 'blog',
    locale: 'en',
    reader: () => hollwareBinding(env.HOLLWARE),
  }),
})
export const collections = { blog }
```

Make one collection per locale. An entry's `id` is its slug and its `data` the frontmatter, with the fields Hollware fills already filled.

## Read at build time

For a static site, `hollwareLoader` in `src/content.config.ts` reads every published entry once, at build time, over HTTP with a read token (`npx hollware token create --read`):

```ts
// src/content.config.ts
import { defineCollection } from 'astro:content'
import { hollwareLoader } from '@hollware/astro'

const blog = defineCollection({
  loader: hollwareLoader({
    site: 'my-site',
    collection: 'blog',
    locale: 'en',
    url: 'https://cms.example.com',
    token: process.env.HOLLWARE_TOKEN ?? '',
  }),
})
export const collections = { blog }
```

Give `defineCollection` a `schema` to validate the frontmatter, as with any loader.

## Render an entry

```astro
---
import HollwareContent from '@hollware/astro/HollwareContent.astro'
import HollwareImage from '@hollware/astro/HollwareImage.astro'
import { getLiveEntry } from 'astro:content'
import Callout from '../components/Callout.astro'

const { entry } = await getLiveEntry('blog', Astro.params.slug!)
if (!entry) return new Response('Not found', { status: 404 })
---

<h1>{entry.data.title}</h1>
<HollwareImage entry={entry} field="img" />
<HollwareContent entry={entry} components={{ callout: Callout }} />
```

- **`HollwareContent.astro`** renders the entry from its document, with your own Astro components for the components of your entries: `::callout{type="tip"}` gives `Callout.astro` the prop `type` and its content in the default `<slot />`. An element with no mapping renders as its plain tag. It goes through the same rules as the HTML Hollware makes, so nothing in an entry runs a script.
- **`HollwareImage.astro`** renders the image a frontmatter field holds, with its alt, its size and the `srcset` of its copies. Any attribute you give wins.

You can also use Astro's own `render(entry)`: its `<Content />` is the HTML Hollware made, which needs no component of yours.

## Lists and translations

`getLiveCollection('blog', { tag, limit })` lists the entries, without their body, each with the images of its frontmatter for `HollwareImage.astro`. `hollwareTranslations(options, group)` gives the `{ locale, slug }` of each entry of a translation group, to link to.

## Previews

A live collection previews drafts. Add the middleware, which exchanges the admin's preview link for a cookie:

```ts
// src/middleware.ts
import { hollwarePreview } from '@hollware/astro'
export const onRequest = hollwarePreview()
```

```astro
---
import { refusePreview } from '@hollware/astro'
const preview = Astro.locals.hollwarePreview
const { entry, error } = await getLiveEntry('blog', { id: Astro.params.slug!, preview })
if (error) refusePreview(Astro.locals, error)
---
```

With a token, the entry is its latest revision, a draft included, and the page is never kept. A token that expired is an `error`, never the published version. A build-time collection has no previews: a static page is built once, for everyone.

## A whole site

`examples/astro-blog` in the repository is an Astro blog on a Worker that reads the playground at request time, with its callout as a component of its own.
