Skip to content

VitePress Sites

Partner Documentation

VitePress is a supported framework for DCS sites. This guide covers VitePress-specific integration using the official @duffcloudservices/cms package.

Why VitePress?

VitePress offers:

  • Fast — Built on Vite, near-instant hot reload
  • Simple — Markdown-based content
  • Flexible — Vue components for custom features
  • SEO-friendly — Static generation with good defaults

Project Structure

A typical DCS VitePress site:

my-site/
├── .dcs/
│   ├── site.yaml           # Site identity + Azure/deploy config
│   ├── pages.yaml          # Managed page registry
│   ├── content.yaml        # Portal-managed text content
│   └── seo.yaml            # Portal-managed SEO
├── .github/
│   ├── copilot-instructions.md
│   └── workflows/
│       └── site-deploy.yml # Generated by `dcs init` (OIDC deploy)
├── .vitepress/
│   ├── config.ts
│   └── theme/
│       └── index.ts
├── docs/
│   ├── index.md
│   └── about.md
├── public/
│   └── images/
├── .env
└── package.json

Installation

1. Create a VitePress Project

bash
pnpm create vitepress

2. Initialize DCS Integration

Use the DCS CLI to scaffold the .dcs config, the deployment workflow, and the integration plans:

bash
npm install -g @duffcloudservices/cli
dcs login
dcs init -s my-site -n "My Site"

3. Install the CMS Package

bash
pnpm add @duffcloudservices/cms

Configure the Vite Plugins

Register the DCS content and SEO plugins in your VitePress config. They inject .dcs/content.yaml and .dcs/seo.yaml at build time, so managed text and SEO ship with the built site at zero runtime cost:

typescript
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
import { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms/plugins'

export default defineConfig({
  title: 'My Site',
  description: 'A DCS-powered site',

  vite: {
    plugins: [
      dcsContentPlugin(),
      dcsSeoPlugin()
    ]
  }
})

Text Content Integration

Use the useTextContent composable for editable copy. Pass hardcoded defaults so the page renders correctly before any portal edits exist:

vue
<!-- .vitepress/theme/components/Hero.vue -->
<script setup lang="ts">
import { useTextContent } from '@duffcloudservices/cms'

const { t } = useTextContent({
  pageSlug: 'home',
  defaults: {
    'hero.title': 'Welcome',
    'hero.subtitle': 'This is the default subtitle.'
  }
})
</script>

<template>
  <section class="hero">
    <h1>{{ t('hero.title') }}</h1>
    <p>{{ t('hero.subtitle') }}</p>
  </section>
</template>

Register the component in your theme and use it in markdown:

typescript
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import type { Theme } from 'vitepress'
import Hero from './components/Hero.vue'

export default {
  extends: DefaultTheme,
  enhanceApp({ app }) {
    app.component('Hero', Hero)
  }
} satisfies Theme

Text falls back through: runtime API overrides (runtime mode only) → build-time content from .dcs/content.yaml → the defaults you pass to the composable.

SEO Integration

Apply portal-managed SEO with the useSEO composable:

vue
<script setup lang="ts">
import { useSEO } from '@duffcloudservices/cms'

const { applyHead } = useSEO('home', '/')
applyHead()
</script>

applyHead sets the meta tags, Open Graph, Twitter Cards, canonical URL, and JSON-LD schemas defined in .dcs/seo.yaml.

Environment Variables

Managed text and SEO are injected at build time, so a standard (commit-mode) site needs no runtime API configuration. The only variables you may set relate to runtime overrides:

bash
# .env
# For runtime overrides (premium tier): the API base URL only.
VITE_API_BASE_URL=https://portal.duffcloudservices.com
VITE_TEXT_OVERRIDE_MODE=commit  # 'commit' (default) or 'runtime'

The site slug is resolved server-side (from the request Host, or the dedicated Container App's DCS_SITE_SLUG) and is never placed in request URLs.

Deployment

dcs init generates .github/workflows/site-deploy.yml, which deploys to Azure Static Web Apps using OpenID Connect (OIDC) — there is no long-lived Static Web App token stored in the repository. The workflow authenticates with azure/login@v2, exchanges the OIDC token with the DCS API for a short-lived deployment token, builds, and deploys.

You do not hand-write this workflow. See GitHub Setup for the OIDC federated-credential setup and Deployment for the full flow.

Best Practices

Performance

  • Use content-visibility: auto for long pages
  • Lazy load images below the fold
  • Minimize custom JavaScript

SEO

  • Set portal SEO for each managed page
  • Use semantic HTML and proper heading hierarchy
  • Include alt text for images

Accessibility

  • Use a correct heading hierarchy
  • Ensure sufficient color contrast
  • Test with keyboard navigation

Troubleshooting

Text Not Updating

  1. Confirm the DCS plugins are registered in .vitepress/config.ts
  2. Rebuild the site — commit-mode content is injected at build time
  3. In commit mode, edits appear only after the site is redeployed

Build Failures

  1. Check for missing dependencies
  2. Verify .dcs/content.yaml and .dcs/seo.yaml are valid
  3. Run dcs validate

Hot Reload Issues

  1. Clear the Vite cache: rm -rf node_modules/.vite
  2. Restart the dev server
  3. Check for syntax errors

Next Steps