Skip to content

Presets

Presets provide pre-configured templates for both blocks and sections, allowing merchants to quickly add common variations without manual configuration. Think of them as "starter templates" or "quick-start options" that appear in the theme editor.

You can define presets in two ways:

  • Inline: Using the presets() method directly in your block/section class
  • Standalone: Creating dedicated preset classes that extend Preset

What Are Presets?

When merchants add a block or section in the Visual theme editor, they can choose from a list of presets - pre-configured variations with different settings, layouts, and child blocks already set up.

Visual editor block picker

Why Use Presets?

Faster setup - Merchants get started quickly with sensible defaults

Better UX - Guide merchants toward common patterns

Consistency - Ensure brand-aligned variations

Discoverability - Show what's possible with your blocks/sections

Basic Presets

Define presets by overriding the presets() method:

php
<?php

namespace Themes\YourTheme\Blocks;

use BagistoPlus\Visual\Blocks\SimpleBlock;
use BagistoPlus\Visual\Settings\Text;
use BagistoPlus\Visual\Settings\Select;
use BagistoPlus\Visual\Support\Preset;

class Button extends SimpleBlock
{
    protected static string $view = 'shop::blocks.button';

    public static function settings(): array
    {
        // ...
    }

    public static function presets(): array
    {
        return [
            Preset::make('Primary CTA')
                ->settings([
                    'text' => 'Shop Now',
                    'style' => 'primary',
                    'size' => 'large',
                ]),
        ];
    }
}

Preset Structure

Each preset can have these properties:

PropertyTypeDescription
namestringRequired. Display name in theme editor
descriptionstringOptional description shown to merchants
iconstringIcon identifier (e.g., heroicon-o-star)
categorystringGroup presets into categories
previewImageUrlstringURL to preview image
settingsarrayDefault settings values (block settings)
childrenarrayDefault child blocks (for containers/sections)

Example

php
use BagistoPlus\Visual\Support\Preset;

public static function presets(): array
{
    return [
        Preset::make('Success Alert')
            ->description('Green alert for success messages')
            ->icon('heroicon-o-check-circle')
            ->category('Alerts')
            ->previewImageUrl('/images/presets/success-alert.png')
            ->settings([
                'message' => 'Success! Your action was completed.',
                'type' => 'success',
                'dismissible' => true,
            ]),

        Preset::make('Error Alert')
            ->description('Red alert for error messages')
            ->icon('heroicon-o-x-circle')
            ->category('Alerts')
            ->settings([
                'message' => 'Error! Something went wrong.',
                'type' => 'error',
                'dismissible' => true,
            ]),
    ];
}

Presets with Child Blocks

Sections and container blocks can include pre-configured child blocks in their presets:

Section Preset with Child Blocks

php
use BagistoPlus\Visual\Support\Preset;
use BagistoPlus\Visual\Support\PresetBlock;

public static function presets(): array
{
    return [
        Preset::make('Hero with CTA')
            ->description('Full-width hero with heading and call-to-action button')
            ->icon('heroicon-o-photograph')
            ->category('Banners')
            ->settings([
                'layout' => 'centered',
                'background_color' => '#4f46e5',
            ])
            ->blocks([
                PresetBlock::make('heading')
                    ->id('hero-title')
                    ->settings([
                        'text' => 'Welcome to Our Store',
                        'level' => 1,
                    ]),

                PresetBlock::make('paragraph')
                    ->id('hero-subtitle')
                    ->settings([
                        'text' => 'Discover amazing products at great prices',
                    ]),

                PresetBlock::make('button')
                    ->id('hero-cta')
                    ->settings([
                        'text' => 'Shop Now',
                        'style' => 'primary',
                    ]),
            ]),

        Preset::make('Hero with Email Signup')
            ->description('Hero section with email capture form')
            ->icon('heroicon-o-mail')
            ->category('Banners')
            ->settings([
                'layout' => 'centered',
            ])
            ->blocks([
                PresetBlock::make('heading')
                    ->settings(['text' => 'Join Our Newsletter']),

                PresetBlock::make('paragraph')
                    ->settings(['text' => 'Get exclusive deals and updates']),

                PresetBlock::make('email-input'),
                PresetBlock::make('button')
                    ->settings(['text' => 'Subscribe']),
            ]),
    ];
}

PresetBlock API

When defining child blocks in presets, use PresetBlock::make():

