Skip to content

Creating Components

This guide explains how to create new custom components for the Hyvä CMS Liveview Editor. Hyvä CMS components are reusable content blocks that content editors can drag and drop onto pages through the visual editor. Well-designed components provide a great editor experience and help maintain consistent design patterns across your site.

Component Structure Overview

A Hyvä CMS component consists of two main parts that work together:

  1. Component Declaration - A JSON configuration file defining the component's ID, label, editable fields, and template path. This declaration determines what options appear in the Hyvä CMS editor interface.
  2. Component Template - A PHTML file that renders the component's HTML output using data from the component declaration fields.

Both files must be created in your custom Magento module to register a new Hyvä CMS component.

Creating a Basic Hyvä CMS Component

Follow these steps to create a functional Hyvä CMS component with editable fields.

Step 1: Create the Component Declaration JSON File

The component declaration defines how the component appears in the Hyvä CMS editor. Create a components JSON file in your module's etc/hyva_cms/ directory.

Create the file at etc/hyva_cms/components.json in your module:

{
  "my_component": {
    "label": "My Component",
    "template": "Vendor_Module::elements/my_component.phtml",
    "content": {
      "title": {
        "type": "text",
        "label": "Title",
        "default_value": "Default Title"
      },
      "description": {
        "type": "textarea",
        "label": "Description",
        "attributes": {
          "placeholder": "Enter description here"
        }
      }
    }
  }
}

The example above creates a component named my_component with two editable fields: a text field for the title and a textarea for the description. The content section contains the fields that appear in the component's Content tab within the Hyvä CMS editor.

Default Template Path

You can omit the template property from your component declaration. When omitted, Hyvä CMS uses a default template path based on the component name: [Vendor]_[Module]::elements/[component-name].phtml

Component Field Sections

The content property defines fields displayed in the component's Content section. You can similarly add fields to the design and advanced sections to organize different types of configuration options. Design fields typically control visual styling, while advanced fields handle technical settings like CSS classes and block IDs.

For detailed information about all available component declaration options, see Component Declaration Schema.

Step 2: Create the Component Template PHTML File

The component template renders the HTML output using data from the component declaration. Create a PHTML template at the path specified in your component declaration (or at the default path if you omitted the template property).

Create the file at view/frontend/templates/elements/my_component.phtml:

<?php
declare(strict_types=1);

use Hyva\CmsLiveviewEditor\Block\Element;
use Magento\Framework\Escaper;

/**
 * Hyvä CMS Component Template: My Component
 * Renders a title and description with editor integration
 * 
 * @var Element $block - Provides access to component field data and editor methods
 * @var Escaper $escaper - Magento HTML escaper for security
 */

// Retrieve field values from the component declaration
// These correspond to the "title" and "description" fields defined in components.json
$title = $block->getTitle() ?? null;
$description = $block->getDescription() ?? null;
?>
<div <?= /** @noEscape */ $block->getEditorAttrs() ?>>
    <!-- The getEditorAttrs('title') method enables click-to-edit in the CMS preview -->
    <h2 <?= /** @noEscape */ $block->getEditorAttrs('title') ?>>
        <?= $escaper->escapeHtml($title) ?>
    </h2>

    <!-- Each editable field should have its own getEditorAttrs() call with the field name -->
    <div <?= /** @noEscape */ $block->getEditorAttrs('description') ?>>
        <?= $escaper->escapeHtml($description) ?>
    </div>
</div>

The template accesses field data using getter methods on the $block object. Field names from the component declaration (like title and description) become camelCase getter methods (like getTitle() and getDescription()).

getEditorAttrs Required for Editor Integration

The getEditorAttrs() method must be included in your Hyvä CMS component template for the visual editor to function correctly. This method adds special HTML attributes that enable click-to-edit functionality in the CMS preview. While not required on every field, you should at least include getEditorAttrs() on the root element of your component. These attributes only appear in preview mode and are automatically removed from the final rendered output on the frontend.

Hyvä CMS Component Block Helper Methods

The Hyva\CmsLiveviewEditor\Block\Element class provides essential helper methods for building Hyvä CMS component templates. These methods handle common tasks like editor integration, image processing, link handling, and child component rendering.

