---
url: https://plugin.stripo.email/extensions/template-modification.md
---
# Template Modification System

## Basic Concept

Using the API call **this.api.getDocumentModifier()**, you can access the TemplateModifier object.

The TemplateModifier provides a controlled way to modify templates:

```javascript
this.api.getDocumentModifier()
    .modifyHtml(immutableNode)     // Select what to modify
    .setInnerHtml('New content')   // Define the modification
    .apply(description);           // Apply with tracking
```

### Modification Flow

```
1. Get Modifier          2. Select Node           3. Chain Modifications
      ↓                        ↓                          ↓
getDocumentModifier() → modifyHtml(node) → setAttribute().setStyle()...
                                                          ↓
                              5. Sync                4. Apply
                                 ↓                        ↓
                        Propagate to Users ← apply(ModificationDescription)
```

This flow ensures that all modifications are:

* **Tracked** - Each change is recorded for version history
* **Atomic** - Related changes are applied together
* **Collaborative** - Changes are synchronized across all users
* **Reversible** - Full undo/redo support is maintained

## Modification Types

### HTML Modifications

The system supports various HTML modifications:

#### Content Modifications

```javascript
.setInnerHtml(html)     // Replace inner HTML
.append(html)           // Add to end
.prepend(html)          // Add to beginning
.replaceWith(html)      // Replace entire element
```

#### Attribute Modifications

```javascript
.setAttribute(name, value)    // Set attribute
.removeAttribute(name)        // Remove attribute
```

#### Text Modifications

```javascript
.setText(text)      // Updates the text content of the HTML text node
```

#### Style Modifications

```javascript
.setStyle(property, value)    // Set CSS property
.removeStyle(property)        // Remove CSS property
```

#### Class Modifications

```javascript
.setClass(className)          // Add CSS class
.removeClass(className)       // Remove CSS class
```

#### Structural Modifications

```javascript
.delete()                     // Remove current node
```

### CSS Modifications

The system also supports CSS modifications:

```javascript
this.api.getDocumentModifier()
    .modifyCss(immutableCssNode)
    .setProperty('color', 'blue')        // Set a CSS property
    .setProperty('font-size', '16px')    // Set a CSS property
    .removeProperty('text-decoration')   // Remove a CSS property
    .apply(description);
```

## MultiRowStructureModifier

### Overview

The `MultiRowStructureModifier` is a specialized helper interface for creating and modifying complex email layouts with multiple rows and columns.
It provides high-level methods to manage email structure containers, handling the intricate details of email-compatible HTML generation.

### When to Use MultiRowStructureModifier

Use the `MultiRowStructureModifier` when you need to:

* **Create multi-row custom blocks** - Build responsive custom blocks with multiple structure rows
* **Modify existing structures** - Change the layout while preserving content
* **Manage container distribution** - Control how content is distributed across containers within the structure

### Why Use MultiRowStructureModifier

Traditional email HTML is complex due to:

1. **Table-based layouts** - Email clients require nested tables for consistent rendering
2. **MSO compatibility** - Outlook needs special conditional comments and markup
3. **Responsive challenges** - Mobile and desktop layouts require different approaches

The `MultiRowStructureModifier` abstracts these complexities by providing:

* **Automatic table structure generation** - No need to manually create complex nested tables
* **Built-in MSO/Outlook compatibility** - Handles all necessary conditional comments and markup
* **Responsive design handling** - Automatically generates mobile-friendly layouts
* **Smart content distribution** - Intelligently places content across containers
* **Image scaling and optimization** - Ensures images render correctly across email clients

### Access and Usage

Access the `MultiRowStructureModifier` through the `multiRowStructureModifier()` method:

```javascript
const modifier = this.api.getDocumentModifier()
    .modifyHtml(structureNode)
    .multiRowStructureModifier();
```

### Methods

#### updateLayoutWithContent

Creates a new structure by replacing the current one with specified containers and content.

