A lightweight Livewire-like reactive component system for Forge Engine applications with server-side rendering and real-time updates.
ForgeWire provides reactive components for Forge applications with server-side rendering, state management, and real-time updates without requiring WebSockets. It uses HTTP requests to handle component interactions and state changes.
Use Forge Package Manager or manually download and install the module.
# Install the latest version
php forge install:module ForgeWire
# Install a specific version
php forge install:module ForgeWire@1.0.5# Clone the repository
git clone https://github.com/forge-engine/modules.git
# Copy the ForgeWire module to your modules directory
cp -r modules/ForgeWire /path/to/your/forge/modules/Dependencies: ForgeWire requires proper session management and CSRF protection for secure component interactions.
Modify configuration file or use environment variables.
// config/forge_wire.php
return [
    "forge_wire" => [
        "example" => "hi"
    ]
];# Component settings
FORGEWIRE_CACHE_ENABLED=true
FORGEWIRE_DEBUG_MODE=false
# Session configuration
SESSION_LIFETIME=120
SESSION_SECURE=trueCreate reactive components with ForgeWire for dynamic user interfaces.
use App\Modules\ForgeWire\Core\WireComponent;
use App\Modules\ForgeWire\Attributes\Action;
use App\Modules\ForgeWire\Attributes\State;
class Counter extends WireComponent
{
    #[State]
    public int $count = 0;
    #[Action]
    public function increment(): void
    {
        $this->count++;
    }
    #[Action]
    public function decrement(): void
    {
        $this->count--;
    }
    public function render(): string
    {
        return $this->view('counter', ['count' => $this->count]);
    }
}<!-- views/counter.html -->
<div class="counter">
    <h3>Count: <span>{{ count }}</span></h3>
    <button wire:click="increment">+</button>
    <button wire:click="decrement">-</button>
</div>ForgeWire provides several helper functions for rendering components in your views:
// Basic usage
<?= wire(\App\Components\Counter::class) ?>
// With props
<?= wire(\App\Components\Counter::class, ['start' => 10]) ?>
// With component ID
<?= wire(\App\Components\Counter::class, 'counter-1') ?>
// With both ID and props
<?= wire(\App\Components\Counter::class, 'counter-1', ['start' => 10]) ?>// Shorter syntax
<?= w(\App\Components\Counter::class, ['start' => 10], 'counter-1') ?>// Converts kebab-case to PascalCase automatically
<?= wire_name('counter', 'counter-1') ?>
<?= wire_name('products-table', ['perPage' => 10], 'products-1') ?>
// These resolve to:
// - 'counter' -> App\Components\Counter
// - 'products-table' -> App\Components\ProductsTable
                            Make sure to include the ForgeWire JavaScript in your layout. Add the
                            forgewire() helper to your layout file:
                        
<!-- Real example from main.php -->
<?php
use Forge\Core\Helpers\ModuleResources;
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Forge Engine</title>
    <?= ModuleResources::loadStyles("forge-ui") ?>
    <?= raw(csrf_meta()) ?>
</head>
<body>
    <div>
        <?= $content ?>
    </div>
    <script>
        window.csrfToken = "<?= window_csrf_token() ?>";
    </script>
    <?= forgewire(); ?>
    <script defer src="/assets/app/js/htmx.min.js"></script>
    <?= ModuleResources::loadScripts("forge-ui") ?>
</body>
</html>
                                        Important: The forgewire() helper must be placed
                                        before the closing </body> tag to ensure proper loading of
                                        the ForgeWire JavaScript.
                                    
ForgeWire integrates with Forge Engine's CSRF protection. Use these helpers in your forms and layouts:
// In your layout head
<?= raw(csrf_meta()) ?>
// In your layout body
<script>
    window.csrfToken = "<?= window_csrf_token() ?>";
</script>
// In your forms
<form method="POST">
    <?= csrf_input() ?>
    <!-- Your form fields -->
</form>Security: CSRF protection is automatically applied to all ForgeWire component actions to prevent cross-site request forgery attacks.
Before using ForgeWire components, you can check if the module is available and properly configured:
// Check if ForgeWire is available
if (forgewire_is_available()) {
    // Safe to use ForgeWire components
    echo wire_name('counter');
    echo wire_name('products-table');
} else {
    // Fallback or error handling
    echo "ForgeWire is not available";
}Best Practice: Always check if ForgeWire is available before using components, especially in shared layouts or conditional rendering scenarios.
All ForgeWire components extend the base WireComponent class which provides core functionality.
Initialize component with properties.
public function mount(array $props = []): void
{
    if (isset($props['initialValue'])) {
        $this->value = $props['initialValue'];
    }
}Render the component view.
public function render(): string
{
    return $this->view('component-name', [
        'data' => $this->data
    ]);
}Actions are component methods that can be called from the frontend. Use the #[Action] attribute to mark methods as actions.
use App\Modules\ForgeWire\Attributes\Action;
class TodoList extends WireComponent
{
    #[Action]
    public function addTodo(string $title): void
    {
        $this->todos[] = [
            'id' => uniqid(),
            'title' => $title,
            'completed' => false
        ];
    }
    #[Action]
    public function toggleTodo(string $id): void
    {
        foreach ($this->todos as &$todo) {
            if ($todo['id'] === $id) {
                $todo['completed'] = !$todo['completed'];
                break;
            }
        }
    }
}Marks a method as callable from the frontend.
#[Action]
public function save(): void
{
    // Action logic
}Marks a property as reactive state.
#[State]
public string $message = '';Marks a method as a computed property.
#[Computed]
public function getFullName(): string
{
    return $this->firstName . ' ' . $this->lastName;
}Adds validation to component properties.
#[Validate('required|email')]
public string $email = '';ForgeWire provides several helper functions to make working with components easier. These functions are automatically available in your views when the module is installed.
Main helper function for rendering components.
// Signature
wire(string $componentClass, mixed $a = null, mixed $b = null): string
// Examples
wire(Counter::class);
wire(Counter::class, ['start' => 10]);
wire(Counter::class, 'counter-1');
wire(Counter::class, 'counter-1', ['start' => 10]);Shorter alias for the wire() function.
// Same as wire() but shorter
w(Counter::class, ['start' => 10], 'counter-1');Name-based component resolver that converts kebab-case to PascalCase.
// Converts kebab-case names to class names
wire_name('counter'); // App\Components\Counter
wire_name('products-table'); // App\Components\ProductsTable
wire_name('user-profile', ['userId' => 1], 'profile-1');Includes the ForgeWire JavaScript in your layout.
// Add to your layout footer
<?= forgewire(); ?>
// Outputs: <script defer src="/assets/modules/forge-wire/js/forgewire.js" defer></script>Check if ForgeWire is available and properly configured.
if (forgewire_is_available()) {
    // ForgeWire is ready to use
    echo wire(Counter::class);
}