Live Collections
Astro stabilized live content collections — collections backed by a runtime loader instead of static files — in 6.0, after introducing them experimentally in 5.10. CaretCMS ships a loader (caretLoader) that wires your CMS data into astro:content on either version.
End result: getLiveEntry('pages', 'home') returns the latest CMS data on every request.
Prerequisites
Section titled “Prerequisites”- Astro 6.0+ stable or Astro 5.10+ experimental
output: 'server'(live collections are dynamic by definition)
-
Create
src/caret.config.tssrc/caret.config.ts import { defineLiveCollection } from 'astro:content';import { caretLoader } from '@caretcms/core';const pages = defineLiveCollection({loader: caretLoader('pages'),});const site = defineLiveCollection({loader: caretLoader('site'),});const products = defineLiveCollection({loader: caretLoader('products'),});export const collections = { pages, site, products };One
defineLiveCollectioncall per CMS collection you want to query. -
Query in page frontmatter
src/pages/index.astro ---import { getLiveEntry, getLiveCollection } from 'astro:content';const { entry: home } = await getLiveEntry('pages', 'home');const { entries: allProducts } = await getLiveCollection('products');---<h1>{home?.data.headline ?? 'Default headline'}</h1><ul>{allProducts.map((p) => (<li>{p.data.name} — ${p.data.price}</li>))}</ul>getLiveEntryandgetLiveCollectioncome fromastro:content— they’re not CaretCMS APIs. Astro routes them to the registered loader behind the scenes. -
Pair with inline editing (optional)
---import { getLiveEntry } from 'astro:content';const { entry: home } = await getLiveEntry('pages', 'home');---<main data-caret-scope="pages::home"><h1 data-caret="headline">{home?.data.headline ?? 'Default headline'}</h1><p data-caret="intro">{home?.data.intro ?? 'Default intro'}</p></main>The CMS data hydrates at SSR (
getLiveEntry), the response-rewriting middleware ensures the latest stored value lands in the HTML, and the inline editor lets editors click to change it. One source of truth.
Filtering and querying
Section titled “Filtering and querying”getLiveCollection accepts a filter:
---const { entries } = await getLiveCollection( 'products', (entry) => entry.data.in_stock === true,);---Errors and missing data
Section titled “Errors and missing data”getLiveEntry returns { entry: undefined, error: undefined } if the entry doesn’t exist. Always nullish-coalesce:
const headline = home?.data?.headline ?? 'Welcome';If the loader fails (storage error, unknown collection, network blip), it throws a CaretLoaderError. By default this propagates to Astro’s error boundary.
---import { getLiveEntry } from 'astro:content';import { CaretLoaderError } from '@caretcms/core';
let home;try { const result = await getLiveEntry('pages', 'home'); home = result.entry;} catch (err) { if (err instanceof CaretLoaderError) { console.warn('[caret] loader failed', err.message); } else { throw err; }}---Caching
Section titled “Caching”The loader has no built-in cache — every render reads from storage. For most setups (filesystem, KV) this is fast enough. If you need caching:
- Add Astro’s
Cache-Controlheaders on the page response - Wrap your storage adapter with a TTL cache
- Use a CDN in front of your origin
Type safety
Section titled “Type safety”If you use registered Zod schemas, you can type the data field on entries:
import { defineLiveCollection } from 'astro:content';import { caretLoader } from '@caretcms/core';import { z } from 'zod';
const PageSchema = z.object({ headline: z.string(), intro: z.string(),});
const pages = defineLiveCollection({ loader: caretLoader('pages'), schema: PageSchema,});getLiveEntry('pages', 'home') then returns typed data.