# Modyo Snippets Architecture

## Overview

Snippets are reusable Liquid template blocks that power Modyo's rendering engine. They are the foundation of how widgets, grids, and page components are rendered.

---

## What are Snippets?

**Snippets** are small, reusable pieces of Liquid code that can be included in layouts, pages, and other templates using the `{% snippet 'name' %}` tag.

### Key Characteristics

- **Reusable**: Can be included in multiple templates
- **Liquid-based**: Use Liquid markup language
- **Organized by groups**: general, grids, widgets, notifications, service_worker
- **System-managed**: Core snippets are non-deletable (`deletable: false`)
- **Versioned**: Support workflow and publishing like other Modyo resources

---

## Snippet Types and API Endpoints

Modyo has **two types of snippets** with different API endpoints:

### System Snippets (`/templates/snippets`)

**API Endpoint**: `/api/admin/sites/{site_id}/templates/snippets`

**Characteristics**:
- ✅ Provided by Modyo (built-in)
- ✅ Editable (can modify content)
- ❌ Cannot be deleted (`deletable: false`)
- 🔧 Structural base parts of the site
- 🏗️ Header, footer, grids, and core rendering logic

**Purpose**: Global structural components that define how the site works

### Custom Snippets (`/templates/custom_snippets`)

**API Endpoint**: `/api/admin/sites/{site_id}/templates/custom_snippets`

**Characteristics**:
- ✅ User-created
- ✅ Fully editable (`deletable: true`)
- 🎨 Reusable UI components
- 🔄 Can be created, updated, and deleted

**Purpose**: Custom reusable components for your specific site needs

---

## System Snippets (Built-in Structural Components)

**From**: `/api/admin/sites/{site_id}/templates/snippets`

### 1. General Snippets
Global structural elements used across all pages:

- **`head`** - HTML head section with meta tags
- **`header`** - Site header/navigation
- **`footer`** - Site footer
- **`menu`** - Navigation menu rendering
- **`seo`** - SEO meta tags
- **`session`** - User session information
- **`body_tag_manager`** - Analytics in body
- **`head_tag_manager`** - Analytics in head

### 2. Grid Snippets
Define how widgets are laid out on pages:

- **`full_grid`** - Single column (column 0)
- **`full_two_cols_grid`** - Two columns (0, 1)
- **`full_three_cols_grid`** - Three columns (0, 1, 2)
- **`side_left_grid`** - Sidebar left + main
- **`side_right_grid`** - Main + sidebar right
- **`side_left_one_col_grid`** - Narrow left sidebar
- **`side_right_one_col_grid`** - Narrow right sidebar
- **`side_left_three_cols_grid`** - Left sidebar + 2 columns
- **`side_right_three_cols_grid`** - 2 columns + right sidebar

### 3. Widget Snippets
Handle widget rendering based on type:

- **`custom_widget`** - Renders custom widgets (normal or CLI)
- **`rich_text_widget`** - Renders rich text content
- **`text_widget`** - Renders plain text content

### 4. Notification Snippets
System notifications:

- **`notifications_html`** - Notification HTML structure
- **`notifications_css`** - Notification styles
- **`notifications_js`** - Notification JavaScript

### 5. Service Worker Snippets
Progressive Web App support:

- **`service_worker_js`** - Service worker code
- **`register_js`** - Service worker registration

**All system snippets** have `deletable: false` - their content can be edited and customized, but they cannot be deleted from the system

---

## Critical Snippet: `custom_widget`

This is the **most important snippet** for understanding widget rendering.

### Full Code

```liquid
<script nonce="{{csp_nonce}}">
  window['resourceBasePath-{{widget.wid}}'] = "{{site.url}}/widget_manager/{{widget.wid}}/{{widget.version}}/";
</script>

{% if widget.sync %}
  <style nonce="{{csp_nonce}}">
    {{ widget.css }}
  </style>
  {{ widget.html }}
  <script nonce="{{csp_nonce}}">
    (function(){
      {{ widget.js }}
    })();
  </script>

{% else %}
<section class="widget-definition widget-{{ widget.name | replace: ' ','-' | replace: 'ñ','n' | downcase }}" id="modyo-{{widget.manager_uuid}}/{{widget.version}}"></section>
<script nonce="{{csp_nonce}}">
  (function(){
    var css_url = "{{site.url}}/widget_manager/{{widget.manager_uuid}}/{{widget.version}}.css";
    var css;
    css = document.createElement('link');
    css.rel = 'stylesheet';
    css.type = 'text/css';
    css.media = "all";
    css.href = css_url;
    css.nonce = '{{ csp_nonce }}';
    document.getElementsByTagName("head")[0].appendChild(css);
  })();
</script>

{% endif %}
```

