Tagixo Docs

Developer Documentation for Laravel, SDK integrations, and extensibility

Extending Tagixo

Custom Page Modules

Step-by-step implementation for a custom content module used in page/mail/pdf contexts.

Custom Page Modules

This page describes the recommended implementation sequence for custom content modules.

Step 0 - Scaffold with the generator

Tagixo ships an artisan generator that scaffolds the module class + companion Blade view in one command:

php artisan make:tagixo-module AlertBox --context=page,mail,pdf --icon=heroicon-o-exclamation-triangle

By default the file lands at app/tagixo/Modules/AlertBox.php with namespace App\Tagixo\Modules, and a Blade companion view at resources/views/tagixo/modules/alert-box.blade.php. Adjust the destination globally via config('tagixo.scaffolding'):

// config/tagixo.php
'scaffolding' => [
    'path' => 'app/tagixo',   // change to relocate everywhere
    'namespace' => null,      // null = derived from path (capitalize each segment)
],

Override per-command with --path= + --namespace= (both required together), or scaffold directly into a third-party plugin you are developing with --package=path/to/plugin (the command reads the plugin's composer.json PSR-4 map and places the file accordingly).

After scaffolding, the command prints a copy-pasteable registration snippet for config/tagixo.php (or for the plugin's service provider in package mode).

You can of course still write the class by hand — the steps below explain the shape the generator produces, and what you'll customize next.

Step 1 - Define the module class

Use the core module abstraction and return a ModuleDefinition.

use Ccast\Tagixo\Components\Module;
use Ccast\Tagixo\Core\DefaultsBuilder;
use Ccast\Tagixo\Core\ModuleDefinition;
use Ccast\Tagixo\Core\Props\TextProp;

class AlertBoxModule extends Module
{
    public static function getLabel(): string { return __('Alert Box'); }
    public static function getIcon(): string { return 'heroicon-o-exclamation-triangle'; }

    public static function define(): ModuleDefinition
    {
        return ModuleDefinition::make()
            ->content(
                TextProp::make('title')->default('Alert'),
                TextProp::make('message')->default('Your message')
            )
            ->design('typography', 'spacing', 'background', 'border')
            ->defaults([
                ...DefaultsBuilder::background()->color('#fff7ed'),
                ...DefaultsBuilder::border()->width(1)->style('solid')->color('#fdba74')->radius(8),
            ])
            ->contexts(['page', 'mail', 'pdf']);
    }
}

Your module class is responsible for:

  • identity
  • schema
  • defaults
  • contexts
  • optional sub-elements
  • optional child constraints

Step 2 - Create Blade output view

Provide a stable module view for renderer output.

Keep the Blade view simple and deterministic. Do not move builder logic into the view.

Typical responsibilities of the view:

  • read content props
  • emit semantic HTML
  • expose stable class names/selectors for styling

If the module needs context-specific wrappers, keep that decision in PHP, not in ad-hoc frontend conditionals.

Step 3 - Add builder preview renderer

If your frontend builder requires module-specific preview logic, add the Vue renderer in your module mapping.

Not every module needs custom preview code. If the server-rendered output is enough, keep the preview path simple.

Step 4 - Register the module

Config-based:

// config/tagixo.php
'modules' => [
    App\Tagixo\Modules\AlertBoxModule::class,
],

Runtime registration is also supported.

Step 5 - Context and child constraints

Set context scope explicitly and define child rules when needed:

  • allowsChildren()
  • getAllowedChildren()

Container-style modules should declare their children intentionally. Do not leave this ambiguous.

Step 6 - Sub-elements

If the module exposes stylable inner parts, define them through subElements(...).

Use sub-elements for cases such as:

  • title vs description
  • card shell vs badge
  • repeated item label/value pairs
  • button label vs button container

Sub-elements keep nested styling explicit instead of burying it in undocumented CSS selectors.

Step 7 - Verify end-to-end

  • appears in bootstrap payload
  • appears in picker in expected contexts
  • saves/loads correctly
  • renders HTML/CSS correctly
  • survives preview + save + reload cycles
  • behaves correctly in every declared context

Migration safety

Never change module type IDs after content exists in production unless you provide migration scripts for stored JSON.

Recommended module implementation checklist

Before shipping a custom module, verify:

  1. defaults are complete
  2. the view renders without undefined indexes
  3. context restrictions are correct
  4. design PropTypes are intentional
  5. sub-elements are documented if used
  6. saved JSON remains backward-compatible