Skip to main content

Overview

Lyger’s component system provides a flexible foundation for building reusable, reactive UI components. Components can be used standalone or integrated with Livewire for enhanced reactivity.
The component system in Lyger\Components is designed to work alongside Livewire components, providing additional UI building blocks and patterns.

Basic Components

Creating a Component

Extend the Lyger\Components\Component base class:
<?php

namespace App\Components;

use Lyger\Components\Component;

class Card extends Component
{
    protected string $title = '';
    protected string $content = '';

    protected function mount(): void
    {
        $this->title = $this->title ?: 'Default Title';
    }

    protected function renderInline(): string
    {
        return <<<HTML
        <div class="card bg-white shadow-lg rounded-lg p-6">
            <h3 class="text-xl font-bold mb-4">{$this->title}</h3>
            <div class="content">{$this->content}</div>
        </div>
        HTML;
    }
}

Component Registration

Register components with the ComponentManager:
use Lyger\Components\ComponentManager;
use App\Components\Card;

ComponentManager::register('card', Card::class);

// Create instance
$card = ComponentManager::make('card');
$card->title = 'Welcome';
$card->content = 'Hello, World!';
echo $card->render();

Component Lifecycle

1

Construction

Component is instantiated with new or ComponentManager::make().
2

Mount

The mount() method is called to initialize component state.
3

Before Render

The beforeRender() method is called before rendering (optional).
4

Render

The component is rendered using render() which calls renderView() or renderInline().

Lifecycle Methods

class LifecycleExample extends Component
{
    protected function mount(): void
    {
        // Called once when component is created
        $this->properties['initialized'] = true;
    }

    protected function beforeRender(): void
    {
        // Called before each render
        $this->properties['timestamp'] = time();
    }

    public function render(): string
    {
        // Automatically calls beforeRender() then renderView()
        return parent::render();
    }
}

Views and Templates

Using External Views

class UserCard extends Component
{
    protected string $view = 'components.user-card';

    protected function mount(): void
    {
        $this->properties = [
            'name' => 'John Doe',
            'email' => 'john@example.com',
            'avatar' => '/images/avatar.jpg',
        ];
    }
}
Create the view file at resources/views/components/user-card.php:
<div class="user-card">
    <img src="<?= $avatar ?>" alt="<?= $name ?>">
    <h3><?= $name ?></h3>
    <p><?= $email ?></p>
</div>

Inline Rendering

Override renderInline() for simple components:
class Badge extends Component
{
    protected string $text = '';
    protected string $color = 'blue';

    protected function renderInline(): string
    {
        return "<span class=\"badge bg-{$this->color}-500 text-white px-3 py-1 rounded-full\">{$this->text}</span>";
    }
}

Properties and Data

Setting Properties

class DataComponent extends Component
{
    protected array $properties = [];

    public function setData(array $data): self
    {
        foreach ($data as $key => $value) {
            $this->setProperty($key, $value);
        }
        return $this;
    }

    public function getData(): array
    {
        return $this->properties;
    }
}

// Usage
$component = new DataComponent();
$component->setData([
    'title' => 'Product',
    'price' => 29.99,
    'stock' => 100,
]);

Magic Methods

Components support magic getters and setters:
$component = new Card();
$component->title = 'My Title';  // Uses __set()
$component->description = 'Text content';

echo $component->title;  // Uses __get()

if (isset($component->title)) {  // Uses __isset()
    // Property exists
}

Method Calling

Components can expose methods that can be called dynamically:
class Calculator extends Component
{
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }

    public function multiply(int $a, int $b): int
    {
        return $a * $b;
    }
}

$calc = new Calculator();
$result = $calc->call('add', [5, 10]);

// Returns:
// [
//     'success' => true,
//     'data' => 15,
//     'updates' => []
// ]

Error Handling

$result = $calc->call('nonExistentMethod', []);

// Returns:
// [
//     'error' => 'Method nonExistentMethod not found'
// ]

Built-in Components

FormComponent

Create forms with validation and error handling:
use Lyger\Components\FormComponent;

class ContactForm extends FormComponent
{
    protected function defineFields(): array
    {
        return [
            'name' => [
                'type' => 'text',
                'label' => 'Full Name',
                'placeholder' => 'Enter your name',
                'rules' => ['required', 'min:3'],
            ],
            'email' => [
                'type' => 'email',
                'label' => 'Email Address',
                'placeholder' => 'you@example.com',
                'rules' => ['required', 'email'],
            ],
            'message' => [
                'type' => 'textarea',
                'label' => 'Message',
                'placeholder' => 'Your message here...',
                'rules' => ['required', 'min:10'],
            ],
        ];
    }

    protected function handleSubmit(): void
    {
        // Process the form data
        $data = $this->values;
        
        // Send email, save to database, etc.
        mail($data['email'], 'Contact Form', $data['message']);
    }
}
Display modal dialogs:
use Lyger\Components\Modal;

$modal = new Modal();
$modal->title = 'Confirm Action';
$modal->size = 'md'; // sm, md, lg, xl, full
$modal->isOpen = true;