### Behavior Analysis

#### When `widget.sync = true` (Synchronous)

**What happens**:
1. CSS injected inline in `<style>` tag
2. HTML rendered directly in page
3. JavaScript executed immediately in inline `<script>` tag
4. All happens during page render (render-blocking)

**Result**: Widget content appears immediately with page load but blocks rendering.

#### When `widget.sync = false` (Asynchronous)

**What happens**:
1. Empty `<section>` placeholder created with widget ID
2. CSS loaded asynchronously via dynamically created `<link>` tag
3. HTML and JS loaded after page renders (via widget manager endpoint)
4. Widget populates placeholder after page is already visible

**Result**: Page renders faster, widget content appears shortly after (non-blocking).

### Key Variables Available in Snippet

```liquid
{{ widget.wid }}            // Widget instance ID
{{ widget.version }}        // Widget version number
{{ widget.sync }}           // Boolean: sync/async mode
{{ widget.css }}            // Widget CSS code
{{ widget.html }}           // Widget HTML code
{{ widget.js }}             // Widget JavaScript code
{{ widget.name }}           // Widget name
{{ widget.manager_uuid }}   // Widget definition UUID (OID)
{{ site.url }}              // Site base URL
{{ csp_nonce }}             // Content Security Policy nonce
```

---

## Critical Snippet: Grid Layouts

Grid snippets define how widgets are positioned on pages.

### Example: `full_grid`

```liquid
{% for widget in page_grid.main_widgets %}
{% snippet widget %}
{% endfor %}
```

**Behavior**:
- Loops through all widgets in `main_widgets` array
- Renders each widget using appropriate snippet (custom_widget, rich_text_widget, etc.)
- Single column layout (all widgets in column 0)

### Example: `full_three_cols_grid`

```liquid
<div class="row">
  <div class="col-md-4">
    {% for widget in page_grid.column_0 %}
    {% snippet widget %}
    {% endfor %}
  </div>
  <div class="col-md-4">
    {% for widget in page_grid.column_1 %}
    {% snippet widget %}
    {% endfor %}
  </div>
  <div class="col-md-4">
    {% for widget in page_grid.column_2 %}
    {% snippet widget %}
    {% endfor %}
  </div>
</div>
```

**Behavior**:
- Three columns using Bootstrap grid
- Each column renders its own widgets array
- Widgets positioned in columns 0, 1, 2

### Available Grid Variables

```liquid
{{ page_grid.main_widgets }}   // All widgets (for single column)
{{ page_grid.column_0 }}       // Widgets in column 0
{{ page_grid.column_1 }}       // Widgets in column 1
{{ page_grid.column_2 }}       // Widgets in column 2
{{ page_grid.sidebar }}        // Sidebar widgets (for side_* grids)
```

---

## How Snippets are Used

### In Layouts

Layouts use snippets to compose the complete HTML structure:

```liquid
{{ html5.open_tag }}
<head>
  {% snippet 'head' %}
  {% snippet 'seo' %}
</head>
<body>
  {% snippet 'header' %}
  <main>
    {{ content_for_layout }}
  </main>
  {% snippet 'footer' %}
</body>
{{ html5.close_tag }}
```

### In Pages

Grid snippets are automatically invoked by `content_for_layout` based on page's `grid_type`:

```typescript
// When page has grid_type: "full_grid"
// content_for_layout renders: {% snippet 'full_grid' %}

// When page has grid_type: "full_three_cols_grid"
// content_for_layout renders: {% snippet 'full_three_cols_grid' %}
```

### Complete Widget Rendering Flow

```
1. Browser requests page (e.g., /homepage)
2. Modyo selects layout (e.g., layouts/default.liquid)
3. Layout renders, encounters {{ content_for_layout }}
4. Modyo checks page.grid_type (e.g., "full_three_cols_grid")
5. Modyo builds page_grid object from page.widgets
6. Modyo invokes grid snippet: {% snippet 'full_three_cols_grid' %}
7. Grid snippet loops through page_grid.column_0, column_1, column_2
8. For each widget, renders: {% snippet widget %}
9. Widget snippet determines type (custom_widget, rich_text, etc.)
10. If custom_widget:
    a. Check widget.sync
    b. If sync=true: inline CSS/HTML/JS (render-blocking)
    c. If sync=false: placeholder + async CSS loading
11. Final HTML sent to browser
12. Async widgets load after page renders
```

---

## Snippet Properties

From the API, snippets have these properties:

