Skip to content

CMS Framework Adapters

Beyond the Vue-based @duffcloudservices/cms, Duff Cloud Services ships CMS adapters for other frontend frameworks so managed content and SEO work the same way on React, Angular, and Astro sites. All of them build on one shared core package and one shared configuration contract, which is described once below and then applied per framework.

The shared contract

Every adapter reads the same two files from your repository's .dcs/ directory:

  • content.yaml — versioned text content, with a global block and per-page overrides keyed by text keys such as hero.title.
  • seo.yaml — versioned SEO configuration: a global block (site name, default title, title template, default images) and per-page title, description, and Open Graph settings.
yaml
# .dcs/content.yaml
version: 1
global:
  nav.home: Home
pages:
  home:
    hero.title: Welcome to Our Site

The pattern is identical everywhere: text is resolved by page and key (page-specific values override globals), and SEO is resolved per page and merged with your global defaults. Only the framework-specific glue changes.

Under the hood every adapter depends on @duffcloudservices/cms-core (current version 0.6.2), which provides the shared TypeScript types, the content and SEO resolution utilities, the YAML loaders, and the runtime-fetch helper. The three framework adapters are each at version 0.3.2.

React — @duffcloudservices/cms-react

React hooks plus Vite plugins. Add the content and SEO plugins to your Vite config, then use the hooks in components:

typescript
// vite.config.ts
import { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms-react/plugins'

export default defineConfig({
  plugins: [react(), dcsContentPlugin(), dcsSeoPlugin()],
})
tsx
import { useTextContent, useSEO } from '@duffcloudservices/cms-react'

export function Home() {
  const { t } = useTextContent({ page: 'home', defaults: { 'hero.title': 'Welcome' } })
  useSEO({ page: 'home' })
  return <h1>{t('hero.title')}</h1>
}
  • useTextContent({ page, defaults }) returns t(key, fallback?) plus the resolved content, loading and error state, and a refresh().
  • useSEO({ page, overrides? }) resolves the page's meta tags.

Peer requirement: React ^18 or ^19.

Angular — @duffcloudservices/cms-angular

Angular services plus a post-build injection step. Register the provider, inject the services, and run the injector after ng build:

typescript
// app.config.ts
import { provideDcs } from '@duffcloudservices/cms-angular'
export const appConfig = { providers: [provideDcs()] }
typescript
import { DcsContentService, DcsSeoService } from '@duffcloudservices/cms-angular'
// content.t('home', 'hero.title', 'Welcome')
// seo.setPageSeo('home')

Because Angular renders differently from a Vite pipeline, content is injected into the built output with the bundled dcs-inject CLI:

bash
ng build && npx dcs-inject dist/my-app
  • DcsContentService exposes t(page, key, fallback?), getPageContent(page), and content signals.
  • DcsSeoService exposes setPageSeo(page, overrides?) and getSeoConfig().

Peer requirement: Angular ^17, ^18, or ^19 (@angular/core, @angular/common, @angular/platform-browser).

Astro — @duffcloudservices/cms-astro

An Astro integration plus runtime helpers. Add the integration, then call the helpers from your frontmatter:

typescript
// astro.config.mjs
import dcs from '@duffcloudservices/cms-astro'
export default defineConfig({ integrations: [dcs()] })
astro
---
import { t, getPageSeo, getPageContent } from '@duffcloudservices/cms-astro'
const seo = getPageSeo('home')
---
<title>{seo?.title}</title>
<h1>{t('home', 'hero.title', 'Welcome')}</h1>
  • t(page, key, fallback?) resolves a single text key.
  • getPageContent(page) returns all content for a page (globals merged with page-specific).
  • getPageSeo(page, overrides?) returns resolved SEO with optional overrides.

Peer requirement: Astro ^4 or ^5.

Core — @duffcloudservices/cms-core

The framework packages all sit on @duffcloudservices/cms-core, which you rarely import directly but which does the real work: content resolution (resolveTextKey, getPageContent), SEO resolution (resolveSeoForPage, buildMetaTags), YAML loading (loadContentYaml, loadSeoYaml), and runtime fetching (fetchRuntimeContent).

Runtime content is a premium feature

By default, content is resolved at build time from your .dcs files. Runtime fetching — pulling the latest content live from the DCS API so portal edits appear without rebuilding — is a premium-tier capability. On non-premium sites the runtime helpers simply return nothing, and your site keeps serving its build-time content.

Publishing and provenance

These packages are published to the public npm registry through GitHub Actions OIDC trusted publishing — there is no long-lived npm token to leak, and each release is tied to the workflow that produced it.