# Workflow: CMS-ify a Page

Convert a hard-coded page with static content into a CMS-driven page using blocks. This workflow takes you from React components with Tailwind styling to fully editable CMS content.

## Overview

The typical workflow:

1. **Design first** - Build a hard-coded page with static content and Tailwind styling
2. **Identify sections** - Map each section to potential blocks
3. **Choose block approach** - For each section, decide: system block, block extension, or custom block
4. **Create overrides** - Build React components that render your blocks
5. **Convert content** - Transform static content to block JSON
6. **Push to CMS** - Sync everything to the CMS

## Prerequisites

- Hard-coded page designed with static content + Tailwind CSS
- `riverbank.config.ts` set up
- Understanding of available system blocks (see `block-types.md`)

## Steps

### Step 1: Design with Hard-coded Content

Start by building your page with static content. Focus on:

- Visual design and layout
- Responsive behavior
- Tailwind styling
- Interactive elements

Example starting point:

```tsx
// pages/services.tsx (hard-coded version)
export default function ServicesPage() {
  return (
    <>
      <section className="py-24 bg-gradient-to-b from-teal-900 to-teal-700">
        <h1 className="text-5xl font-bold text-white">Our Services</h1>
        <p className="text-xl text-teal-100">We help businesses grow online</p>
      </section>

      <section className="py-16 bg-white">
        <h2 className="text-3xl font-bold">Our Approach</h2>
        <div className="grid grid-cols-2 gap-8">
          <div className="p-8 bg-green-50">
            <h3>Discovery</h3>
            <p>We learn about your business...</p>
          </div>
          <div className="p-8 bg-tan-50">
            <h3>Design</h3>
            <p>We create beautiful designs...</p>
          </div>
        </div>
      </section>

      <section className="py-16 bg-gray-900 text-white">
        <h2>Ready to get started?</h2>
        <a href="/contact" className="btn-primary">Contact Us</a>
      </section>
    </>
  );
}
```

### Step 2: Identify Sections → Map to Blocks

Analyze each section and determine what type of block it could be:

| Page Section | Analysis | Potential Block |
|--------------|----------|-----------------|
| Hero with gradient | Heading + subheading + background | `block.hero` |
| Approach tiles | Grid of colored cards | `custom.color-tiles` or `block.body-text` with layout |
| CTA banner | Dark background with button | `block.cta-full` with layout variant |

### Step 3: Choose Block Approach

For each section, decide which approach to use:

```
Is this a standard content section?
├─ Yes → Can a system block handle it as-is?
│  ├─ Yes → Use system block directly
│  └─ No → Do you just need visual variants?
│     ├─ Yes → Use block extension (add layout field)
│     └─ No → Create custom block
└─ No → Is it site-specific branding/interaction?
   ├─ Yes → Create custom block
   └─ No → Reconsider if block is needed
```

**Decision matrix for our example:**

| Section | Decision | Reason |
|---------|----------|--------|
| Hero | `block.hero` + layout extension | Standard hero, but needs custom background style |
| Approach tiles | `custom.color-tiles` | Unique grid layout with colored backgrounds |
| CTA banner | `block.cta-full` + layout extension | Standard CTA, but needs dark variant |

### Step 4: Implement Block Strategy

Based on your decisions, implement each approach:

#### 4a: System Block As-Is

If a system block works without modification:

```bash
riverbankcms block upsert services intro \
  --kind block.body-text \
  --data '{"content": "<p>Welcome to our services page...</p>"}'
```

#### 4b: System Block + Layout Extension

For blocks that need visual variants, add field extensions:

```typescript
// riverbank.config.ts
blockFieldExtensions: {
  'block.hero': {
    fields: [
      {
        id: 'layout',
        type: 'select',
        label: 'Layout',
        options: [
          { value: 'default', label: 'Default' },
          { value: 'gradient', label: 'Gradient Background' },
        ],
        defaultValue: 'default',
      },
    ],
  },
  'block.cta-full': {
    fields: [
      {
        id: 'layout',
        type: 'select',
        label: 'Layout',
        options: [
          { value: 'default', label: 'Default' },
          { value: 'dark', label: 'Dark Theme' },
        ],
        defaultValue: 'default',
      },
    ],
  },
}
```

Then create override components (see `block-extensions.md`).

#### 4c: Custom Block

For unique sections, define a custom block:

```typescript
// riverbank.config.ts
customBlocks: [
  {
    id: 'custom.color-tiles',
    title: 'Color Tiles',
    fields: [
      { id: 'heading', type: 'text', label: 'Heading' },
      {
        id: 'tiles',
        type: 'repeater',
        label: 'Tiles',
        schema: {
          fields: [
            { id: 'title', type: 'text', label: 'Title', required: true },
            { id: 'description', type: 'text', label: 'Description', multiline: true },
          ],
        },
      },
    ],
  },
]
```

