PHPackages                             bastinald/malzahar - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. bastinald/malzahar

ActiveLibrary

bastinald/malzahar
==================

A magic PHP framework. Build reactive web apps without writing HTML, CSS, or JavaScript! Powered by Tailwind, Alpine, Laravel, &amp; Livewire.

1.0.7(4y ago)27228[2 issues](https://github.com/bastinald/malzahar/issues)MITPHPPHP ^8.0

Since May 27Pushed 4y ago2 watchersCompare

[ Source](https://github.com/bastinald/malzahar)[ Packagist](https://packagist.org/packages/bastinald/malzahar)[ Docs](https://github.com/bastinald/malzahar)[ RSS](/packages/bastinald-malzahar/feed)WikiDiscussions master Synced today

READMEChangelog (4)Dependencies (4)Versions (9)Used By (0)

Malzahar
========

[](#malzahar)

A magic PHP framework. Build reactive web apps without writing HTML, CSS, or JavaScript! Powered by Tailwind, Alpine, Laravel, &amp; Livewire.

### Requirements

[](#requirements)

- PHP 8
- Laravel 8
- NPM

Installation
------------

[](#installation)

Create a new Laravel app:

```
laravel new my-app
```

Configure `.env` APP, DB, &amp; MAIL values:

```
APP_*
DB_*
MAIL_*
```

Require Malzahar via composer:

```
composer require bastinald/malzahar
```

Run the install command:

```
php artisan malz:install
```

This will install &amp; configure Tailwind &amp; Alpine and create some example components to get you started.

Commands
--------

[](#commands)

### Automatic Migrations

[](#automatic-migrations)

```
php artisan malz:migrate {--f} {--s} {--fs}
```

This will run the automatic migrations by comparing the `migration` methods of your models to the current database table structures and apply any necessary changes. Use the `--f` option for fresh, `--s` for seed, or `--fs` for both. Using automatic migrations is completely optional with Malzahar.

### Making Blade Components

[](#making-blade-components)

```
php artisan malz:blade {class}
```

This will generate a Blade abstraction component inside the `app/Components/Blade` folder. As with all generator commands, you may specify a sub folder using slashes or dot notation for the `class`.

### Making Livewire Components

[](#making-livewire-components)

```
php artisan malz:livewire {class} {--f}
```

This will generate a reactive Livewire component inside the `app/Components/Livewire` folder. Use the `--f` option to make a full-page component with automatic routing included.

### Making Models

[](#making-models)

```
php artisan malz:model {class}
```

This will generate an Eloquent model including a `migration` and `definition` method for use with the automatic migration command. It also creates a factory for the model.

Components
----------

[](#components)

### Blade Components

[](#blade-components)

Blade components are used to abstract one or more HTML components into their own PHP class so that they can be reused and maintained with ease.

For example, let's say you need to make a reusable `Button` component for your forms:

```
namespace App\Components\Blade\Forms;

use Bastinald\Malzahar\Components\Blade;
use Bastinald\Malzahar\Components\Html;

class Button extends Blade
{
    public $color = 'blue';

    public function attributes()
    {
        return [
            'type' => 'button',
        ];
    }

    public function classes()
    {
        return [
            'text-white rounded-md shadow-sm px-4 py-2',
            'bg-' . $color . '-600 hover:bg-' . $color . '-700',
        ];
    }

    public function template()
    {
        return Html::button($this->slot)
            ->merge($this);
    }
}
```

The design behind these components works similar to standard Laravel blade components, except we have a few more features. Notice the `attributes` and `classes` methods. This is where you would specify default attributes and classes for the component. These are applied via the `merge($this)` method on the `Html::button()` component. You can also see custom properties being utilized e.g. `$color` in the example above. Properties are different from attributes. Also, notice the use of `$this->slot` in the component template. The slot is what is passed via the `make()` method parameters.

Now you can use this `Button` component inside any other component via the `make()` method:

```
class Login extends Livewire
{
    public $email;

    public function template()
    {
        return GuestLayout::make(
            Html::form(
                Input::make()
                    ->type('email')
                    ->placeholder(__('Email'))
                    ->error($this->error('email'))
                    ->wireModelDefer('email'),

                Button::make(__('Login'))
                    ->color('red')
                    ->type('submit')
                    ->class('w-full'),
            )->wireSubmitPrevent('login'),
        );
    }
```

Notice how we can still pass a `color`, `type` and `class` to the `Button` component via chained methods, which will be merged with whatever default properties, attributes and classes are specified inside the component class itself.

Chain methods on the component in order to set class properties, HTML attributes, and CSS classes via Tailwind. Just use the name of the property or attribute as the method name, and its parameter will be the value. Tailwind classes can be applied via the `class` method.

### Livewire Components

[](#livewire-components)

Livewire components are reactive components used for making interactive partial and full page experiences.

Full page components should use a `route` and `title` method e.g.:

```
class Home extends Livewire
{
    public function route()
    {
        return Route::get('/home', static::class)
            ->name('home')
            ->middleware('auth');
    }

    public function title()
    {
        return __('Home');
    }

    public function template()
    {
        return AuthLayout::make(
            Html::h1($this->title())
                ->class('text-3xl font-bold mb-4'),

            Html::p(__('You are logged in!')),
        );
    }
}
```

This full page component would be accessible via the `/home` route.

For partial components, you can create any components you want to include inside of other Livewire components, and then include them via the `make()` method. You use the `make()` method to construct all of your custom Blade and Livewire components.

Let's say we created this simple partial component:

```
class Alert extends Livewire
{
    public $message;

    public function mount($message)
    {
        $this->message = $message;
    }

    public function template()
    {
        return Html::div(
            Html::p(__($this->message))
                ->class('font-bold mb-4'),

            Html::button(__('Change Message'))
                ->class('bg-blue-600 text-white px-4 py-2')
                ->wireClick('changeMessage'),
        );
    }

    public function changeMessage()
    {
        $this->message = 'I am a changed message.';
    }
}
```

Now we can include, and even declare mounted properties for this `Alert` component in our other Livewire components via `make()`:

```
class Home extends Livewire
{
    public function template()
    {
        return AuthLayout::make(
            Html::h1($this->title())
                ->class('text-3xl font-bold mb-4'),

            Alert::make()->message('Hello, world!'),
        );
    }
```

Notice how we can pass a `message` property to the `mount()` method via a magic `message()` method that is available to us dynamically. Malzahar works similarly for Blade, Livewire, and HTML components. You're a wizard, Harry!

Oh, and if you need to grab validation errors, you can use the `$this->error()` method inside your Livewire component:

```
use App\Components\Blade\Forms\Button;
use App\Components\Blade\Forms\Input;
use App\Components\Blade\Layouts\GuestLayout;
use Bastinald\Malzahar\Components\Html;
use Bastinald\Malzahar\Components\Livewire;

class Login extends Livewire
{
    public $email, $password;

    public function template()
    {
        return GuestLayout::make(
            Html::form(
                Input::make()
                    ->type('email')
                    ->placeholder(__('Email'))
                    ->error($this->error('email'))
                    ->wireModelDefer('email'),

                Input::make()
                    ->type('password')
                    ->placeholder(__('Password'))
                    ->error($this->error('password'))
                    ->wireModelDefer('password'),

                Button::make(__('Login'))
                    ->type('submit')
                    ->class('w-full'),
            )->wireSubmitPrevent('login'),
        );
    }

    public function rules()
    {
        return [
            'email' => ['required', 'email'],
        ];
    }

    public function login()
    {
        $this->validate();

        // attempt to log the user in
    }
```

### HTML Components

[](#html-components)

HTML components are the building blocks for all of your other components. The syntax is similar to other Malzahar components, and we can add any attributes we want via magically chained methods.

All HTML components can be made by using their tag as the constructor method, and attributes for said tag come after:

```
public $email;

public function template()
{
    return Html::div(
        Html::h1(__('Hello, world')),

        Html::input()
            ->type('email')
            ->placeholder(__('Email'))
            ->wireModelDefer('email'),

        Html::p(__('Look ma, no hands!'))
            ->class('text-blue-600 font-bold'),
    );
}
```

If you notice, you'll see that the parameters for the constructing method contain the slot (or content) for that HTML element. See how the `div` in the example above contains an `h1`, `input`, and `p` inside of it. Same goes for the `h1` element, it has some translated text inside it.

For chained attribute methods, just specify an HTML element attribute name, and it's value as the parameter. Look at the `Html::input()` example above, we have given it a `type` of `email`, etc.

HTML components also support Livewire and Alpine methods as well. In the example above, you can see the use of `wireModelDefer('email')`, which actually translates to `wire:model.defer="email"` in the rendered HTML. This is used to bind the input value to the `$email` property when an action is performed (`defer`).

Same concept goes for Alpine. Let's say we wanted to add some Tailwind transitions to a dropdown:

```
Html::div(
    Html::button('Open Dropdown')
        ->xOnClick('open = true'),

    Html::div('Dropdown Body')
        ->xShow('open')
        ->xTransitionEnter('transition ease-out duration-100')
        ->xTransitionEnterStart('transform opacity-0 scale-95')
        ->xTransitionEnterEnd('transform opacity-100 scale-100')
        ->xTransitionLeave('transition ease-in duration-75')
        ->xTransitionLeaveStart('transform opacity-100 scale-100')
        ->xTransitionLeaveEnd('transform opacity-0 scale-95')
        ->xOnClickAway('open = false')
        ->class('absolute bg-white p-3')
)->xData('{ open: false }'),
```

Notice how Livewire attributes start with `wire`, and the Alpine attributes start with `x`. Malzahar is smart enough to format these attributes to their proper syntax when being rendered to actual HTML on compile.

### Dynamic Components

[](#dynamic-components)

Sometimes you will need to use third party blade components in your Malzahar app. Fortunately, this package makes this very simple via the `Dynamic` class.

For example, let's say I installed the Laravel Honey package. Normally, to include this component inside one of my views, I would use something like this:

```

```

Now we can't use actual HTML with Malzahar, so what do we do? We use the `Dynamic` class with a magic constructor method:

```
Dynamic::honey(),
```

Passing attributes to the dynamic component is as simple as adding a chained method:

```
Dynamic::honey()->recaptcha(),
```

It all works similarly to other Malzahar components. With dynamic components, the constructor method is the name of the component itself, and attributes are passed the same way you would with HTML or other components.

Check out the `NavLink` example that was created when you installed Malzahar. You can even see Dynamic components utilizing the `merge($this)` method inside a custom Blade component:

```
class NavLink extends Blade
{
    public $icon, $route, $title;

    public function attributes()
    {
        return [
            'name' => 'heroicon-o-' . $this->icon,
        ];
    }

    public function classes()
    {
        return [
            'w-6 h-6',
            'text-gray-600 hover:text-black' => Route::currentRouteName() != $this->route,
            'text-blue-600' => Route::currentRouteName() == $this->route,
        ];
    }

    public function template()
    {
        return Html::a(
            Dynamic::icon()->merge($this),
        )->href(route($this->route))->title($this->title);
    }
}
```

Statements
----------

[](#statements)

### If Statement

[](#if-statement)

Conditional `if` statements with Malzahar are easy:

```
Statement::if($this->label,
    fn() => Html::label($this->label),
),
```

You can also use `elseif` and `else` as chained methods for your `if` statement:

```
public $color = 'blue';

public function template()
{
    return Statement::if($this->color == 'red',
        fn() => Html::h1(__('The color is red!'))
            ->class('text-red-600'),
    )->elseif($this->color == 'blue',
        fn() => Html::h2(__('The color is blue!'))
            ->class('text-blue-600'),
    )->else(
        fn() => Html::h3(__('The color is something else!')),
    );
}
```

Notice how the first parameter of `if` is the condition, and every closure after is what will be rendered if the statement passes.

### Each Statement

[](#each-statement)

`Each` statements, or loops as they're often called, allow you to iterate through a result set and display things per iteration.

For example, let's say we want to spit out a list of our users names:

```
Statement::each(User::all(),
    fn(User $user) => Html::div(e($user->name)),
)->empty(
    fn() => Html::p('No users found.'),
),
```

The first parameter of the `each` statement is the collection or array. The second parameter is a callable function with the item, and optionally the key. You can use the `empty` method to show something if no results are found.

Also, notice how the `e` function is used here to escape the users name. Normally, you'd use the `{{ $user->name }}` syntax in a view file, which literally just calls the `e` function when compiled.

Need to use the keys of the items? No problem:

```
Statement::each(['red' => '#ff0000', 'green' => '#00ff00'],
    fn($hexCode, $colorName) => Html::dl(
        Html::dt($colorName),
        Html::dd($hexCode),
    )->class('mb-4'),
),
```

If you have questions or need help, please use the Github issues and I will respond ASAP. Thank you for checking out this package and happy coding!

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance19

Infrequent updates — may be unmaintained

Popularity19

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

8

Last Release

1805d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/82109804?v=4)[bastinald](/maintainers/bastinald)[@bastinald](https://github.com/bastinald)

---

Top Contributors

[![bastinald](https://avatars.githubusercontent.com/u/82109804?v=4)](https://github.com/bastinald "bastinald (10 commits)")

### Embed Badge

![Health badge](/badges/bastinald-malzahar/health.svg)

```
[![Health](https://phpackages.com/badges/bastinald-malzahar/health.svg)](https://phpackages.com/packages/bastinald-malzahar)
```

###  Alternatives

[livewire/volt

An elegantly crafted functional API for Laravel Livewire.

4195.3M84](/packages/livewire-volt)[namu/wirechat

A Laravel Livewire messaging app for teams with private chats and group conversations.

54324.5k](/packages/namu-wirechat)[ramonrietdijk/livewire-tables

Dynamic tables for models with Laravel Livewire

21147.4k](/packages/ramonrietdijk-livewire-tables)[a2insights/filament-saas

Filament Saas for A2Insights

161.1k](/packages/a2insights-filament-saas)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
