Props System
Props are schema-level building blocks used in module definitions.
What a Prop is
A Prop defines a single editable field in the builder schema.
Examples:
- a text input for a title
- a toggle for enabling a feature
- a color picker
- a size field with units
- a repeater of nested items
Core base class:
Ccast\Tagixo\Core\Props\AbstractProp
Key responsibilities:
- property key and label
- widget type and config
- default values
- UI metadata serialization for the drawer
Where Props are used
Props are usually declared inside ModuleDefinition:
ModuleDefinition::make()->content(
TextProp::make('title')->default('Hero title'),
TextareaProp::make('subtitle'),
)
They can also appear inside PropType schemas.
Common fluent methods
setLabel(...)default(...)helpText(...)variablePicker(...)sub(...)showWhen(field, value, operator)withConfig([...])
Common built-in Props
TextPropTextareaPropEditorPropNumberPropSelectPropTogglePropColorPropMediaPropIconPropSliderPropSizePropRepeaterPropModelSelectProp
There are also specialized props for more complex controls such as four-way spacing, tabs, or shadow presets.
Example: simple content schema
use Ccast\Tagixo\Core\Props\TextProp;
use Ccast\Tagixo\Core\Props\TextareaProp;
use Ccast\Tagixo\Core\Props\ToggleProp;
ModuleDefinition::make()->content(
TextProp::make('title')
->setLabel('Title')
->default('Welcome'),
TextareaProp::make('description')
->setLabel('Description'),
ToggleProp::make('show_button')
->setLabel('Show button')
->default(true)
);
Conditional editor UX
Use showWhen() to make the properties panel easier to use:
TextProp::make('button_text')
->showWhen('show_button', true);
This is a UX feature. It is not a substitute for server-side validation in your save pipeline.
Repeater strategy
Use RepeaterProp for item-based structures (for example tabs/slides/plans).
Ensure repeated item keys are stable and version-safe.
Example:
RepeaterProp::make('items')
->fields([
TextProp::make('label'),
TextProp::make('url'),
]);
Repeaters are the right choice when you need structured collections, not free-form JSON blobs.
Pricing table pattern
The built-in pricing table module is a good reference for repeaters with optional commercial fields.
Each plan is stored as a repeater item with stable keys such as:
namepriceperiodoriginalPriceoptionaldiscountExpiresTextoptionalbadgeoptionalfeaturesbuttonTextbuttonUrlisFeatured
That pattern is useful when:
- a plan may or may not be discounted
- a promo window must be shown without changing the base price
- the same module should support evergreen pricing and launch offers
Example:
RepeaterProp::make('plans')
->fields([
TextProp::make('name')->default('Annual'),
TextProp::make('price')->default('199'),
TextProp::make('period')->default('/year'),
TextProp::make('originalPrice')->default('349'),
TextProp::make('discountExpiresText')->default('Launch price until May 31, 2026'),
TextProp::make('badge')->default('Most popular'),
TextareaProp::make('features'),
TextProp::make('buttonText')->default('Buy now'),
TextProp::make('buttonUrl'),
ToggleProp::make('isFeatured')->default(false),
]);
Keep promo fields optional. Do not overload price with mixed content such as "199 instead of 349" because it makes rendering and styling unstable.
Variable picker integration
variablePicker() allows a field to reference global variables instead of fixed values.
Use it for:
- theme colors
- shared spacing tokens
- reusable links
- managed text fragments
This keeps design systems consistent across templates and pages.
Custom prop example
class JsonEditorProp extends AbstractProp
{
public function widget(): string
{
return 'json_editor';
}
public function widgetConfig(): array
{
return [
...$this->baseConfig(),
'mode' => 'tree',
];
}
}
Frontend requirement for custom widgets
If you add a custom widget key, the frontend drawer renderer must know how to render it.
If no renderer exists, the field appears broken or is ignored.
That means a custom Prop can require both:
- backend schema work
- frontend field renderer support
Design guidance
- Keep Props small and composable.
- Avoid business logic in prop definitions.
- Use
showWhenfor UX, not server validation. - Prefer a reusable Prop factory over copy-pasting field definitions across modules.
- Use repeaters for structured data rather than inventing untyped array payloads.