```typescript
{
  id: number,              // Snippet ID (CHANGES after publishing - see Versioning below)
  uuid: string,            // Snippet UUID (STABLE - use for references)
  type: string,            // "html", "css", "javascript"
  path: string,            // File path in Modyo
  status: string,          // "published", "draft"
  deletable: boolean,      // Can be deleted (false for system snippets)
  name: string,            // Snippet name (used in {% snippet 'name' %})
  group: string,           // Category: general, grids, widgets, etc.
  body: string,            // Liquid template code
  workflow: object,        // Publishing workflow
  lock_info: object        // Edit lock information
}
```

### Critical: Versioning and ID Stability

**IMPORTANT**: When a snippet or widget is published in Modyo:

1. **ID Changes**: The `id` field will change because Modyo creates a new version
2. **UUID Stable**: The `uuid` field remains constant across versions
3. **Old Version Stored**: Previous version is stored for rollback/history
4. **Rollback Available**: Can revert to previous versions via Modyo UI

**Why This Matters**:
```typescript
// ❌ WRONG - Don't store or reference by ID
const snippetId = 123;
template-update({ templateId: snippetId, ... });  // May fail after publish

// ✅ CORRECT - Always use UUID for references
const snippetUuid = "abc123-def456-...";
// Use UUID to find current ID, then update
```

**Versioning Workflow**:
```
1. Create snippet (id: 100, uuid: "abc123")
2. Edit snippet (still id: 100, uuid: "abc123")
3. Publish snippet → Creates version
   - Old version archived (id: 100, version: 1)
   - New published version (id: 101, uuid: "abc123")
4. Edit again (id: 101, uuid: "abc123")
5. Publish again → Creates another version
   - Version 1 archived (id: 100)
   - Version 2 archived (id: 101)
   - New published version (id: 102, uuid: "abc123")
```

**Rollback Behavior**:
- Can revert to version 1 (id: 100) or version 2 (id: 101)
- After rollback, a new version is created with new ID
- UUID always remains "abc123" throughout all versions

**Best Practices**:
- ✅ Use `uuid` for stable references across versions
- ✅ Fetch current version by UUID before operations
- ✅ Store UUIDs in external systems, not IDs
- ❌ Don't hardcode snippet/widget IDs
- ❌ Don't assume IDs remain constant after publishing

---

## Custom Snippets (User-Created)

**From**: `/api/admin/sites/{site_id}/templates/custom_snippets`

Custom snippets are reusable components you create for your specific site needs. Unlike system snippets, these are fully editable and deletable.

### Use Cases for Custom Snippets

- **UI Components**: Product cards, banners, buttons
- **Repeated Layouts**: Article previews, testimonials, pricing tables
- **Reusable Blocks**: Call-to-action sections, social media embeds
- **Conditional Content**: A/B test variations, personalized content blocks

### Creating Custom Snippets

**Via Tools**:
```typescript
template-create({
  platformSlug: "fed-team",
  siteId: 4605,
  name: "product_card",
  type: "custom_snippet",
  body: `
    <div class="product-card">
      <img src="{{ product.image }}" alt="{{ product.name }}">
      <h3>{{ product.name }}</h3>
      <p>{{ product.description }}</p>
      <span class="price">{{ product.price | money }}</span>
    </div>
  `
})
```

**Via API**: `POST /api/admin/sites/{site_id}/templates`
```json
{
  "type": "custom_snippet",
  "name": "product_card",
  "body": "<!-- Liquid code here -->"
}
```

### Custom Snippet Example

**Snippet Code** (`custom_snippets/product_card`):
```liquid
<div class="product-card">
  <img src="{{ product.image }}" alt="{{ product.name }}">
  <h3>{{ product.name }}</h3>
  <p>{{ product.description }}</p>
  <span class="price">{{ product.price | money }}</span>

  {% if product.on_sale %}
    <span class="badge sale">On Sale!</span>
  {% endif %}
</div>
```

**Usage in Page/Layout/Widget**:
```liquid
<!-- Loop through products -->
{% for product in products %}
  {% snippet 'product_card' with product %}
{% endfor %}

<!-- Or pass parameters -->
{% snippet 'product_card', product: featured_product %}
```

### System vs Custom Snippets

| Aspect | System Snippets | Custom Snippets |
|--------|----------------|-----------------|
| **API Endpoint** | `/templates/snippets` | `/templates/custom_snippets` |
| **Source** | Modyo built-in | User-created |
| **Editable** | ✅ Content can be customized | ✅ Fully editable |
| **Deletable** | ❌ `deletable: false` | ✅ `deletable: true` |
| **Purpose** | Site structure (header, footer, grids) | Reusable UI components |
| **Examples** | `header`, `footer`, `full_grid`, `custom_widget` | `product_card`, `banner`, `cta_section` |
| **Usage** | `{% snippet 'header' %}` | `{% snippet 'product_card' %}` |