Essential Block Methods Reference

Below is a summary of the most useful methods available in your Hyvä CMS component templates. These methods are part of the Hyva\CmsLiveviewEditor\Block\Element class. Review the class source code for complete details.

getEditorAttrs

Adds special attributes that allow the Hyvä CMS liveview editor to interact with content in preview mode.

getEditorAttrs(string $field = '', ?string $childUid = null): string

When a user clicks on content in the Hyvä CMS preview mode, these attributes ensure the correct form field opens for editing. These attributes are only added in preview mode and are automatically removed from the final rendered output on the frontend.

Example usage in component template:

// Root element - enables component selection
<div <?= /** @noEscape */ $block->getEditorAttrs() ?>>

// Field-specific - enables direct field editing
<h2 <?= /** @noEscape */ $block->getEditorAttrs('title') ?>>

renderEditorMessage

Renders a message visible only in the Hyvä CMS editor preview, not on the public frontend.

renderEditorMessage(array $arguments = [], ?string $type = null, ?string $template = null): string

This method is useful for showing placeholder messages when a component has no content or when a required field is empty. The message only appears in the CMS editor and helps guide content editors.

Example usage for missing image placeholder:

<?= /** @noEscape */ $block->renderEditorMessage([
    'preview_message' => __('No image selected'),
    'button_text' => __('Select Image'),
]) ?>

validPreview

Checks if the current request is a valid Hyvä CMS preview request.

validPreview(): bool

Returns false when rendering public-facing content on the frontend. Returns true when viewing a preview of the content in a new tab or from within the Hyvä CMS editor. Use this method to conditionally add content that should only appear in preview mode, such as debugging information or editor-specific markup.

getImagePath

Returns the full URL path to an image file in the Magento media directory.

getImagePath(string $imagePath): string

This method converts a relative media path (like catalog/image.jpg) into a full URL that can be used in <img> tags.

getResponsiveImageData

Prepares image data for generating responsive <picture> tags with multiple image sources.

getResponsiveImageData(array $image, ?string $type = null, array $attributes = []): array

This method processes Hyvä CMS image field data and returns an array with width, height, srcset, and media query attributes. Use this method to create responsive images that load different sizes based on viewport width.

Example usage for responsive images:

$imageData = $block->getResponsiveImageData($image, 'hyva_cms_image');

getLinkData

Processes Hyvä CMS link field configuration and returns structured link data.

getLinkData(array $link): array

This method takes link configuration from a link field and returns an array containing the URL, label, and open-in-new-tab settings. Use this method to safely render links from Hyvä CMS link fields.

Example usage for link fields:

$link = $block->getLinkData($block->getData('link') ?? []);
// Returns: ['url' => '...', 'label' => '...', 'open_in_new' => true/false]

buildClasses

Builds a string of CSS classes combining base classes with user-configured classes.

buildClasses(array $baseClasses, ?string $configKey = null, bool $includeElementClasses = true): string

This method merges CSS classes defined in your template with any additional classes specified by content editors in the Hyvä CMS component configuration. This allows editors to add custom styling without modifying the template.

Example usage for component CSS classes:

$classes = $block->buildClasses(['btn', 'btn-primary']);

createChildHtml

Creates and renders child components without using parent-child layout relationships.

createChildHtml(array $elementData, ?string $prefix = 'child'): string

This method renders Hyvä CMS child components while avoiding layout conflicts. Use this method when your component accepts children to properly render nested component structures.

Example usage for child components:

<?= $block->createChildHtml($elementData, 'cta_child') ?>
Content Security Policy (CSP) and JavaScript in Hyvä CMS Components

When adding JavaScript to Hyvä CMS component templates, follow these guidelines to ensure compatibility with Magento Content Security Policy (CSP) and the Hyvä CMS Liveview Editor.

CSP-Compatible Script Loading:

  1. Do not include inline <script> tags directly in your Hyvä CMS component templates
  2. Add your JavaScript files before the closing </body> tag using Magento's layout XML
  3. Initialize Alpine.js components by listening to the alpine:init event

