> ## Documentation Index
> Fetch the complete documentation index at: https://docs.skayle.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Astro

> Integrate Skayle CMS with Astro using frontmatter fetch and dynamic static routes.

## Overview

This guide uses:

* Astro 5.x
* Server-side fetch in frontmatter
* Static blog index and dynamic post pages via `getStaticPaths`

## 1. Configure environment variables

```bash theme={null}
# .env
SKAYLE_API_BASE_URL=https://api.skayle.ai
SKAYLE_ORG_ID=your_organization_id
```

## 2. Add a CMS client helper

Create `src/lib/skayle-cms.ts`:

```ts theme={null}
const API_BASE = import.meta.env.SKAYLE_API_BASE_URL || "https://api.skayle.ai";
const ORG_ID = import.meta.env.SKAYLE_ORG_ID;

if (!ORG_ID) throw new Error("Missing SKAYLE_ORG_ID");

export type JsonApiResource<T = Record<string, unknown>> = {
	id: string;
	type: string;
	attributes: T;
};

export async function fetchSkayle<T>(path: string): Promise<T> {
	const res = await fetch(`${API_BASE}/v1/${ORG_ID}${path}`);
	if (!res.ok) throw new Error(`Skayle request failed: ${res.status}`);
	return res.json() as Promise<T>;
}
```

## 3. Build blog listing page

Create `src/pages/blog/index.astro`:

```astro theme={null}
---
import { fetchSkayle, type JsonApiResource } from "../../lib/skayle-cms";

type CmsPost = { title: string; slug: string; excerpt: string | null };

type ListResponse = { data: Array<JsonApiResource<CmsPost>> };

const response = await fetchSkayle<ListResponse>(
	"/articles?status=published&orderby=date&order=desc&page=1&per_page=50",
);
const posts = response.data;
---

<html>
	<body>
		<h1>Blog</h1>
		<ul>
			{posts.map((post) => (
				<li>
					<a href={`/blog/${post.attributes.slug}`}>{post.attributes.title}</a>
				</li>
			))}
		</ul>
	</body>
</html>
```

## 4. Build dynamic blog detail page

Create `src/pages/blog/[slug].astro`:

```astro theme={null}
---
import { fetchSkayle, type JsonApiResource } from "../../lib/skayle-cms";

type CmsPost = {
	title: string;
	slug: string;
	excerpt: string | null;
	content_html: string | null;
};

type ListResponse = { data: Array<JsonApiResource<CmsPost>> };
type SingleResponse = { data: JsonApiResource<CmsPost> | null };

export async function getStaticPaths() {
	const list = await fetchSkayle<ListResponse>(
		"/articles?status=published&orderby=date&order=desc&page=1&per_page=100",
	);

	return list.data.map((post) => ({
		params: { slug: post.attributes.slug },
	}));
}

const { slug } = Astro.params;
const postResponse = await fetchSkayle<SingleResponse>(`/articles/${encodeURIComponent(slug || "")}`);
const post = postResponse.data;

if (!post) {
	throw new Error("Post not found");
}
---

<html>
	<body>
		<article>
			<h1>{post.attributes.title}</h1>
			{post.attributes.excerpt ? <p>{post.attributes.excerpt}</p> : null}
			<div set:html={post.attributes.content_html || ""} />
		</article>
	</body>
</html>
```

## 5. Static build flow

Run a static build with:

```bash theme={null}
npm run build
```

Astro generates listing and post pages from Skayle CMS data at build time.

For frequent content updates, schedule regular builds or use Astro hosting features that support on-demand rendering.

## 6. Use non-article collections (optional)

If you enabled additional collections/content types in Skayle CMS, fetch them via collection-scoped items.
Non-article collections are available on the `Authority` plan.

```ts theme={null}
const answers = await fetchSkayle<ListResponse>(
	"/collections/answers/items?status=published&page=1&per_page=50",
);
```

`/posts` still works as a backward-compatible alias, but new code should use `/articles`.