### Managing Custom Snippets

**List custom snippets**:
```typescript
template-list-custom-snippets({
  platformSlug: "fed-team",
  siteId: 4605
})
```

**Update custom snippet**:
```typescript
template-update({
  platformSlug: "fed-team",
  siteId: 4605,
  templateId: 123,
  template: { body: "<!-- Updated code -->" }
})
```

**Delete custom snippet**:
```typescript
template-delete({
  platformSlug: "fed-team",
  siteId: 4605,
  templateId: 123
})
```

### Best Practices for Custom Snippets

1. **Naming**: Use descriptive names (`product_card`, not `snippet1`)
2. **Parameters**: Design snippets to accept parameters for flexibility
3. **Documentation**: Add comments explaining expected parameters
4. **Reusability**: Keep snippets focused on single responsibility
5. **Testing**: Test snippets with various data inputs

**Example with Documentation**:
```liquid
{%- comment -%}
  Product Card Snippet

  Parameters:
    - product: Product object with name, image, price, description
    - show_badge (optional): Boolean to show/hide sale badge

  Usage:
    {% snippet 'product_card', product: my_product, show_badge: true %}
{%- endcomment -%}

<div class="product-card">
  <!-- Snippet code -->
</div>
```

---

## Snippet vs Widget

Understanding the difference:

| Feature | Snippet | Widget |
|---------|---------|--------|
| **Purpose** | Reusable Liquid template | Interactive component |
| **Technology** | Liquid markup | HTML/CSS/JS (can be React/Vue/Angular) |
| **Rendering** | Server-side (always) | Server-side or client-side |
| **Positioning** | Included anywhere via `{% snippet %}` | Positioned in grid columns |
| **Use Case** | Templates, layouts, common elements | Interactive features, micro frontends |
| **Can contain** | Liquid code, HTML | Full JavaScript applications |
| **Sync/Async** | N/A (always synchronous) | Configurable via `sync` parameter |

---

## Important Notes

### 1. System Snippets are Read-Only

Core snippets like `custom_widget`, grid snippets, etc. have `deletable: false`. These are maintained by Modyo and define core platform behavior.

### 2. Snippet Rendering is Server-Side

All snippets are rendered server-side by Modyo's Liquid engine before HTML is sent to browser. This is different from widgets which can load asynchronously.

### 3. Grid Snippets Match grid_type

The page's `grid_type` parameter determines which grid snippet is used:

```typescript
grid_type: "full_grid" → {% snippet 'full_grid' %}
grid_type: "full_three_cols_grid" → {% snippet 'full_three_cols_grid' %}
grid_type: "side_left_grid" → {% snippet 'side_left_grid' %}
```

### 4. Widget Snippet Handles Both Normal and CLI Widgets

The `custom_widget` snippet works for:
- Normal widgets (editable in Modyo)
- CLI widgets (read_only, deployed via CLI)
- Bundle format (`zip: false`)
- Zip format (`zip: true`)

The snippet checks `widget.sync` to determine rendering mode, regardless of widget type.

---

## Security: CSP Nonce

Notice `{{ csp_nonce }}` throughout snippet code:

```liquid
<script nonce="{{csp_nonce}}">
  // JavaScript code
</script>
```

**Purpose**: Content Security Policy (CSP) nonce for inline scripts/styles
**Behavior**: Modyo generates unique nonce per request
**Why**: Prevents XSS attacks while allowing inline code

---

## Performance Implications

### Synchronous Widgets (`widget.sync = true`)

**Pros**:
- Content appears immediately
- No layout shift
- Critical content visible instantly

**Cons**:
- Blocks page rendering
- Slower Time to First Byte (TTFB)
- All CSS/JS inline (larger HTML)

### Asynchronous Widgets (`widget.sync = false`)

**Pros**:
- Faster page rendering
- Better Core Web Vitals
- Progressive enhancement
- CSS loaded separately (cacheable)

**Cons**:
- Potential layout shift
- Content appears after page load
- Placeholder visible briefly

---

## Tools Integration

When working with Modyo MCP tools:

### Reading Snippets
```typescript
template-list-snippets({ platformSlug, siteId })
// Returns all snippets

template-get({ platformSlug, siteId, templateId })
// Returns snippet with body code
```

### Modifying Snippets
```typescript
template-update({ platformSlug, siteId, templateId, template: { body } })
// Update snippet code

template-save({ platformSlug, siteId, templateId, body })
// Save snippet body
```

### Creating Custom Snippets
```typescript
template-create({
  platformSlug,
  siteId,
  name: "product_card",
  type: "custom_snippet",
  body: "<!-- Liquid code here -->"
})
```

---
