Skip to content
Shamar

Install

This page is how you add Wire to an Adonis app after the overview has explained the round trip. It is not the admin-panel installer. You make a class, a view, and a tag. The provider finds the class when the app boots. You do not register a route per component.

Install @shamar/adonis. It depends on @shamar/wire, so you get the kernel with the panel. Install @shamar/wire on its own only when a page has no admin panel.

Later versions are published from GitHub Actions. npm trusts the publish.yml workflow in coolsam726/shamar as the publisher for @shamar/wire. A GitHub release runs that workflow.

The files live in three places. The left column is the same idea in Livewire, only if that comparison helps.

Livewire Wire
app/Livewire/Counter.php app/wire/counter.ts
resources/views/livewire/counter.blade.php resources/views/wire/counter.edge
<livewire:counter /> <wire:counter /> or @wire('counter')

The provider registers every class it finds. You do not write a kernel file, and you do not add routes for the browser script.

Terminal window
pnpm add @shamar/adonis

@shamar/adonis already depends on @shamar/wire. In adonisrc.ts:

providers: [
() => import('@adonisjs/core/providers/app_provider'),
() => import('@adonisjs/core/providers/edge_provider'),
() => import('@adonisjs/shield/shield_provider'),
() => import('@shamar/adonis/provider'),
]

If this app already hosts a Shamar panel, the provider is already in that list. Stop here and generate a component.

Boot does four things:

  1. Imports every class under app/wire (an empty folder is fine).
  2. Mounts resources/views/wire so Edge can render wire::counter.
  3. Registers <wire:counter />, @wire(), @persist(), and the wire() helper.
  4. Registers GET /wire.js and POST /wire, and signs snapshots with APP_KEY.

No config/shamar.ts means there is no admin panel. /wire is still registered.

Terminal window
node ace make:wire counter
node ace make:wire posts/form
File Tag
app/wire/counter.ts @wire('counter')
app/wire/posts/form.ts @wire('posts.form')
app/wire/posts/create_form.ts @wire('posts.create-form')

The class default-exports public fields as state and methods as actions. The view sits next to it, with the same path under resources/views/wire.

import { Wire } from '@shamar/adonis'
export default class Counter extends Wire {
count = 0
increment() {
this.count++
}
}
<button type="button" wire:click="increment">{{ count }}</button>

{{ count }} is the public field. Edge escapes it. @wire is one component per line, the same way @include is. Restart the dev server after adding a class: discovery runs at boot, not on every request.

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Counter</title>
<meta name="csrf-token" content="{{ csrfToken }}" />
<script defer src="/wire.js"></script>
</head>
<body>
<wire:counter />
</body>
</html>
router.get('/counter', async ({ view }) => view.render('pages/counter'))

<wire:counter /> and @wire('counter') render the same island. @wire is one component per line. The element form can sit in markup:

<wire:counter />
<wire:counter :count="4" />
<wire:posts.form title="Hello" :open="canEdit" />

A plain attribute is a string. :count is JavaScript, and count="{{ starting }}" is the same expression. Number and boolean fields are coerced from those strings, so count="4" still fills a count = 0 field. Unknown keys are ignored. <wire:counter></wire:counter> is the same as the self-closing tag when the body is empty.

@wire('counter', { count: 3 }) passes a props object directly. Inside the provider’s islands, the browser posts to /wire.

The playground serves this at /counter, with a second page at /counter/about linked by wire:navigate.

@persist('nav')
<a href="/counter" wire:navigate>Counter</a>
@end

@persist wraps a region in wire:persist so SPA navigation keeps that DOM node. The full behavior is SPA navigation.

Without Adonis is the kernel used by itself: you construct WireKernel, return HTML from render, and serve /wire.js. Components documents that object API. A normal Adonis app stays on this page.