Then create an override component (see `custom-block.md`).

### Step 5: Create Block Overrides

Register all your override components:

```typescript
// src/lib/block-overrides.tsx
import type { BlockOverrides } from '@riverbankcms/sdk/rendering';
import { CustomHero } from '@/components/CustomHero';
import { CustomCtaFull } from '@/components/CustomCtaFull';
import { ColorTilesBlock } from '@/components/ColorTilesBlock';

export const blockOverrides: BlockOverrides = {
  hero: CustomHero,
  ctaFull: CustomCtaFull,
  'custom.color-tiles': ColorTilesBlock,
};
```

### Step 6: Convert Content to Block JSON

Transform your static content into block data:

**Hero block:**
```json
{
  "heading": "Our Services",
  "subheading": "We help businesses grow online",
  "layout": "gradient"
}
```

**Color tiles block:**
```json
{
  "heading": "Our Approach",
  "tiles": [
    { "title": "Discovery", "description": "We learn about your business..." },
    { "title": "Design", "description": "We create beautiful designs..." }
  ]
}
```

**CTA block:**
```json
{
  "heading": "Ready to get started?",
  "cta": { "text": "Contact Us", "url": "/contact" },
  "layout": "dark"
}
```

### Step 7: Create Page and Push

Create the page and add blocks:

```bash
# Create the page
riverbankcms page upsert services \
  --title "Our Services" \
  --path /services

# Add blocks
riverbankcms block upsert services hero \
  --kind block.hero \
  --data '{"heading": "Our Services", "subheading": "We help businesses grow online", "layout": "gradient"}'

riverbankcms block upsert services approach \
  --kind custom.color-tiles \
  --file ./content/services-tiles.json

riverbankcms block upsert services cta \
  --kind block.cta-full \
  --data '{"heading": "Ready to get started?", "layout": "dark"}'

# Push everything
riverbankcms push
```

## Complete Example

Converting a services page from the Riverbank site:

**Original hard-coded sections:**
1. Hero with animated canvas background
2. Color tiles grid (4 approach points)
3. Feature preview with parallax
4. Pricing tiers comparison
5. Dark CTA banner

**Final block structure:**

```
services.json
├── hero (block.hero + layout: 'topography')
├── approach (custom.color-tiles)
├── features (custom.feature-preview)
├── pricing (custom.pricing-tiers)
└── cta (block.cta-full + layout: 'dark')
```

**Config additions:**

```typescript
// Block extensions for system blocks
blockFieldExtensions: {
  'block.hero': {
    fields: [
      { id: 'layout', type: 'select', options: [...], defaultValue: 'default' },
      { id: 'topographyColor', type: 'select', ... },
    ],
  },
  'block.cta-full': {
    fields: [
      { id: 'layout', type: 'select', options: [...], defaultValue: 'default' },
    ],
  },
}

// Custom blocks for unique sections
customBlocks: [
  { id: 'custom.color-tiles', ... },
  { id: 'custom.feature-preview', ... },
  { id: 'custom.pricing-tiers', ... },
]
```

## Verification

1. Preview blocks: `riverbankcms preview <kind> --data '...' --open`
2. Check page in local development: `npm run dev`, visit `/services`
3. Verify all sections render correctly
4. Test responsive behavior
5. Check CMS dashboard for editing experience

## Tips

### Preserving Design Fidelity

- Keep your Tailwind styles in override components
- Use theme tokens for colors when available
- Test responsive behavior after CMS-ifying

### When to Create Custom Blocks vs Extensions

| Scenario | Approach |
|----------|----------|
| Standard content with visual variants | Block extension |
| Unique layout or structure | Custom block |
| Site-specific interactions (animations, state) | Custom block |
| Reusable across multiple sites | Consider extending system block |

### Gradual Migration

You don't have to CMS-ify everything at once:

1. Start with content that changes frequently
2. Keep complex interactions as static components initially
3. Add more blocks over time as patterns emerge

### Maintaining Block Overrides

- Keep override components focused and small
- Extract shared logic into utility functions
- Use TypeScript for content type safety

## Related Workflows

- **Block Extensions** - Add layout variants to system blocks: see `block-extensions.md`
- **Custom Blocks** - Create site-specific blocks: see `custom-block.md`
- **Content Types** - Create reusable content entries: see `content-types.md`
- **Create a Page** - Create pages via CLI: see `create-page.md`
- **Add a Block** - Add blocks to pages: see `add-block.md`