php
PresetBlock::make('button')
    ->id('unique-id')                    // Semantic ID
    ->name('Custom Name')                 // Display name in editor
    ->settings([...])                     // Block settings values
    ->static()                            // Lock from editing
    ->children([...])                     // Nested child blocks
    ->order(['id1', 'id2'])              // Rendering order

Dynamic Sources in Presets

Settings values in presets can use dynamic sources to resolve from runtime context:

php
PresetBlock::make('@awesome-theme/product-card')
    ->id('grid-card')
    ->static()
    ->settings([
        'productName' => '@product.name',        // Resolves from context
        'price' => '@product.price',
        'image' => '@product.base_image.url',
    ])
    ->children([
        PresetBlock::make('@awesome-theme/product-image')
            ->settings([
                'src' => '@product.base_image.url',
                'alt' => '@product.name',
            ]),
        PresetBlock::make('@awesome-theme/product-title')
            ->settings([
                'text' => '@product.name',
                'url' => '@product.url',
            ]),
    ])

The @ prefix enables blocks to access:

  • Page context (variables passed from controllers)
  • Parent-shared data (via share() method)
  • Model properties using dot notation

This is especially useful for static blocks that need to display different data based on loop context.

Learn more: Dynamic Sources

PresetBlock Methods

MethodDescription
type(string)Block type (e.g., 'button', 'heading')
id(string)Unique semantic ID for the block
name(string)Custom display name in editor
settings(array)Block settings values
static(bool)Mark as static (non-editable by merchant)
children(array)Nested child blocks
order(array)Order of child block IDs

Reusable Preset Classes

For reusable, shareable, or complex presets, you can define them as standalone classes instead of inline arrays. This approach is particularly useful for:

  • Theme-wide presets shared across multiple blocks/sections
  • Complex configurations with deep nesting
  • Reusable templates across different themes
  • Version-controlled presets maintained separately

Creating a Standalone Preset Class

Standalone preset classes extend BagistoPlus\Visual\Support\Preset and implement two key methods:

php
<?php

namespace Themes\YourTheme\Presets;

use BagistoPlus\Visual\Support\Preset;
use BagistoPlus\Visual\Support\PresetBlock;

class HeroBanner extends Preset
{
    /**
     * Specify which block or section this preset is for
     */
    protected function getType(): string
    {
        return '@your-theme/hero-section';
    }

    /**
     * Configure the preset
     */
    protected function build(): void
    {
        $this
            ->name('Hero Banner')
            ->description('Full-width hero with heading, text, and CTA')
            ->icon('heroicon-o-photograph')
            ->category('Banners')
            ->settings([
                'layout' => 'centered',
                'background_color' => '#4f46e5',
                'padding' => 'large',
            ])
            ->blocks([
                PresetBlock::make('@your-theme/heading')
                    ->id('hero-title')
                    ->settings([
                        'text' => 'Welcome to Our Store',
                        'level' => 1,
                        'color' => 'white',
                    ]),

                PresetBlock::make('@your-theme/paragraph')
                    ->id('hero-subtitle')
                    ->settings([
                        'text' => 'Discover amazing products at great prices',
                        'color' => 'white',
                    ]),

                PresetBlock::make('@your-theme/button')
                    ->id('hero-cta')
                    ->settings([
                        'text' => 'Shop Now',
                        'style' => 'primary',
                        'size' => 'large',
                    ]),
            ]);
    }
}

Using PresetBlock

Always use PresetBlock from the Visual package when defining child blocks in presets:

php
use BagistoPlus\Visual\Support\Preset;
use BagistoPlus\Visual\Support\PresetBlock;

public static function presets(): array
{
    return [
        Preset::make('Hero Banner')
            ->blocks([
                PresetBlock::make('heading')
                    ->settings(['text' => 'Welcome']),

                PresetBlock::make('button')
                    ->settings(['text' => 'Shop Now']),
            ]),
    ];
}

The block type identifier can be:

  • Simple name: 'button', 'heading' (for built-in or theme blocks)
  • Namespaced: '@your-theme/button', '@visual-debut/heading' (explicit namespace)

Example: Complex Nested Preset

php
<?php

namespace Themes\YourTheme\Presets;

use BagistoPlus\Visual\Support\Preset;
use BagistoPlus\Visual\Support\PresetBlock;

class FeatureGrid extends Preset
{
    protected function getType(): string
    {
        return '@your-theme/flex-section';
    }

