Build modern interactive applications with TypeScript classes and Edge templates. No client-side JavaScript required. No REST APIs. No GraphQL. Just your server code.
import { WireComponent } from 'adowire'
export default class Counter extends WireComponent {
count = 0
increment() {
this.count++
}
decrement() {
this.count--
}
}
<div>
<h1>{{ count }}</h1>
<button adowire:click="increment">
+
</button>
<button adowire:click="decrement">
−
</button>
</div>
That's it. No fetch calls. No REST APIs. No state management libraries. It just works.
Features
Write TypeScript on the server, HTML in your templates. Adowire handles the rest.
Build fully interactive apps without writing a single line of JavaScript. Your TypeScript class is the component.
Server renders HTML, the client diffs and patches only what changed. Powered by morphdom for surgical updates.
Every component snapshot is signed with HMAC-SHA256. Tampered state is rejected. No client can forge server data.
Decorators, full type safety, IDE autocompletion. @Title, @Layout, @Validate — it's all typed.
Stream content word-by-word from the server via Server-Sent Events. Perfect for AI/LLM output, progress updates, and real-time feeds.
Uses AdonisJS's native VineJS validator — 5–10× faster than Zod. Zero extra dependencies. Errors flow to your template automatically.
How it works
Every interaction follows the same simple cycle — no client state to manage.
Click, type, submit — any adowire:* directive fires
Snapshot + action sent via POST. No boilerplate fetch calls
Your TypeScript method runs, state updates, Edge re-renders the HTML
Only changed nodes are patched. No full re-render. Feels instant
import router from '@adonisjs/core/services/router'
// One line. Component IS the page.
router.adowire('/counter', 'counter')
router.adowire('/dashboard', 'dashboard')
router.adowire('/settings', 'settings')
// No controller. No view file. No middleware wiring.
// The component handles everything.
Directives
HTML attributes that wire your template to server-side logic. No JavaScript glue code.
adowire:click
Call server methods on click. Pass arguments inline.
adowire:model
Two-way binding with .live, .blur, .debounce, .throttle modifiers.
adowire:submit
Handle form submissions server-side with validation.
adowire:loading
Show/hide elements, add classes, set attributes during requests.
adowire:poll
Auto-refresh on an interval. Supports .visible modifier.
adowire:dirty
Show unsaved changes. Add classes when local state differs from server.
adowire:show
Toggle visibility with a JS expression. No server roundtrip needed.
adowire:cloak
Hide elements until initialized. Prevents flash of unstyled content.
adowire:stream
SSE streaming with append or .replace modes. Perfect for AI output.
adowire:key
Stable identity for list items. Efficient DOM diffing.
adowire:ignore
Protect client-only DOM from server re-renders.
Lifecycle
Full control over your component's lifecycle — from first mount to every subsequent round-trip. Plus property-specific hooks like updatingSearch().
mount()
— First request only
boot()
— Every request, before hydration
hydrate()
— Subsequent AJAX requests
updating() / updated()
— Property changes
rendering() / rendered()
— Template output
dehydrate()
— End of every request
exception()
— Handle errors gracefully
Decorators
TypeScript decorators that declare behavior at the class level — no configuration files needed.
@Computed()
— Memoized getter, accessible in templates
@Locked()
— Prevent client-side mutation
@Validate(rule)
— VineJS validation on properties
@Title(text)
— Set browser title for page components
@Layout(name)
— Wrap in a layout template
Magic Actions
Every component ships with 10 magic methods for common interactions — no boilerplate needed.
$refresh()
Re-render without calling any action
$set(prop, value)
Set a public property from the server
$toggle(prop)
Toggle a boolean property
$redirect(url)
Redirect the browser after the response
$dispatch(event)
Dispatch a browser event with params
$stream(name, content)
Stream content to a target element via SSE
$download(name, url)
Trigger a file download from the server
js(expression)
Execute a JavaScript expression on the client
skipRender()
Skip re-rendering for the current request
Plus state helpers: fill(), reset(), pull(), only(), all()
CLI
Scaffold, list, move, delete, and configure components from the command line.
make:adowire
Scaffold class + view. Supports --page, --class, --view flags.
adowire:list
List all registered components. Supports JSON output.
adowire:layout
Generate a default Adowire layout template.
adowire:move
Rename/move component files and update class names.
adowire:delete
Delete component class and view files with confirmation.
adowire:stubs
Publish stub templates for customization.
configure
One-time setup: config, provider, directories.
Why Adowire
adowire:click to a button. Done.
@error blocks in your template
Built on
Roadmap
$stream)
@Computed, @Locked, @Validate, @Title, @Layout
<adowire:counter />)
router.adowire()
$wire bridge
$dispatch, @On decorator, parent/child communication
@Lazy decorator
adowire:navigate for seamless page transitions
@Url decorator for query string binding
WireTest harness for Japa
Stop writing boilerplate. Start shipping features. Adowire makes AdonisJS apps feel like magic.