echo $modal->render();
// Control modal state
$modal->open();
$modal->close();
$modal->toggle();
Modal component includes built-in backdrop and animation support. The size property controls the modal width.

Table Component

Create sortable data tables:
use Lyger\Components\Table;

class UserTable extends Table
{
    protected function defineColumns(): array
    {
        return [
            'id' => [
                'label' => 'ID',
                'sortable' => true,
            ],
            'name' => [
                'label' => 'Name',
                'sortable' => true,
            ],
            'email' => [
                'label' => 'Email',
                'sortable' => false,
            ],
            'created_at' => [
                'label' => 'Created',
                'sortable' => true,
                'format' => fn($value) => date('Y-m-d', strtotime($value)),
            ],
        ];
    }

    protected function getData(): array
    {
        return User::all();
    }
}

$table = new UserTable();
echo $table->render();

Table Actions

Add action buttons to table rows:
class UserTable extends Table
{
    protected function mount(): void
    {
        parent::mount();
        
        $this->actions = [
            [
                'label' => 'Edit',
                'url' => fn($row) => "/users/{$row['id']}/edit",
                'class' => 'text-blue-600 hover:text-blue-900',
            ],
            [
                'label' => 'Delete',
                'url' => fn($row) => "/users/{$row['id']}/delete",
                'class' => 'text-red-600 hover:text-red-900',
            ],
        ];
    }
}

Sorting

// Sort by column
$table->sort('name');  // First click: ascending
$table->sort('name');  // Second click: descending

Alert Component

Display notifications and messages:
use Lyger\Components\Alert;

$alert = new Alert();
$alert->type = 'success';
$alert->message = 'Your changes have been saved!';
$alert->dismissible = true;

echo $alert->render();

Events and Communication

Emitting Events

class EventEmitter extends Component
{
    public function triggerAction()
    {
        $result = $this->emit('actionTriggered', [
            'timestamp' => time(),
            'user' => 'John',
        ]);
        
        return $result;
    }
}

Event Data Structure

[
    'event' => 'actionTriggered',
    'data' => [
        'timestamp' => 1234567890,
        'user' => 'John',
    ],
]

Validation

Components support data validation:
class ValidatedComponent extends Component
{
    protected array $properties = [
        'email' => '',
        'age' => 0,
    ];

    public function save()
    {
        try {
            $validated = $this->validate([
                'email' => ['required', 'email'],
                'age' => ['required', 'integer', 'min:18'],
            ], [
                'email.required' => 'Email is required',
                'age.min' => 'You must be at least 18 years old',
            ]);

            // Use validated data
            return ['success' => true, 'data' => $validated];
        } catch (ValidationException $e) {
            return ['success' => false, 'errors' => $e->getErrors()];
        }
    }
}

AJAX Handler

Components can handle AJAX requests:
use Lyger\Components\LiveHandler;

// Handle incoming request
$request = [
    'component' => 'user-card',
    'method' => 'updateProfile',
    'params' => ['John Doe', 'john@example.com'],
    'properties' => [
        'userId' => 123,
    ],
];

$response = LiveHandler::handle($request);

// Response format:
// [
//     'success' => true,
//     'data' => [...],
//     'updates' => [...]
// ]

Slots

Components support slot-based content injection:
use Lyger\Components\Slot;

class LayoutComponent extends Component
{
    protected function renderInline(): string
    {
        return <<<HTML
        <div class="layout">
            <header>{{$header}}</header>
            <main>{{$slot}}</main>
            <footer>{{$footer}}</footer>
        </div>
        HTML;
    }
}

// Usage
$layout = new LayoutComponent();
$layout->header = new Slot('<h1>Site Title</h1>');
$layout->slot = new Slot('<p>Main content</p>');
$layout->footer = new Slot('<p>&copy; 2026</p>');

Best Practices

1

Component Composition

Build complex UIs by composing smaller, focused components rather than creating monolithic ones.
2

Separate Concerns

Keep presentation logic in components and business logic in services or models.
3

Reusability

Design components to be reusable across different contexts by using properties and configuration.
4

Type Safety

Use PHP type declarations for component properties and method parameters.
5

Documentation

Document component properties, methods, and usage examples for your team.

API Reference

Component Methods

MethodDescription
mount()Called when component is created
beforeRender()Called before rendering
render()Renders the component
renderView()Renders from external view file
renderInline()Renders inline template
toArray()Returns component as array
call($method, $params)Calls component method
emit($event, $data)Emits event
validate($rules, $messages)Validates properties
setProperty($key, $value)Sets property
getProperty($key)Gets property
refresh()Refreshes component

ComponentManager Methods

MethodDescription
register($name, $class)Registers component
make($name)Creates component instance
getRegistered()Gets all registered components

FormComponent Methods

MethodDescription
defineFields()Defines form fields
submit()Submits form
value($name)Gets field value
setValue($name, $value)Sets field value
error($name)Gets field error
hasError($name)Checks if field has error
renderFields()Renders all fields
renderField($name, $field)Renders single field

Table Methods

MethodDescription
defineColumns()Defines table columns
getData()Gets table data
sort($column)Sorts by column
MethodDescription
open()Opens modal
close()Closes modal
toggle()Toggles modal state