---
title: "Handling DOM Events"
description: "Attach client-side load, error, and body event handlers through useHead, with SSR hydration support."
canonical_url: "https://unhead.unjs.io/docs/head/guides/core-concepts/dom-event-handling"
last_updated: "2026-08-11T00:47:04.572Z"
---

Pass functions to supported `on*` properties. Scripts support `onload` and `onerror`; stylesheet, preload, and module-preload links support load and error events; `bodyAttrs` supports window-level body events.

## Rendering and hydration

On the client, Unhead installs listeners with `addEventListener()` and removes or replaces them when the owning entry changes. Functions are not serialized into HTML.

During SSR, a function handler is rendered as a small marker handler such as:

```html
<script onload="this.dataset.onloadfired = true"></script>
```

If the event fires before hydration, the marker lets the client renderer replay the handler with a synthetic event after adopting the element. You should therefore register the same handler during SSR and hydration. Wrapping `useHead()` in `if (import.meta.client)` removes this replay path and can also produce different server and client head entries.

## Resource events

```ts
import { useHead } from '@unhead/dynamic-import'

useHead({
  link: [
    {
      rel: 'stylesheet',
      href: '/assets/critical.css',
      onload: event => console.log('Stylesheet loaded', event),
      onerror: event => console.error('Stylesheet failed', event)
    }
  ],
  script: [
    {
      src: '/assets/app.js',
      defer: true,
      onload: event => console.log('Script loaded', event),
      onerror: event => console.error('Script failed', event)
    }
  ]
})
```

Browsers fire `load` after a stylesheet and its imports have loaded and parsed, and `error` if stylesheet processing fails. Script elements likewise receive `load` or `error` after the external resource is accepted or rejected. See the MDN references for [`<link>` load events](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/link#stylesheet_load_events) and [`<script>` loading](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script#notes).

Use [`useScript()`](/docs/head/api/composables/use-script) when you need triggers, a load promise, API resolution, warmup hints, or reusable `onLoaded()` and `onError()` callbacks.

## Body events

Handlers under `bodyAttrs` are attached to `window`, with `this` bound to `document.body`. Each handler receives its native `Event` object.

```ts
import { useHead } from '@unhead/dynamic-import'

useHead({
  bodyAttrs: {
    onresize(event) {
      console.log('Window resized', event)
    },
    onoffline() {
      showNotification('You are offline.')
    },
    ononline() {
      showNotification('You are back online.')
    }
  }
})
```

Supported body event names include `onafterprint`, `onbeforeprint`, `onbeforeunload`, `onerror`, `onhashchange`, `onload`, `onmessage`, `onoffline`, `ononline`, `onpagehide`, `onpageshow`, `onpopstate`, `onresize`, `onstorage`, and `onunload`.

## Cleanup

Unhead removes a listener when its entry is patched without that handler or when the entry is disposed. The return value of an event handler is ignored; returning a cleanup function from `onload` does not register cleanup work.

If a handler creates its own observer, timer, or subscription, clean that resource up through your framework's component lifecycle.

## See Also

- [Loading Scripts](/docs/head/guides/core-concepts/loading-scripts): Managed script loading
- [useScript() API](/docs/head/api/composables/use-script): Script callbacks and triggers
- [useHead() API](/docs/head/api/composables/use-head): Head entry reference
