> From the [Strife developer docs](https://strife.app/docs/tutorials/astro). Every page is listed in [llms.txt](https://strife.app/docs/llms.txt).

# Astro

Build fast, flexible, and CMS-powered websites with Strife and Astro.

## Getting Started with Strife and Astro

This guide walks you through how to integrate Strife CMS into your Astro project using TypeScript.

> [!NOTE]
> **There's a newer, code-first alternative to steps 3–4 and 9 below.** [Strife CLI](https://strife.app/docs/reference/cli.md) lets you define templates with [`defineType`](https://strife.app/docs/reference/cli/schemas.md) instead of hand-written `Template` objects registered via the integration's `collections` option, and deploys them explicitly with `strife push` rather than lazily on first request. The Template-object approach on this page still works and isn't being removed, but `defineType` + `strife push` is the recommended path for new projects — see the [CLI reference](https://strife.app/docs/reference/cli.md) for the full picture, including [how credentials are provisioned automatically](https://strife.app/docs/reference/cli.md#bootstrapping-a-brand-new-project) instead of the six manual `.env` values in step 6.

***

### 1. Create a new Astro project

Start by creating a fresh Astro project:

```bash
npm create astro@latest
```

Follow the prompts to scaffold your site.

***

### 2. Install the Strife Astro Integration

Inside your Astro project, install the Strife integration:

```bash
npx astro add @strifeapp/astro
```

After running this, your `astro.config.mjs` will be updated automatically:

```javascript
import { defineConfig } from 'astro/config';
import strife from '@strifeapp/astro';

export default defineConfig({
  integrations: [strife()],
  output: 'server',
});
```

Strife requires `output: 'server'` because content is fetched at request time. The integration sets this automatically on install, and also pulls in `@strifeapp/types` so TypeScript users get full type safety without any extra install step.

From `@strifeapp/astro` 1.6.0 the integration caps the RavenDB client's in-memory response cache at 8 MiB (set `strife({ httpCache: { maxBytes } })` to change it). If your project also lists `ravendb` as its own dependency, keep it at 7.2.3 or later: older clients cannot be given a byte budget, so the integration limits them to 500 cached responses and logs a warning.

> [!WARNING]
> **Using an older version of the integration?** Until the updated Astro plugin ships, three things need to be done manually:
>
> 1.  **Install types manually** if `astro add` doesn't include them automatically:
>
>     ```bash
>     npm install -D @strifeapp/types
>     ```
> 2. **Add `output: 'server'`** to your `defineConfig({ ... })` call if it's missing after `astro add`.
> 3. **Add a `name` field** to every Template, matching its `collection` value (e.g. `name: 'Homes'` on the Homes template in the next step). Without it, all templates silently collide at the same storage key and only the last registered one survives.
>
> Once the new plugin version lands, all three are handled for you and these steps aren't needed.

***

### 3. Define your first Collection and Template

In Strife, content is structured via **Collections** and **Templates**. Each template describes the fields available for a collection item.

A typical site has a mix of **singleton** templates (one document per project — e.g. a Home page or Global Settings) and **multi-instance** templates (many documents — e.g. blog posts or product pages). Strife controls this with the `composable` field on the template.

> For more information on how templates and collections work, see the full [Strife Templates & Collections documentation](https://strife.app/docs/reference/templates.md).

> [!NOTE]
> **A quick note on "collections":** Strife uses the word "collections" in two places — the `collections` array on the `strife()` integration (the list of templates to register, see step 4) and the `collection` field on each Template (the RavenDB document collection name). Neither is related to Astro's native [content collections](https://docs.astro.build/en/guides/content-collections/) feature (configured via `src/content/` and `content.config.ts`). If you see a `Content config not loaded` warning at boot, that's Astro noticing you haven't set up its own content collections — it's safe to ignore if you're using Strife exclusively.

Start with a Home template for your site's root page. Create `src/collections/Home.ts`:

```typescript
import { type Template } from '@strifeapp/types';

const Home: Template = {
  displayName: 'Home',
  collection: 'Homes',
  templateType: 'dt',
  normalizedName: 'home',
  composable: false,  // singleton — only one Home document per project
  editors: [
    {
      label: 'Heading',
      editor: { name: 'str-input', type: 'text', propertyName: 'heading' },
    },
    {
      label: 'Description',
      editor: { name: 'str-textarea', type: 'text', propertyName: 'description' },
    },
  ],
};

export default Home;
```

Then add a Pages template for any other content — about, contact, marketing pages, anything that isn't a one-off. Create `src/collections/Pages.ts`:

```typescript
import { type Template } from '@strifeapp/types';

const Pages: Template = {
  displayName: 'Page',
  collection: 'Pages',
  templateType: 'dt',
  normalizedName: 'page',
  composable: true,  // multi-instance — editors can create many Pages
  editors: [
    {
      label: 'Heading',
      editor: { name: 'str-input', type: 'text', propertyName: 'heading' },
    },
    {
      label: 'Description',
      editor: { name: 'str-textarea', type: 'text', propertyName: 'description' },
    },
  ],
};

export default Pages;
```

The two templates illustrate the contrast: `Home` has `composable: false` (only one document allowed), while `Pages` has `composable: true` (editors can create many). Use `composable: false` for templates that should only ever have one document — Home, Global Settings, Footer config, etc. — and `composable: true` for repeatable content. See the templates reference for the full list of template properties.

> [!WARNING]
> **Singleton seeding:** for templates with `composable: false`, the single document needs to exist before your site can render it. Open Strife Studio → your team → Content, create a new Home document, and set its `url` to `/`.
>
> Future Strife versions auto-provision the root Home document when a team is created — so this specific step disappears. Other singleton templates you add later (Global Settings, Footer, etc.) follow the same manual-seed pattern and aren't auto-provisioned.

***

### 4. Register your Collection

Import your collection and register it in your Strife integration at `astro.config.mjs`

```javascript
import { defineConfig } from 'astro/config';
import strife from '@strifeapp/astro';
import Home from './src/collections/Home';
import Pages from './src/collections/Pages';

export default defineConfig({
  integrations: [strife({
    collections: [Home, Pages]
  })],
  output: 'server',
});
```

***

### 5. Create your Content Types

Define TypeScript interfaces matching each collection for strong typing when querying:

```typescript
import { type Content } from '@strifeapp/types';

export type Home = Content & {
  heading: string;
  description: string;
};

export type Page = Content & {
  heading: string;
  description: string;
};
```

In real projects these two might diverge — Home could have its own hero structure, Pages might be more flexible — but for the tutorial they share the same shape.

***

### 6. Add your Strife Credentials

Create a `.env` file in your project root with the credentials provided for your team:

```bash
# .env

# RavenDB client certificate — base64-encoded PFX bundle
STRIFE_CERTIFICATE=...

# Password protecting the PFX certificate above
STRIFE_CERTIFICATE_PASSWORD=...

# Comma-separated list of RavenDB node URLs
STRIFE_DATABASE_URLS=https://a.example.strife.ravendb.cloud

# Name of your team's RavenDB database
STRIFE_DATABASE=...

# Your team's unique identifier
TEAM_ID=...

# Signing secret used for preview and webhook payloads
SECRET=...
```

> [!NOTE]
> These six values are provided during your Strife onboarding. The same cert-based authentication model is used by the Node.js SDK — if you've set that up before, the values are the same.
>
> If you're using [Strife CLI](https://strife.app/docs/reference/cli.md) (`npx strife` / `strife .`), you won't need to set these six individually — the CLI packs the same information into a single `STRIFE_SECRET` value and writes it to `.env` for you. Both forms are read by the integration; use whichever your provisioning path gave you.

> [!WARNING]
> Treat every value above as a secret. Never commit `.env` to version control — add it to `.gitignore` if `npm create astro@latest` didn't do so already.

***

### 7. Enable TypeScript Module Declarations

To support type-safe access to Strife's document store, update or create `src/env.d.ts` (recent Astro starters auto-generate types in `.astro/` and don't ship a stub `env.d.ts`):

```typescript
/// <reference types="astro/client" />

declare module 'strife:store' {
  import type { IDocumentSession, IDocumentStore } from 'ravendb';
  export const store: IDocumentStore;
  export { IDocumentSession };
}
```

***

### 8. Query Content from Strife

You can now query your content directly from RavenDB via Strife. The same query pattern works for both singleton and multi-instance templates — it matches by URL:

```typescript
import { store } from 'strife:store';
import type { Home } from '../collections/Home';

const session: IDocumentSession = store.openSession();

// Returns the Home document for url='/'.
// For other URLs ('/about', '/contact', etc.), the same query returns
// whichever Page document has that url.
const home: Home = (await session
  .query({ indexName: 'Content/ByUrl' })
  .whereEquals('url', '/')
  .selectFields(['heading', 'description'])
  .firstOrNull()) as Home;
```

***

#### 9. Deploy your templates

> [!WARNING]
> Earlier versions of the integration deployed templates and the content index automatically, on the first request to a route that imported `strife:store`. That's no longer the case — the integration only connects to the database and reads content now. Deployment is explicit, via the CLI, as described below.

Templates and the `Content/ByUrl` query index are deployed with [Strife CLI](https://strife.app/docs/reference/cli.md):

```bash
npm install --save-dev @strifeapp/cli @strifeapp/strife
npx strife login
npx strife link
```

The templates you registered as `Template` objects in step 4 aren't picked up by `strife push` directly — `push` deploys schemas written with [`defineType`](https://strife.app/docs/reference/cli/schemas.md). If you're following this tutorial's `Template`-object approach, create your templates directly in Strife Studio instead (Settings → Templates) rather than via `push`, or see the [CLI reference](https://strife.app/docs/reference/cli.md) for defining them in code as `defineType` schemas instead of the `Template` objects used in steps 3–4.

Once your templates exist (via Studio or `defineType` + `push`), start the dev server as usual:

```bash
npm run dev
```

and visit the URL Astro prints (e.g. `http://localhost:4321/`).

***

#### 10. Add live preview (optional)

Strife Studio includes a preview iframe that mirrors your site as content is edited. With the integration installed, your islands can subscribe to updates:

```svelte
<!-- src/components/Hero.svelte -->
<script lang="ts">
  import { subscribe } from '@strifeapp/strife';
  import { onMount } from 'svelte';

  let { initial } = $props<{ initial: { heading: string; description: string } }>();
  let data = $state(initial);

  onMount(() => subscribe((next) => { data = next; }));
</script>

<h1>{data.heading}</h1>
<p>{data.description}</p>
```

This requires loading `@strifeapp/strife` eagerly in your root layout (otherwise the iframe handshake completes before subscribers are ready). For full setup, the data-shape reference, and framework-agnostic patterns, see [Custom Live Preview implementation](https://strife.app/docs/reference/live-preview/custom-live-preview-implementation.md).

***

#### Snapshots (optional)

Editors can hand a reviewer without a Strife account a **preview link** — `https://example.com/snapshot/{token}` — that shows a frozen, unpublished version of a document on your site for 7 days. The integration's snapshot middleware resolves the link; you add the page that renders it. Copy the package's [`templates/snapshot-page.astro`](https://github.com/wieldyapp/wieldy/blob/master/src/sdk/js/src/packages/astro/templates/snapshot-page.astro) to `src/pages/snapshot/[token].astro`:

```astro
---
export const prerender = false;              // rendered on demand — never prebuilt
import Layout from '../../layouts/Layout.astro';

const { name, expiresAt, locale, content } = Astro.locals.snapshot!;
---
<Layout title={content.displayName ?? 'Preview'}>
  <aside role="status">{name ? `Preview “${name}”` : 'Preview'} — expires {expiresAt.toISOString()}</aside>
  <h1>{content.heading}</h1>
</Layout>
```

Expired, revoked or unknown links render your 404 page. The route needs a `Content/ByUrl` index built from the `@strifeapp/strife` release that ships snapshots (run `strife push` after updating), and the name must be rendered as text only. Locale prefixes, `snapshot.fields`, sites that resolve locale in their own middleware, and the CDN/analytics rules are in the [Snapshots](https://strife.app/docs/reference/snapshots.md) reference.

***

### 11. Choose a deployment adapter

`astro dev` works out of the box. For production builds (`astro build`), Astro needs an adapter that matches your deploy target. Strife doesn't require a specific one — pick whichever suits your hosting:

| Deploy target       | Adapter               | Install                    |
| ------------------- | --------------------- | -------------------------- |
| Self-hosted Node.js | `@astrojs/node`       | `npx astro add node`       |
| Vercel              | `@astrojs/vercel`     | `npx astro add vercel`     |
| Netlify             | `@astrojs/netlify`    | `npx astro add netlify`    |
| Cloudflare Workers  | `@astrojs/cloudflare` | `npx astro add cloudflare` |

See [Astro's deploy guides](https://docs.astro.build/en/guides/deploy/) for the full list and configuration details.

After installing an adapter, your `astro.config.mjs` will look something like:

```javascript
import { defineConfig } from 'astro/config';
import strife from '@strifeapp/astro';
import node from '@astrojs/node';           // or vercel, netlify, cloudflare…
import Homes from './src/collections/Homes';

export default defineConfig({
  integrations: [strife({ collections: [Homes] })],
  output: 'server',
  adapter: node({ mode: 'standalone' }),     // adapter-specific options vary
});
```

Push your repository to your chosen host and your Strife-powered Astro site is live.
