npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@clearcms/astro

v0.3.0

Published

Astro Content Layer adapter for clear. Wraps @clearcms/sdk so consumers can use Astro's native getCollection() / getEntry() APIs to read from a clear bucket (fs / R2 / S3) or a running admin (REST).

Downloads

59

Readme

@clearcms/astro

Astro Content Layer adapter for clear. Drop clearLoader('posts') into defineCollection, then read with getCollection('posts') / getEntry('posts', slug) like any other Astro content. Same surface against any clear backend — fs / REST / R2 / S3.

Status: v0. Read-only. MIT licensed. Wraps @clearcms/sdk — that's the seam.

Why

Astro 5+ ships a Content Layer API: any Loader you point at can drive getCollection() and getEntry(). clear stores content as JSON in a bucket. @clearcms/astro is the bridge — it turns a clear collection into an Astro content collection.

The big win: static-build deploys. Set CLEAR_BACKEND=fs (or r2 / s3), run astro build, and you get static HTML rendered straight off bucket files — the admin doesn't need to be reachable at build time, never mind at request time.

Install

pnpm add @clearcms/astro
# or: npm install @clearcms/astro

astro@^6 is a peer dependency.

Use

Configure collections

// src/content/config.ts
import { defineCollection } from 'astro:content';
import { clearLoader } from '@clearcms/astro';

export const collections = {
  posts: defineCollection({ loader: clearLoader('posts') }),
  recipes: defineCollection({ loader: clearLoader('recipes', { locale: 'en' }) }),
};

Render pages

---
// src/pages/index.astro
import { getCollection } from 'astro:content';
const posts = await getCollection('posts');
---
<ul>
  {posts.map((post) => (
    <li>
      <a href={`/${post.id}`}>{post.data.title}</a>
      <small>{new Date(post.data._meta.publishedAt).toLocaleDateString()}</small>
    </li>
  ))}
</ul>
---
// src/pages/[slug].astro
import { getEntry } from 'astro:content';
import { renderTipTap } from '@clearcms/astro';
const post = await getEntry('posts', Astro.params.slug);
if (!post) return new Response('Not found', { status: 404 });
---
<article>
  <h1>{post.data.title}</h1>
  <div set:html={renderTipTap(post.data.body)} />
</article>

Globals

Identity, nav, and theme tokens are singletons — they don't fit the collection-shaped Astro API. Use the plain helpers:

---
import { getIdentity, getNav, getThemeTokens } from '@clearcms/astro';

const identity = await getIdentity();
const nav      = await getNav();
const tokens   = await getThemeTokens();
---
<title>{identity.siteTitle}</title>

These reuse the same env-driven config as clearLoader, so one setup serves both.

Entry shape

Each Astro entry produced by clearLoader has:

| Field | Source | |---|---| | id | clear's item.slug | | data | { ...item.data, _meta: { id, locale, status, publishedAt, scheduledFor, updatedAt, createdAt, authorId, canonicalId, baseId, deletedAt } } | | digest | item.updatedAt (lets Astro skip re-loads when nothing changed) | | body / rendered | Not set. clear stores richtext as TipTap JSON, not markdown — there is no raw "body string" to surface. |

So in templates:

  • post.data.title — your content fields (whatever your collection defines).
  • post.data._meta.publishedAt — clear's protocol-level metadata.

Caveat: if you literally have a field named _meta on your collection it'll be shadowed by the protocol metadata. Pick a different field slug.

Richtext fields (TipTap JSON)

clear stores kind: 'richtext' fields as TipTap / ProseMirror JSON, not HTML or markdown. The loader passes the JSON through unchanged. To render:

  • renderTipTap() (recommended): ships in @clearcms/astro. Thin wrapper over @tiptap/html configured with the same StarterKit extensions the admin uses, plus Image. Returns '' for null/empty input, so it's safe to pipe straight into set:html.

    ---
    import { renderTipTap } from '@clearcms/astro';
    ---
    <article set:html={renderTipTap(post.data.body)} />

    Covered: paragraph, headings (h1–h6), bullet/ordered lists, blockquote, code (inline + block), horizontal rule, hard break, image, link, bold/italic/underline/strike marks. If you need extra extensions, drop down to @tiptap/html directly.

  • React / Vue / Svelte islands: use the framework-specific TipTap renderer.

  • Server-side reshape: if you're on the REST backend you can hit the admin's /api/v1/{collection}/{slug}?format=html directly — clearLoader doesn't expose that today; open an issue if you need it.

