---
name: cms-component-builder
description: Build Optimizely CMS components for Astro v5. Use when creating/modifying components, pages, experiences, or working with GraphQL fragments, opti-type.json, or opti-style.json files (project)
---
# CMS Component Builder
Build production-ready Optimizely CMS components with proper GraphQL integration, daisyUI styling, and TypeScript type safety.
## When to Use This Skill
**ALWAYS use this skill when the user asks to:**
- Create a new component, page, or experience (e.g., "create a testimonial component", "add a new page type")
- Modify or update `.opti-type.json` files (content type definitions)
- Modify or update `.opti-style.json` files (style/display template definitions)
- Create or modify GraphQL fragments (`.graphql` or `.dam.graphql` files)
- Add components to `allComponents.graphql` or similar aggregation files
- Debug component rendering issues
- Fix GraphQL type errors related to components
- Push/sync content types or styles to the CMS
- Understand how the component system works
## Critical Instructions
**YOU MUST FOLLOW THESE STEPS IN ORDER:**
1. **READ THE GUIDES FIRST** - This skill includes comprehensive guides that YOU MUST reference:
- `CONTENTTYPE-GUIDE.md` - For creating `.opti-type.json` files
- `STYLE-GUIDE.md` - For creating `.opti-style.json` files
- `GRAPHQL-PATTERNS.md` - For creating `.graphql` and `.dam.graphql` files
2. **EXAMINE EXISTING COMPONENTS** - Always look at similar existing components as examples before creating new ones. Check `src/cms/components/` for patterns.
3. **CREATE ALL REQUIRED FILES** - A complete component needs **5 files minimum**:
- `.astro` - Component template with TypeScript
- `.opti-type.json` - Content type definition
- `.opti-style.json` - Style definition(s) (can have multiple)
- `.graphql` - Base GraphQL fragment
- `.dam.graphql` - DAM-enabled GraphQL fragment
4. **INTEGRATE PROPERLY** - After creating files:
- Add fragment to `src/cms/components/allComponents.graphql`
- Push to CMS: `yarn type:push ComponentName` and `yarn style:push StyleName`
- Wait 10 seconds for Optimizely Graph sync
- Generate types: `yarn codegen`
## Quick Start Example
Creating a new "Testimonial" component:
```bash
# 1. Create component directory
mkdir -p src/cms/components/TestimonialComponent
# 2. Create required files
src/cms/components/TestimonialComponent/
├── Testimonial.astro # Component template
├── Testimonial.opti-type.json # Content type definition
├── DefaultTestimonial.opti-style.json # Default style
├── testimonial.graphql # Base GraphQL fragment
└── testimonial.dam.graphql # DAM GraphQL fragment
```
## Required Files Explained
Every component needs **at least 5 files**:
### 1. `.astro` - Component Template
**IMPORTANT**: Always follow the pattern from existing components like `Button.astro`
```typescript
---
import type {
TestimonialFragment,
DisplaySettingsFragment,
} from '../../../../__generated/sdk';
import type { ContentPayload } from '../../../graphql/shared/ContentPayload';
import { isEditContext } from '../../shared/utils.ts';
const isCmsEdit = isEditContext(Astro.url);
export interface Props {
key: string;
data: TestimonialFragment;
displaySettings: DisplaySettingsFragment[];
displayTemplateKey: string;
contentPayload: ContentPayload;
}
const { key, data, displaySettings, displayTemplateKey, contentPayload } = Astro.props as Props;
// Your styling logic here - reference STYLE-GUIDE.md
const componentClass = 'component-testimonial';
---
{data.Author}
```
**Key Points:**
- Import types from `__generated/sdk` (NOT from `@/graphql/__generated/graphql`)
- Include ALL required props: `key`, `data`, `displaySettings`, `displayTemplateKey`, `contentPayload`
- Use `data-epi-block-id` for CMS editing context
- Use `set:html` for rich text fields
- Add a component class for CSS targeting
### 2. `.opti-type.json` - Content Type
See `CONTENTTYPE-GUIDE.md` for complete guide on creating content types.
### 3. `.opti-style.json` - Style Definitions
See `STYLE-GUIDE.md` for complete guide on creating style definitions.
### 4. `.graphql` + `.dam.graphql` - GraphQL Fragments
**ALWAYS create both versions.** See `GRAPHQL-PATTERNS.md` for complete details.
**Base (`testimonial.graphql`):**
```graphql
fragment Testimonial on Testimonial {
Quote { html }
Author
AuthorTitle
AuthorImage {
...ContentUrl
}
}
```
**DAM (`testimonial.dam.graphql`):**
```graphql
fragment Testimonial on Testimonial {
Quote { html }
Author
AuthorTitle
AuthorImage {
...ContentUrl
...ContentReferenceItem
}
}
```
## Component Integration
### 1. Add to allComponents.graphql
```graphql
fragment AllComponentsExceptGrid on _IComponent {
...Text
...Button
...Testimonial # Add your component here
}
```
**Important:** Add to `AllComponentsExceptGrid`, NOT `AllComponents`
### 2. Sync to CMS & Generate Types
```bash
# Push content type and styles to CMS
yarn type:push ComponentName
yarn style:push StyleName
# ⚠️ Wait ~10 seconds for Optimizely Graph to sync
# Then generate TypeScript types
yarn codegen
```
### 3. Verify Integration
Check `__generated/graphql.ts` for your component type.
## Component Locations
- **Components:** `src/cms/components/` - Reusable UI (Button, Card, Hero)
- **Pages:** `src/cms/pages/` - Page types (ArticlePage, LandingPage)
- **Experiences:** `src/cms/experiences/` - Experience templates
- **Compositions:** `src/cms/compositions/` - Layout elements (Row, Column)
## GraphQL Shared Fragments
Quick reference (full details in `GRAPHQL-PATTERNS.md`):
- `LinkUrl` - Links with url, title, target, text
- `ContentUrl` - Content reference URLs
- `LinkCollection` - Simple link arrays
- `ContentReferenceItem` - DAM metadata (`.dam.graphql` only)
- `DisplaySettings` - Display/styling settings
- `PageUrl` - Page metadata
**Rich text fields:** Always use `{ html }`:
```graphql
Body { html }
Description { html }
```
## Styling with daisyUI + TailwindCSS
**Prefer daisyUI components:**
```html