@eyeballs/cms
v2.5.1
Published
Shared Eyeballs CMS API types, client helpers, and documentation.
Downloads
1,822
Readme
@eyeballs/cms
Shared Eyeballs CMS API types, client helpers, Next.js revalidation helpers, and API docs for starter sites and custom website projects.
Install In A Website Project
npm install @eyeballs/cmsVercel installs the package during build like any other npm dependency. Keep site-specific secrets in the Vercel project environment:
EYEBALLS_CMS_URL=https://app.example.com
EYEBALLS_CMS_TOKEN=eyb_...
EYEBALLS_CMS_VERCEL_BYPASS=...
REVALIDATE_SECRET=eyb_reval_...EYEBALLS_CMS_VERCEL_BYPASS is only needed when the CMS deployment uses Vercel Deployment
Protection. VERCEL_AUTOMATION_BYPASS_SECRET is also a useful fallback name in website projects.
Basic Usage
import { createEyeballsCms, type CmsVideo } from '@eyeballs/cms';
const cms = createEyeballsCms({
baseUrl: process.env.EYEBALLS_CMS_URL!,
token: process.env.EYEBALLS_CMS_TOKEN!,
});
const videos: CmsVideo[] = await cms.videos.list();defaultRequestInit.headers can supply any other deployment-specific request headers. The client
always adds the CMS bearer token and can add x-vercel-protection-bypass through the dedicated
vercelProtectionBypass option.
Image Delivery
Configure the delivery hostname per environment and use the provider-neutral image entry point:
NEXT_PUBLIC_EYEBALLS_MEDIA_URL=https://images.eyeballs.coimport { createImageDelivery } from '@eyeballs/cms/images';
const images = createImageDelivery({
baseUrl: process.env.NEXT_PUBLIC_EYEBALLS_MEDIA_URL!,
});
const src = images.getUrl(profile.image_asset ?? profile.image_url!, {
width: 800,
height: 1000,
fit: 'cover',
quality: 75,
});The helper accepts structured assets, legacy Supabase URLs, and Bunny delivery URLs. It supports
width-only, height-only, contain, centred cover, quality, explicit formats, and responsive
width-descriptor srcset generation. Normal Bunny responses use negotiated WebP.
Recommended Next.js Adapter
Keep one small server-only adapter in each website. The Next client applies a one-hour default revalidation window and the tag names used by Eyeballs revalidation webhooks:
// src/lib/cms.ts
import type { CmsContentEntry, CmsFeaturedContentItem, CmsVideo } from '@eyeballs/cms';
import { createEyeballsNextCms } from '@eyeballs/cms/next';
import { cache } from 'react';
type HomeFields = {
'hero-title'?: string;
'featured-work'?: CmsVideo[];
'featured-content'?: CmsFeaturedContentItem[];
};
const cms = createEyeballsNextCms({
baseUrl: process.env.EYEBALLS_CMS_URL!,
token: process.env.EYEBALLS_CMS_TOKEN!,
revalidate: 3600,
vercelProtectionBypass:
process.env.EYEBALLS_CMS_VERCEL_BYPASS || process.env.VERCEL_AUTOMATION_BYPASS_SECRET,
});
export const getHome = cache(async () => {
return cms.content.getBySlug<HomeFields>('home');
});
export type HomeEntry = CmsContentEntry<HomeFields>;The helper assigns work, work:{slug}, roster, roster-profile:{slug}, content,
content-type:{typeSlug}, and content:{entrySlug} tags as appropriate. Use
eyeballsCmsCacheTags and a method's tags option when a site needs a route-specific override,
such as roster:directors.
Next.js Revalidation Route
// app/api/revalidate/route.ts
import { createEyeballsRevalidateHandler } from '@eyeballs/cms/next';
export const POST = createEyeballsRevalidateHandler({
secret: process.env.REVALIDATE_SECRET,
});The handler preserves Next's default revalidatePath(path) behavior. Pass pathType: 'page' or
pathType: 'layout' only when a site intentionally needs that scope.
Agent Instructions For Starter Sites
Add this to the starter site's AGENTS.md:
Before using the Eyeballs CMS API, read:
node_modules/@eyeballs/cms/docs/content-api.mdPublishing
Build locally:
npm run cms:buildPreview the npm package contents:
npm run cms:pack:dryPublish after bumping packages/cms/package.json version:
npm run cms:publishThis package contains no account tokens or site-specific config.