    protected function build(): void
    {
        $this
            ->name('Feature Grid')
            ->description('Three-column feature grid with icons')
            ->category('Content')
            ->settings([
                'layout' => 'grid',
                'columns' => 3,
            ])
            ->blocks([
                // First feature
                PresetBlock::make('@your-theme/container')
                    ->id('feature-1')
                    ->settings(['alignment' => 'center'])
                    ->children([
                        PresetBlock::make('@your-theme/icon')
                            ->settings([
                                'icon' => 'heroicon-o-lightning-bolt',
                                'size' => 'large',
                            ]),
                        PresetBlock::make('@your-theme/heading')
                            ->settings([
                                'text' => 'Fast Shipping',
                                'level' => 3,
                            ]),
                        PresetBlock::make('@your-theme/paragraph')
                            ->settings([
                                'text' => 'Get your order in 2-3 business days',
                            ]),
                    ]),

                // Second feature
                PresetBlock::make('@your-theme/container')
                    ->id('feature-2')
                    ->settings(['alignment' => 'center'])
                    ->children([
                        PresetBlock::make('@your-theme/icon')
                            ->settings([
                                'icon' => 'heroicon-o-shield-check',
                                'size' => 'large',
                            ]),
                        PresetBlock::make('@your-theme/heading')
                            ->settings([
                                'text' => 'Secure Checkout',
                                'level' => 3,
                            ]),
                        PresetBlock::make('@your-theme/paragraph')
                            ->settings([
                                'text' => 'Your payment information is safe',
                            ]),
                    ]),

                // Third feature
                PresetBlock::make('@your-theme/container')
                    ->id('feature-3')
                    ->settings(['alignment' => 'center'])
                    ->children([
                        PresetBlock::make('@your-theme/icon')
                            ->settings([
                                'icon' => 'heroicon-o-heart',
                                'size' => 'large',
                            ]),
                        PresetBlock::make('@your-theme/heading')
                            ->settings([
                                'text' => '100% Satisfaction',
                                'level' => 3,
                            ]),
                        PresetBlock::make('@your-theme/paragraph')
                            ->settings([
                                'text' => 'Love it or return it within 30 days',
                            ]),
                    ]),
            ]);
    }
}

Organizing Preset Classes

Store standalone preset classes in a dedicated directory:

your-theme/
└── src/
    └── Presets/
        ├── HeroBanner.php
        ├── FeatureGrid.php
        ├── TestimonialCarousel.php
        └── ClassicFooter.php

When to Use Standalone Preset Classes

Use Standalone Classes WhenUse Inline presets() Method When
Preset is complex with deep nestingPreset is simple (2-3 properties)
Preset is reused across themesPreset is specific to one block
Preset needs to be reused in other presets (::asChild())Preset is only used inline
Preset requires translationsPreset uses static values
Preset is version-controlled separatelyPreset is quick variations
Multiple presets share logicVariations are straightforward

Reusing Presets with ::asChild()

Standalone preset classes can be reused as child blocks within other presets using the ::asChild() method. This enables composition and eliminates duplication when the same preset configuration needs to appear in multiple places.

Basic Usage

php
<?php

namespace Themes\YourTheme\Presets;

use BagistoPlus\Visual\Support\Preset;
use BagistoPlus\Visual\Support\PresetBlock;

// Define a reusable preset
class ProductCard extends Preset
{
    protected function getType(): string
    {
        return '@your-theme/product-card';
    }

    protected function build(): void
    {
        $this
            ->name('Product Card with Overlay')
            ->category('Products')
            ->blocks([
                PresetBlock::make('@your-theme/product-image')
                    ->settings(['aspect_ratio' => 'square']),

                PresetBlock::make('@your-theme/product-title')
                    ->settings(['size' => 'large']),

                PresetBlock::make('@your-theme/product-price')
                    ->settings(['show_compare' => true]),
            ]);
    }
}

// Reuse it in another preset
class ProductGrid extends Preset
{
    protected function getType(): string
    {
        return '@your-theme/product-grid';
    }

    protected function build(): void
    {
        $this
            ->name('Featured Products Grid')
            ->settings(['columns' => 4])
            ->blocks([
                PresetBlock::make('@your-theme/heading')
                    ->settings(['text' => 'Featured Products']),

                // Reuse ProductCard preset as a child block
                ProductCard::asChild()
                    ->id('product-card')
                    ->static()
                    ->repeated(),
            ]);
    }
}

