Preact Event Handling — Capturing User Interactions in 3kB
Learn how Preact handles DOM events, synthetic vs native events, event delegation, and custom event patterns for user interactions in the lightweight framework.
In this lesson, you'll understand Preact's event system, how it differs from React's synthetic events, and how to handle common user interactions.
What You'll Learn
How to attach event handlers in Preact, understand that Preact uses native DOM events (not synthetic), handle form events, keyboard events, and custom events.
Why It Matters
Preact does NOT use React's synthetic event system. Events are attached directly to the DOM using addEventListener. This means native event behavior and no memory overhead from synthetic event pooling.
Real-World Use
Doda Browser's tab bar uses Preact events for click-to-switch, drag-to-reorder, and right-click context menus. Native DOM events give direct access to event.target and event.clientX without synthetic event wrappers.
flowchart LR
A[User Click] --> B[Native DOM Event]
B --> C[Preact onClick Handler]
C --> D[State Update]
D --> E[Re-render]
style C fill:#673ab8,color:#fff
Basic Event Handling
Preact uses camelCase event handler props like React:
function ClickButton() {
const handleClick = (event) => {
console.log('Button clicked!', event.type);
};
return <button onClick={handleClick}>Click me</button>;
}
Output: When clicked, the console logs "Button clicked!" and the native MouseEvent object. Preact passes the native event directly without wrapping it.
Passing Arguments
Use arrow functions or bind to pass additional arguments:
function ItemList() {
const handleItemClick = (id, event) => {
console.log(`Item ${id} clicked at (${event.clientX}, ${event.clientY})`);
};
return (
<ul>
{[1, 2, 3].map(id => (
<li key={id} onClick={(e) => handleItemClick(id, e)}>
Item {id}
</li>
))}
</ul>
);
}
Output: Clicking any item logs its ID and the click coordinates. The arrow function captures id and passes the native event.
Form Events
Preact handles form events natively:
function LoginForm() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (event) => {
event.preventDefault();
console.log('Logging in with:', username, password);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Username" />
<input type="password" value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password" />
<button type="submit">Login</button>
</form>
);
}
Output: On submit, the form data is logged. The page doesn't reload because preventDefault() stops the default form submission.
Keyboard and Focus Events
function SearchInput() {
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
console.log('Searching for:', event.target.value);
}
if (event.key === 'Escape') {
event.target.blur();
}
};
const handleFocus = () => console.log('Input focused');
const handleBlur = () => console.log('Input blurred');
return (
<input type="text"
onKeyDown={handleKeyDown}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder="Type and press Enter" />
);
}
Output: Pressing Enter triggers a search log, Escape removes focus, and focus/blur events are logged. All events are native DOM events.
Event Delegation
Preact doesn't implement synthetic event delegation like React. Events are attached to the actual DOM elements:
function LargeList({ items }) {
const handleClick = (event) => {
// event.target is the actual clicked element
const li = event.target.closest('li');
if (li) {
console.log('Clicked item:', li.dataset.id);
}
};
return (
<ul onClick={handleClick}>
{items.map(item => (
<li key={item.id} data-id={item.id}>{item.name}</li>
))}
</ul>
);
}
Output: Clicking any list item logs its ID. The event handler is on the <ul>, but event.target reveals which <li> was clicked.
Common Mistakes
- Expecting synthetic event pooling: Preact passes native DOM events directly. Don't call
event.persist()— it doesn't exist and isn't needed. - Using
onDoubleClickinconsistently: UseonDblClick(camelCase) for double-click events in Preact. - Forgetting
event.preventDefault()in form handlers: Without it, the form submits and reloads the page, losing component state. - Using arrow functions in JSX for every render: This creates a new function each render. For performance-critical lists, use
useCallbackor extract handlers. - Not removing event listeners: Preact cleans up event handlers when components unmount, but manually added
addEventListenercalls need manual cleanup incomponentWillUnmountoruseEffectcleanup.
Practice Questions
Does Preact use synthetic events? Answer: No. Preact attaches native DOM events directly. There's no synthetic event wrapper or pooling.
How do you stop a form from reloading the page? Answer: Call
event.preventDefault()inside theonSubmithandler.What is the correct prop name for double-click? Answer:
onDblClick. This maps to the nativedblclickDOM event.How do you pass the event object to a handler with custom arguments? Answer: Use an arrow function:
onClick={(e) => handle(id, e)}or usehandle.bind(null, id).
Challenge
Create a keyboard shortcut system that listens for Ctrl+S (save) and Ctrl+Z (undo) anywhere in the app. Use onKeyDown on the document or a container div.
Mini Project
Build a draggable color picker that tracks mouse drag events (onMouseDown, onMouseMove, onMouseUp) to let users select a hue and saturation from a 2D canvas.
FAQ
What's Next
Learn about Preact Lifecycle Methods to understand component mounting, updating, and unmounting in class components.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro