---
title: "@onEvent"
description: "Declarative event subscription for RadiantElement and RadiantController hosts."
group: Decorators
order: 10
---
# @onEvent
The `@onEvent` decorator provides a declarative way to subscribe to DOM events on elements within or outside your host. It automatically handles event subscription and unsubscription, ensuring no memory leaks when your host disconnects.
## Usage
```typescript
import { RadiantElement, customElement, onEvent } from '@ecopages/radiant';
@customElement('click-counter')
export class ClickCounter extends RadiantElement {
private count = 0;
@onEvent({ selector: 'button', type: 'click' })
handleClick(event: MouseEvent) {
this.count++;
console.log(`Clicked ${this.count} times`);
}
}
```
## Parameters
The decorator accepts a configuration object. You must provide a target and an event type.
### Target Selection (Choose one)
| Parameter | Type | Description |
| :-------- | :--- | :---------- |
| `selector` | `string` | CSS selector to match target elements (uses event delegation). |
| `ref` | `string` | Value of `data-ref` attribute to match (uses event delegation). |
| `window` | `boolean` | Listen on the global `window` object. |
| `document` | `boolean` | Listen on the global `document` object. |
### Event Configuration
| Parameter | Type | Required | Description |
| :-------- | :--- | :------- | :---------- |
| `type` | `string` | Yes | Event type (e.g., 'click', 'input', 'keydown'). |
| `options` | `AddEventListenerOptions` | No | Standard options like `{ passive: true, once: true }`. |
## Common Use Cases
### Using data-ref
This is the recommended approach for referencing elements within your component's structure.
```typescript
@onEvent({ ref: 'submit-btn', type: 'click' })
handleSubmit(event: MouseEvent) {
event.preventDefault();
console.log('Form submitted');
}
```
HTML:
```html
```
### Window and Document Events
```typescript
@onEvent({ window: true, type: 'scroll', options: { passive: true } })
handleScroll() {
console.log(`Scrolled to: ${window.scrollY}px`);
}
@onEvent({ document: true, type: 'keydown' })
handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') this.close();
}
```
### RadiantController usage
`@onEvent(...)` also works on `RadiantController`.
Use it when the controller is enhancing authored HTML, delegating across DOM it does not own through `render()`, or listening on `window` / `document`.
If the controller owns the markup through `render()`, prefer direct JSX handlers such as `on:click` for local interactions.
```typescript
import { RadiantController, onEvent, state } from '@ecopages/radiant';
export class ClickCounterController extends RadiantController {
@state count = 0;
@onEvent({ ref: 'button', type: 'click' })
handleClick() {
this.count += 1;
}
}
```
HTML:
```html
```
### Event Delegation for Non-Bubbling Events
Since `selector` and `ref` use event delegation, they rely on event bubbling. For events that don't bubble (like `focus`), use their bubbling alternatives (like `focusin`).
```typescript
// Works: focusin bubbles
@onEvent({ selector: 'input', type: 'focusin' })
handleFocus() { /* ... */ }
```
## Learn More
- [Best Practices](/docs/getting-started/best-practices) - Learn more about component design and events.
- [@query](/docs/decorators/query) - Query elements to use with events.
- [@debounce](/docs/decorators/debounce) - Debounce frequent event handlers.