Components
Build a component with a PHP class, a PHTML template, and a Magento layout
block. Magewire resolves the class from the block's magewire argument and
passes it to the template as $magewire.
Create your first component
1. Add a component to your Magento module
This example adds a counter to the CMS home page. Start in an existing, enabled
Vendor_Module module. The three files are:
Vendor/Module/
├── Magewire/Counter.php
└── view/frontend/
├── layout/cms_index_index.xml
└── templates/magewire/counter.phtml
<?php
declare(strict_types=1);
namespace Vendor\Module\Magewire;
use Magewirephp\Magewire\Component;
class Counter extends Component
{
public int $count = 0;
public function increment(): void
{
$this->count++;
}
}
The public property is browser-visible state. The public method is an action the browser can call; validate and authorize actions that read or change business data.
2. Render it with PHTML
Magewire makes the component available to its template as $magewire:
<div data-testid="magewire-counter">
<span><?= $escaper->escapeHtml(__('Count: %1', $magewire->count)) ?></span>
<button type="button" wire:click="increment">
<?= $escaper->escapeHtml(__('Increase')) ?>
</button>
</div>
Keep one root element. Magewire uses it to attach the component snapshot and to
update the rendered HTML. Escape text and attributes using Magento's $escaper.
3. Place it with layout XML
The cms_index_index handle places the counter on the CMS home page:
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="content">
<block name="vendor.module.counter"
template="Vendor_Module::magewire/counter.phtml">
<arguments>
<argument name="magewire" xsi:type="object" shared="false">
Vendor\Module\Magewire\Counter
</argument>
</arguments>
</block>
</referenceContainer>
</body>
</page>
The magewire argument binds the PHP class to this block. shared="false" gives
the block its own component instance, which also makes the pattern safe when you
place the same class more than once on a page.
4. Try it
Open the home page and click Increase. Magewire calls increment() on the
server and updates the count without a full page reload. If the block does not
appear, confirm the module is enabled and that this page uses cms_index_index.
Pass initial values from layout
Prefix an argument with magewire:mount: to pass a named value to mount().
This makes the same class reusable on different pages:
<block name="vendor.module.category-products"
template="Vendor_Module::magewire/category-products.phtml">
<arguments>
<argument name="magewire" xsi:type="object" shared="false">
Vendor\Module\Magewire\CategoryProducts
</argument>
<argument name="magewire:mount:category-id" xsi:type="number">10</argument>
<argument name="magewire:mount:page-size" xsi:type="number">20</argument>
</arguments>
</block>
<?php
namespace Vendor\Module\Magewire;
use Magewirephp\Magewire\Component;
class CategoryProducts extends Component
{
public int $categoryId = 0;
public int $pageSize = 10;
public function mount(int $categoryId, int $pageSize = 10): void
{
$this->categoryId = $categoryId;
$this->pageSize = $pageSize;
}
}
Magewire converts the category-id and page-size suffixes to $categoryId
and $pageSize. mount() runs on the first render. On later requests, Magewire
restores public state from the snapshot, so treat both values as untrusted when
using them in a query or action. Resolve the current category and check its
visibility through your Magento service.
Reuse the class safely
Each rendered block needs a unique name and an independent component instance.
Set shared="false" on the Magento object argument when a class is bound more
than once. Give each block its own magewire:mount:* values:
<block name="vendor.module.featured" template="Vendor_Module::magewire/category-products.phtml">
<arguments>
<argument name="magewire" xsi:type="object" shared="false">Vendor\Module\Magewire\CategoryProducts</argument>
<argument name="magewire:mount:category-id" xsi:type="number">10</argument>
</arguments>
</block>
<block name="vendor.module.sale" template="Vendor_Module::magewire/category-products.phtml">
<arguments>
<argument name="magewire" xsi:type="object" shared="false">Vendor\Module\Magewire\CategoryProducts</argument>
<argument name="magewire:mount:category-id" xsi:type="number">20</argument>
</arguments>
</block>
Advanced block arguments
Custom resolvers and features can read grouped arguments through the argument
API. Application components usually only need magewire:mount:*, plus the
per-placement magewire:listeners, magewire:loader, and
magewire:modifiers arguments described in
Layout overrides.
$arguments->forMount()->all(); // ['categoryId' => 10, 'pageSize' => 20]
$arguments->forGroup('config')->all(); // ['cacheTtl' => 3600]
Use magewire:alias only when you need a stable component lookup alias, and
magewire:resolver only for a custom resolver. The older array binding with a
type item is retained for migration but marked deprecated in the V3 resolver.
A boolean type item cannot construct a component. The magewire.* namespace
is parsed but does not initialize public properties; use mount() instead.
See Resolvers for custom binding and Properties for public state.