Example Alpine.js component initialization:

<script>
    // Listen for Alpine.js initialization
    document.addEventListener('alpine:init', () => {
        // Register your Alpine.js component data
        Alpine.data('myComponent', () => ({
            open: false,
            toggle() {
                this.open = !this.open
            }
        }))
    })
</script>

This approach ensures your JavaScript is available both during initial page load and when the Hyvä CMS editor updates the preview content dynamically. The alpine:init event fires whenever Alpine.js initializes, including during live preview updates.

For more information about Content Security Policy configuration in Hyvä Themes, see the CSP and Magento documentation.

Available Field Types for Hyvä CMS Components

The Hyvä CMS editor supports many field types for building rich component interfaces. Each field type provides a different editing experience in the CMS editor and returns different data structures to your component template.

The following field types are available for use in your component declarations:

  • boolean - Checkbox for true/false values
  • children - Container for nested child components
  • color - Color picker with hex value input
  • date - Date picker calendar interface
  • html - Raw HTML code editor
  • image - Image upload with media gallery browser
  • link - Link builder with URL, label, and target options
  • multiselect - Multiple checkbox selection
  • number - Numeric input with increment controls
  • range - Slider input for numeric ranges
  • products - Magento product selector
  • richtext - WYSIWYG text editor
  • select - Single dropdown selection
  • searchable_select - Dropdown with search/filter capability
  • text - Single-line text input
  • text-align - Text alignment selector (left, center, right, justify)
  • textarea - Multi-line plain text input
  • variant - Template variant selector for different component styles
  • custom_type - Custom field type for advanced implementations

For detailed information about each field type, including configuration options and data structures, see the Component Declaration Schema field types reference.

Adding HTML5 Validation and Field Attributes

Hyvä CMS components support HTML5 validation attributes to provide immediate feedback to content editors. The attributes property in your component field declaration adds validation rules and input constraints.

Example component with field validation:

{
  "my_component": {
    "content": {
      "text_field": {
        "type": "text",
        "label": "Text Field",
        "attributes": {
          "required": true,
          "minlength": "1",
          "maxlength": "100",
          "pattern": ".*(foo|bar|Foo|Bar).*",
          "comment": "This is a comment",
          "placeholder": "Enter text here"
        }
      }
    }
  }
}

The example above adds several validation constraints to a text field:

  • required - Field must have a value before saving
  • minlength / maxlength - Enforces character count limits
  • pattern - Validates against a regular expression
  • comment - Displays helper text below the field
  • placeholder - Shows example text in empty fields

These validation rules run in the browser before content is saved, providing immediate feedback to content editors. For more information about available attributes, see the Component Declaration Schema attributes reference.

Creating Parent-Child Component Relationships

Hyvä CMS components can contain other components as children, enabling nested content structures like rows containing columns, or lists containing list items. Configure child component relationships in the component declaration using the children property.

Example: Simple container accepting any child components:

{
  "row": {
    "label": "Row",
    "children": true
  }
}

The "children": true configuration allows any Hyvä CMS component to be nested inside the Row component.

Example: Restricted container with child component constraints:

{
  "usp_list": {
    "label": "USP List",
    "children": {
      "config": {
        "accepts": ["usp"],
        "max_children": 4
      }
    },
    "content": {
      "title": {
        "type": "text",
        "label": "USP List Title",
        "default_value": "USP List Title"
      }
    }
  }
}

The accepts array restricts which components can be added as children. The example above only allows usp components to be nested inside usp_list. The max_children property limits the number of child components to 4.

Controlling Allowed Child Components

Use the accepts property to specify which components can be nested as children. When omitted, all Hyvä CMS components are allowed. Restricting allowed children helps maintain consistent content structures and prevents editors from creating invalid component combinations.

Reusing Common Fields with Includes

The includes property allows you to reuse common field definitions across multiple Hyvä CMS components. This is particularly useful for shared design settings and advanced options that should be consistent across many components.

Example: Including shared design fields:

{
  "my_component": {
    "label": "My Component",
    "content": {
      "title": {
        "type": "text",
        "label": "Title"
      }
    },
    "design": {
      "includes": "Hyva_CmsBase::etc/hyva_cms/default_design.json"
    }
  }
}

The example above includes all design fields from the default_design.json file in the Hyva_CmsBase module. The includes property accepts either a single path string or an array of paths to include multiple field definition files.

This approach ensures consistent design options across all components without duplicating JSON configuration.

Creating Component Style Variants

Component variants allow you to create multiple visual styles for a single Hyvä CMS component. Each variant can use a different template file to render the component with alternative HTML structure or styling.

Example component with variant selector:

{
  "my_component": {
    "label": "My Component",
    "content": {
      "variants": {
        "type": "variant",
        "label": "Style Variant",
        "options": [
          {
            "label": "Default",
            "value": "Vendor_Module::elements/my_component/default.phtml"
          },
          {
            "label": "Alternative",
            "value": "Vendor_Module::elements/my_component/alternative.phtml"
          }
        ]
      }
    }
  }
}

The variant field creates a dropdown selector in the Hyvä CMS editor. When a content editor selects a variant, the corresponding template renders the component. This allows a single component to have multiple visual presentations without creating separate component declarations.

Advanced Hyvä CMS Component Configuration

Hiding Components from Frontend Display

Use the visible property to hide a Hyvä CMS component from frontend display while keeping it available in the editor. This is useful for components that should only appear in specific contexts or for creating draft components during development.

Example component with visibility control:

{
  "my_component": {
    "label": "My Component",
    "visible": false,
    "content": {
      "title": {
        "type": "text",
        "label": "Title"
      }
    }
  }
}

When "visible": false, the component appears in the Hyvä CMS editor component list but does not render on the frontend. Content editors can still add and configure the component in preview mode.

Restricting Components to Specific Parents

Use the require_parent property to create child components that only appear when a parent component explicitly allows them. This prevents editors from placing child components in invalid locations.

Example child component requiring a parent:

{
  "child_component": {
    "label": "Child Component",
    "require_parent": true,
    "content": {
      "title": {
        "type": "text",
        "label": "Child Title"
      }
    }
  }
}

The child component above will only appear in the Hyvä CMS editor when adding components inside a parent that accepts it.

Example parent component accepting the child:

{
  "parent_component": {
    "label": "Parent Component",
    "children": {
      "config": {
        "accepts": ["child_component"]
      }
    },
    "content": {
      "title": {
        "type": "text",
        "label": "Parent Title"
      }
    }
  }
}

This pattern ensures child components only appear in appropriate contexts, preventing invalid component hierarchies.

Adding Advanced Email Validation to Text Fields

HTML5 pattern attributes enable custom validation rules for Hyvä CMS component text fields. This example shows email validation with a custom error message.

Example email field with pattern validation:

{
  "email_field": {
    "type": "text",
    "label": "Email Address",
    "attributes": {
      "required": true,
      "pattern": "[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$",
      "data-validation-message": "Please enter a valid email address"
    }
  }
}

The pattern attribute validates input against the regular expression. The data-validation-message attribute provides a custom error message when validation fails. Note that backslashes in the pattern must be escaped in JSON.

Rendering Block ID and CSS Classes in Templates

Most Hyvä CMS components should support custom block IDs and CSS classes in their advanced section. The Hyva\CmsLiveviewEditor\Block\Element class provides helper methods to render these values.

Example component template with block ID and CSS classes:

<?php
declare(strict_types=1);

use Hyva\CmsLiveviewEditor\Block\Element;
use Magento\Framework\Escaper;

/** @var Element $block */
/** @var Escaper $escaper */
?>
<div
    class="<?= $escaper->escapeHtmlAttr($block->getCssClasses()) ?>"
    <?= /** @noEscape */ $block->renderBlockId() ?>
    <?= /** @noEscape */ $block->getEditorAttrs() ?>
>
    <!-- Component content goes here -->
</div>

The getCssClasses() method returns CSS classes configured in the component's advanced section. The renderBlockId() method outputs the HTML id attribute if a block ID was configured. Both methods allow content editors to add custom styling and anchor links without modifying templates.

