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 between inputs and server state.
adowire:submit
Handle form submissions server-side with validation.
adowire:loading
Show spinners and disable buttons during requests.
adowire:poll
Auto-refresh on an interval. Real-time without WebSockets.
adowire:stream
Server-Sent Events streaming. Word-by-word 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.
mount()
— First request only
boot()
— Every request
hydrate()
— Subsequent requests
updating() / updated()
— Property changes
rendering() / rendered()
— Template output
dehydrate()
— End of every request
Decorators
TypeScript decorators that declare behavior at the class level — no configuration files needed.
import { WireComponent,
Title, Layout } from 'adowire'
@Title('Contact Form')
@Layout('layouts/app')
export default class Contact
extends WireComponent {
name = ''
email = ''
message = ''
submit() {
// Validate, save, redirect
}
}
Why Adowire
adowire:click to a button. Done.
@error blocks in your template
Built on
Stop writing boilerplate. Start shipping features. Adowire makes AdonisJS apps feel like magic.