Type narrowing

clearLoader does not ship a Zod schema. @clearcms/sdk already validates every read against the protocol; layering a second schema on top would just duplicate it.

If you want extra type narrowing on top of a specific collection, add a schema to defineCollection yourself:

import { defineCollection, z } from 'astro:content';
import { clearLoader } from '@clearcms/astro';

export const collections = {
  posts: defineCollection({
    loader: clearLoader('posts'),
    schema: z.object({
      title: z.string(),
      subtitle: z.string().optional(),
      body: z.unknown(), // TipTap JSON
      _meta: z.object({
        publishedAt: z.string().nullable(),
        updatedAt: z.string(),
        // ...whichever fields you actually consume
      }).passthrough(),
    }),
  }),
};

Configuration (env)

Both clearLoader and the global helpers resolve their backend in this priority order:

  1. Explicit client opt passed to the call.
  2. Process env (below).

| Var | Backend | Required | Default | Notes | |---|---|---|---|---| | CLEAR_BACKEND | * | no | rest | one of fs / rest / r2 / s3 | | CLEAR_LOCALE | * | no | (sdk default en for .get, all locales for .list) | overrideable per-clearLoader call via { locale } | | CLEAR_ADMIN_URL | rest | no | http://localhost:3000 | | | CLEAR_API_TOKEN | rest | no | — | required only for non-published reads | | CLEAR_BUCKET_ROOT | fs | yes | — | absolute path to a clear bucket (the dir containing collections/, content/, theme/, …) | | CLEAR_R2_ACCOUNT_ID | r2 | yes | — | | | CLEAR_R2_BUCKET | r2 | yes | — | | | CLEAR_R2_ACCESS_KEY_ID | r2 | yes | — | | | CLEAR_R2_SECRET_ACCESS_KEY | r2 | yes | — | | | CLEAR_S3_ENDPOINT | s3 | yes | — | full https URL | | CLEAR_S3_BUCKET | s3 | yes | — | | | CLEAR_S3_REGION | s3 | yes | — | | | CLEAR_S3_ACCESS_KEY_ID | s3 | yes | — | | | CLEAR_S3_SECRET_ACCESS_KEY | s3 | yes | — | |

These are consumer-side env vars — they're independent of the admin's internal CLEAR_STORAGE_ROOT / CLEAR_DB_URL.

Static build deploy

The whole point of this package: build a site to plain HTML against a clear bucket, deploy dist/ anywhere a CDN runs, no admin in the hot path.

# CI / pre-deploy step:
export CLEAR_BACKEND=fs
export CLEAR_BUCKET_ROOT=/var/data/my-project/bucket   # synced from the admin
astro build

# Outputs: ./dist/  — plain HTML/JS/CSS. Deploy to Netlify / Cloudflare
# Pages / S3+CloudFront / GitHub Pages / nginx static / wherever.

R2 / S3 work the same — just swap the env vars:

export CLEAR_BACKEND=r2
export CLEAR_R2_ACCOUNT_ID=...
export CLEAR_R2_BUCKET=my-clear-bucket
export CLEAR_R2_ACCESS_KEY_ID=...
export CLEAR_R2_SECRET_ACCESS_KEY=...
astro build

In dev (astro dev) you typically point at a running admin via the REST backend — that's the default.

Multiple buckets / advanced

Pass an explicit client to mix buckets in one Astro project:

import { defineCollection } from 'astro:content';
import { clearLoader } from '@clearcms/astro';

export const collections = {
  blog: defineCollection({
    loader: clearLoader('posts', {
      client: { backend: 'fs', root: '/data/blog/bucket' },
    }),
  }),
  docs: defineCollection({
    loader: clearLoader('articles', {
      client: { backend: 'rest', adminUrl: 'https://docs.example.com' },
    }),
  }),
};

The same client opt is on getIdentity / getNav / getThemeTokens.

Errors

The loader propagates @clearcms/sdk errors. The most common is ClearSdkError with .code of 'transport' (network failure), 'parse' (a bucket file disagreed with the protocol schema), 'config' (env vars missing), or 'unauthorized' (REST backend, draft read without a token).

getEntry() returns undefined for a missing slug — clearLoader already wiped/repopulated the store, so misses are expressed as Astro misses, not thrown errors.

License

MIT.