Best Practices for Hyvä CMS Component Templates

Rendering Responsive Images

Use the getResponsiveImageData() method to create responsive <picture> tags that serve different image sizes based on viewport width. This improves page load performance by serving appropriately sized images.

Example responsive image rendering:

<?php
declare(strict_types=1);

use Hyva\CmsLiveviewEditor\Block\Element;
use Magento\Framework\Escaper;
use Hyva\Theme\ViewModel\Media;

/** @var Element $block */
/** @var Escaper $escaper */
/** @var Media $media */

// Get image data from component fields
$image = $block->getData('image') ?: [];
$desktopImage = $block->getData('desktop_image') ?: $image;

// Prepare responsive image data for mobile and desktop
// Each call to getResponsiveImageData() creates a source for the picture tag
$imageData = array_filter([
    // Mobile/default image (no media query, used as fallback)
    $block->getResponsiveImageData($image, 'hyva_cms_image'),
    // Desktop image (only loads on screens 1024px and wider)
    $block->getResponsiveImageData(
        $desktopImage,
        'hyva_cms_image_desktop',
        ['media' => '(min-width: 1024px)']
    )
]);

// Set image tag attributes (alt text, lazy loading)
$imgTagAttributes = [
    'alt' => $image['alt'],
    'loading' => 'lazy'
];

// Set picture tag attributes and editor integration
$pictureTagAttributes = array_merge(
    ['data-liveview-element' => 'image'],
    $block->getEditorAttrsArray()
);
?>
<?= /** @noEscape */ $media->getResponsivePictureHtml($imageData, $imgTagAttributes, $pictureTagAttributes) ?>

This example creates a responsive image that loads a mobile-optimized version by default and switches to a desktop version on larger screens. The getResponsiveImageData() method handles srcset generation and image sizing.

Use the getLinkData() method to process Hyvä CMS link fields into a consistent data structure. This method handles URL generation, label extraction, and open-in-new-tab settings.

Example link field rendering:

<?php
declare(strict_types=1);

use Hyva\CmsLiveviewEditor\Block\Element;
use Magento\Framework\Escaper;

/** @var Element $block */
/** @var Escaper $escaper */

// Process link field data into structured array
// Returns: ['url' => '...', 'label' => '...', 'open_in_new' => true/false]
$link = $block->getLinkData($block->getData('link') ?? []);
?>
<a
    href="<?= $escaper->escapeHtmlAttr($link['url']) ?>"
    <?= $link['open_in_new'] ? 'target="_blank" rel="noopener"' : '' ?>
    <?= /** @noEscape */ $block->getEditorAttrs() ?>
>
    <?= $escaper->escapeHtml($link['label']) ?>
</a>

The getLinkData() method ensures consistent link handling across all Hyvä CMS components and properly resolves different link types (internal pages, external URLs, etc.).

Rendering Child Components

Use the createChildHtml() method to render Hyvä CMS child components within a parent component template. This method properly handles editor integration and avoids Magento layout conflicts.

Example child component rendering:

<?php
declare(strict_types=1);

use Hyva\CmsLiveviewEditor\Block\Element;

/** @var Element $block */

// Check if component has children
$hasChildren = $block->hasChildren();
$children = $block->getChildren();
?>
<div <?= /** @noEscape */ $block->getEditorAttrs() ?>>
    <?php if ($hasChildren): ?>
        <div class="child-container">
            <?php foreach ($children ?: [] as $elementData): ?>
                <?= $block->createChildHtml($elementData, 'child_prefix') ?>
            <?php endforeach; ?>
        </div>
    <?php endif; ?>
</div>

The createChildHtml() method renders each child component with proper editor attributes. The second parameter ('child_prefix') provides a unique prefix for child block names to avoid naming conflicts.

Reference Examples in Hyva_CmsBase Module

The Hyva_CmsBase module contains many example components demonstrating different features and patterns. Review these examples to learn advanced component techniques and see real-world implementations.

Browse the component templates in the Hyva_CmsBase module source code: Hyva_CmsBase component templates

These examples include components with variants, child components, responsive images, link handling, and advanced field configurations.