```javascript
// Create a three-column layout with mixed container types
modifier.multiRowStructureModifier()
    .updateLayoutWithContent(
        [
            {width: '25%', contentType: 'EMPTY'},  // Empty placeholder
            '50%',                                 // Content column 1
            '25%'                                  // Content column 2
        ],
        [
            `<${BlockType.BLOCK_TEXT}>
                <p>Content 1</p>
            </${BlockType.BLOCK_TEXT}>`,
            `<${BlockType.BLOCK_TEXT}>
                <p>Content 2</p>
            </${BlockType.BLOCK_TEXT}>`
        ]
    )
    .apply(new ModificationDescription('Created three-column layout'));
```

**Parameters:**

* `layout`: Array of `StructureLayout` defining container widths and types
* `containerContent`: Array of HTML strings for content containers

**Container Types:**

* **Content Container** (string) - Width percentage for content-holding containers
* **Empty Container** - `{width: string, contentType: 'EMPTY'}` for placeholders awaiting future content
* **Spacer Container** - `{width: string, contentType: 'SPACER'}` for creating empty space

#### updateLayout

Modifies the container layout of an existing structure while preserving content.

```javascript
// Change from two columns to three columns
modifier.multiRowStructureModifier()
    .updateLayout([
        '33%',     // First column
        '34%',     // Second column
        '33%'      // Third column
    ])
    .apply(new ModificationDescription('Changed to three-column layout'));
```

**Parameters:**

* `layout`: New container layout configuration

**Behavior:**

* **Preserves existing content** when possible during layout changes
* **Redistributes content** intelligently among new containers
* **Manages content overflow** by automatically creating additional structure beneath the current one when needed

### Best Practices

1. **Plan Your Layout** - Design your container structure before implementation
2. **Use Meaningful Widths** - Ensure widths add up to 100% for proper rendering
3. **Test Content Distribution** - Verify content appears in the correct containers
4. **Leverage Spacers** - Use spacer containers for consistent margins and padding

## Modification Descriptions

### Purpose

Every modification requires a description that:

* **Documents the change** for comprehensive version history
* **Provides context** for other users in collaborative editing
* **Enables meaningful labels** for undo/redo operations
* **Supports internationalization** for multi-language environments

### Basic Usage

```javascript
.apply(new ModificationDescription('Changed text color'));
```

### With Parameters

```javascript
.apply(new ModificationDescription('Changed color to {color}')
    .withParams({ color: '#ff0000' }));
```

### Internationalization

```javascript
.apply(new ModificationDescription('color_changed')
    .withParams({ color: '#ff0000' }));
// Uses translation key 'color_changed'
```

## Transaction Model

### Atomic Operations

All modifications in a single chain are atomic:

```javascript
this.api.getDocumentModifier()
    .modifyHtml(container)
    .setStyle('background', 'blue')          // All three
    .setClass('highlighted')                 // happen
    .setAttribute('data-modified', 'true')   // together
    .apply(description);
```

### Benefits of Transactions

1. **Consistency** - All related changes succeed together or fail as a unit
2. **Performance** - Batch processing reduces overhead and improves responsiveness
3. **History** - Single undo/redo operation for logically related changes
4. **Synchronization** - Fewer network requests improve collaborative editing performance

## Collaborative Editing Support

### How It Works

When a modification is applied:

1. **Local Application**: Changes apply immediately locally
2. **Serialization**: Modifications are serialized to operations
3. **Transmission**: Operations sent to collaboration server
4. **Transformation**: Server resolves conflicts if needed
5. **Broadcast**: Changes sent to all other users
6. **Application**: Remote changes applied to all clients

### Conflict Resolution

The system automatically handles conflicts:

```
User A: Changes heading color to red
User B: Changes heading color to blue (simultaneously)

Result: Last write wins (User B's change)
Both users see: Heading is blue
History shows: Both changes with timestamps
```

Complex conflicts are resolved using operational transformation:

```
User A: Inserts text at position 10
User B: Deletes text at position 5-8

System transforms User A's operation:
Original: Insert at position 10
Transformed: Insert at position 7 (adjusted for deletion)
```

## Version History Integration

### Automatic Tracking

Every modification is automatically tracked:

```javascript
// This single modification creates a history entry
this.api.getDocumentModifier()
    .modifyHtml(element)
    .setInnerHtml('Updated content')
    .apply(new ModificationDescription('Updated welcome message'));

// Version history shows:
// - Timestamp
// - "Updated welcome message"
// - Username
// - Restore option
```

### Undo/Redo Support

The modification system provides built-in undo/redo:

```javascript
// User performs modification
modifier.apply(description);

// User clicks undo
// System automatically reverses the modification

// User clicks redo
// System reapplies the modification
```

### Background Modifications

Starting from v3.11.0, a modification can be marked as a background change with [asHidden()](./reference/modification/ModificationDescription#ashidden). It is applied and saved as usual, but it is not shown in the version history and is not a separate undo/redo step:

```javascript
this.api.getDocumentModifier()
    .modifyHtml(element)
    .setAttribute('data-block-version', '2')
    .apply(new ModificationDescription('Migrated block markup').asHidden());
```

## Performance Considerations

### Batching Strategies

Batch related modifications for better performance:

```javascript
// Good - Single transaction
const modifier = this.api.getDocumentModifier();
elements.forEach(el => {
    modifier.modifyHtml(el).setStyle('color', 'blue');
});
modifier.apply(description);

// Avoid - Multiple transactions
elements.forEach(el => {
    this.api.getDocumentModifier()
        .modifyHtml(el)
        .setStyle('color', 'blue')
        .apply(description);
});
```

### Debouncing

For high-frequency updates (like color pickers):

```javascript
let modificationTimeout;

onColorChange(color) {
    clearTimeout(modificationTimeout);
    modificationTimeout = setTimeout(() => {
        this.applyColorChange(color);
    }, 100);
}
```

## Best Practices

### 1. Use Descriptive Messages

```javascript
// Good - specific and informative
.apply(new ModificationDescription('Changed button color to match brand'));

// Avoid - too vague
.apply(new ModificationDescription('Updated'));
```

### 2. Group Related Changes Logically

```javascript
// Good - related changes in a single transaction
modifier
    .modifyHtml(button)
    .setStyle('background', color)
    .setStyle('border-color', darkenColor(color))
    .apply(description);

// Avoid - unrelated changes in the same transaction
modifier
    .modifyHtml(button)
    .setStyle('background', color)
    .modifyHtml(heading)  // Different element - should be separate
    .setInnerHtml(title)
    .apply(description);
```

### 3. Minimize Unnecessary Modifications

```javascript
// Good - only modify when values actually change
if (newValue !== oldValue) {
    modifier.modifyHtml(element)
        .setAttribute('data-value', newValue)
        .apply(description);
}

// Avoid - modifying even when values haven't changed
modifier.modifyHtml(element)
    .setAttribute('data-value', newValue)  // Wasteful if unchanged
    .apply(description);
```

### 4. Use Appropriate Methods for Each Task

```javascript
// Good - use specialized methods for their intended purpose
modifier.modifyHtml(element)
    .setDisplayCondition(condition)     // For visibility control
    .setHiddenElementState('mobile')    // For device-specific hidden state
    .setNodeConfig(widgetConfig)        // For custom data storage
    .apply(description);

// Avoid - misusing generic attributes for specialized functionality
modifier.modifyHtml(element)
    .setAttribute('data-config', JSON.stringify(config))  // Use setNodeConfig instead
    .apply(description);
```

## Summary

The template modification system provides:

* **Safety** - Immutable nodes prevent direct manipulation and ensure data integrity
* **Tracking** - All changes are recorded and attributed for complete audit trails
* **Collaboration** - Automatic conflict resolution and real-time synchronization
* **History** - Built-in undo/redo functionality with comprehensive version tracking
* **Performance** - Batched operations and optimized synchronization for scalability
* **Advanced Features** - Display conditions, hidden element state, and node configurations for complex use cases
* **Layout Management** - MultiRowStructureModifier for sophisticated email structures

This system is fundamental to creating reliable, collaborative extensions that maintain template integrity while providing powerful modification capabilities.

## See Also

* [How to Handle Template Modifications](/extensions/tutorials/how-to/template-modifications)
* [TemplateModifier Reference](/extensions/reference/modification/TemplateModifier)