Real-World Examples

Example 1: Testimonials Section

php
use BagistoPlus\Visual\Support\Preset;
use BagistoPlus\Visual\Support\PresetBlock;

public static function presets(): array
{
    return [
        Preset::make('Three Testimonials')
            ->description('Grid of three customer testimonials')
            ->icon('heroicon-o-chat-bubble-left-right')
            ->category('Social Proof')
            ->settings([
                'heading' => 'What Our Customers Say',
                'layout' => 'grid',
            ])
            ->blocks([
                PresetBlock::make('testimonial')
                    ->settings([
                        'quote' => 'Amazing product! Highly recommend.',
                        'author' => 'John Doe',
                        'rating' => 5,
                    ]),

                PresetBlock::make('testimonial')
                    ->settings([
                        'quote' => 'Great quality and fast shipping.',
                        'author' => 'Jane Smith',
                        'rating' => 5,
                    ]),

                PresetBlock::make('testimonial')
                    ->settings([
                        'quote' => 'Excellent customer service!',
                        'author' => 'Bob Johnson',
                        'rating' => 5,
                    ]),
            ]),

        Preset::make('Carousel Testimonials')
            ->description('Scrolling carousel of testimonials')
            ->icon('heroicon-o-arrow-path')
            ->category('Social Proof')
            ->settings([
                'heading' => 'Customer Reviews',
                'layout' => 'carousel',
            ])
            ->blocks([
                PresetBlock::make('testimonial')->repeated(),
                PresetBlock::make('testimonial')->repeated(),
                PresetBlock::make('testimonial')->repeated(),
                PresetBlock::make('testimonial')->repeated(),
            ]),
    ];
}
php
use BagistoPlus\Visual\Support\Preset;
use BagistoPlus\Visual\Support\PresetBlock;

public static function presets(): array
{
    return [
        Preset::make('2 Column Grid')
            ->category('Galleries')
            ->settings(['columns' => 2])
            ->blocks([
                PresetBlock::make('image'),
                PresetBlock::make('image'),
                PresetBlock::make('image'),
                PresetBlock::make('image'),
            ]),

        Preset::make('3 Column Masonry')
            ->category('Galleries')
            ->settings(['columns' => 3, 'style' => 'masonry'])
            ->blocks([
                PresetBlock::make('image'),
                PresetBlock::make('image'),
                PresetBlock::make('image'),
                PresetBlock::make('image'),
                PresetBlock::make('image'),
                PresetBlock::make('image'),
            ]),

        Preset::make('Image Carousel')
            ->category('Galleries')
            ->settings(['style' => 'carousel', 'autoplay' => true])
            ->blocks([
                PresetBlock::make('image')->repeated(),
                PresetBlock::make('image')->repeated(),
                PresetBlock::make('image')->repeated(),
            ]),
    ];
}

Example 3: Call-to-Action Variations

php
use BagistoPlus\Visual\Support\Preset;
use BagistoPlus\Visual\Support\PresetBlock;

public static function presets(): array
{
    return [
        Preset::make('Simple CTA')
            ->category('CTAs')
            ->settings(['layout' => 'simple'])
            ->blocks([
                PresetBlock::make('heading')
                    ->settings(['text' => 'Ready to get started?'])
                    ->static(),

                PresetBlock::make('button')
                    ->settings(['text' => 'Get Started', 'style' => 'primary']),
            ]),

        Preset::make('Split CTA with Image')
            ->category('CTAs')
            ->settings(['layout' => 'split'])
            ->blocks([
                PresetBlock::make('image')
                    ->settings(['image' => '/default-cta.jpg'])
                    ->static(),

                PresetBlock::make('heading')
                    ->settings(['text' => 'Join thousands of happy customers']),

                PresetBlock::make('paragraph')
                    ->settings(['text' => 'Start your journey today']),

                PresetBlock::make('button')
                    ->settings(['text' => 'Sign Up Free']),
            ]),
    ];
}

Preview Images

Add visual previews to help merchants choose:

php
use BagistoPlus\Visual\Support\Preset;

Preset::make('Hero with Image Left')
    ->previewImageUrl('/images/presets/hero-image-left.png')

Tips for preview images:

  • Use 16:9 aspect ratio (e.g., 800x450px)
  • Show realistic content, not placeholders
  • Keep file size under 100KB
  • Use PNG or JPEG format
  • Store in public/images/presets/

Next Steps

Released under the MIT License.