---
title: "dom:renderTag Hook · v2 · Unhead"
meta:
  "og:description": "Learn about the dom:renderTag hook in Unhead that controls how individual tags are rendered to the DOM"
  "og:title": "dom:renderTag Hook · v2 · Unhead"
  description: "Learn about the dom:renderTag hook in Unhead that controls how individual tags are rendered to the DOM"
---

**Hooks**

# **dom:renderTag Hook**

**On this page **

- [Hook Signature](#hook-signature)
- [Usage Example](#usage-example)
- [Use Cases](#use-cases)

The `dom:renderTag` hook is called for each individual tag as it's being rendered to the DOM. This hook gives you fine-grained control over how each tag is rendered, with access to the DOM element, tag data, and the ability to track side effects.

## [Hook Signature](#hook-signature)

```
export interface Hook {
  'dom:renderTag': (ctx: DomRenderTagContext, document: Document, track: any) => HookResult
}
```

### [Parameters](#parameters)

| **Name** | **Type** | **Description** |
| --- | --- | --- |
| `ctx` | `DomRenderTagContext` | Context object with tag information |
| `document` | `Document` | The DOM document where rendering occurs |
| `track` | `any` | Utility for tracking side effects |

The `DomRenderTagContext` interface is defined as:

```
interface DomRenderTagContext {
  id: string
  $el: Element
  shouldRender: boolean
  tag: HeadTag
  entry?: HeadEntry<any>
  markSideEffect: (key: string, fn: () => void) => void
}
```

### [Returns](#returns)

`HookResult` which is either `void` or `Promise<void>`

## [Usage Example](#usage-example)

```
import { createHead } from '#imports'

const head = createHead({
  hooks: {
    'dom:renderTag': (ctx, document) => {
      const { tag, $el } = ctx

      // Log each tag being rendered
      console.log(\`Rendering ${tag.tag} to the DOM\`)

      // Add additional attribute to all rendered elements
      $el.setAttribute('data-rendered-at', new Date().toISOString())

      // Prevent rendering specific tags
      if (tag.tag === 'meta' && tag.props.name === 'robots'
        && tag.props.content === 'noindex') {
        ctx.shouldRender = false
      }
    }
  }
})
```

## [Use Cases](#use-cases)

### [Custom Element Creation](#custom-element-creation)

Customize how DOM elements are created and configured:

```
import { defineHeadPlugin } from '#imports'

export const customElementPlugin = defineHeadPlugin({
  hooks: {
    'dom:renderTag': (ctx, document) => {
      const { tag, $el } = ctx

      // Apply special handling for script tags
      if (tag.tag === 'script') {
        // Add custom attribute to track script loading performance
        $el.setAttribute('data-load-start', Date.now().toString())

        // Set up performance tracking
        ctx.markSideEffect('script-tracking', () => {
          $el.addEventListener('load', () => {
            const loadStart = Number.parseInt($el.getAttribute('data-load-start') || '0')
            const loadTime = Date.now() - loadStart
            console.log(\`Script loaded in ${loadTime}ms:\`, tag.props.src)
          }, { once: true })
        })
      }

      // Enhance meta tags with additional data
      if (tag.tag === 'meta') {
        // Add data attribute for debugging
        $el.setAttribute('data-unhead-id', ctx.id)
      }
    }
  }
})
```

### [Conditional Rendering with Feature Detection](#conditional-rendering-with-feature-detection)

Conditionally render tags based on browser capabilities:

```
import { defineHeadPlugin } from '#imports'

export const featureDetectionPlugin = defineHeadPlugin({
  hooks: {
    'dom:renderTag': (ctx) => {
      const { tag } = ctx

      // Skip rendering preload fonts if the browser doesn't support font loading API
      if (tag.tag === 'link'
        && tag.props.rel === 'preload'
        && tag.props.as === 'font'
        && !('FontFace' in window)) {
        ctx.shouldRender = false
        return
      }

      // Conditionally apply WebP images for supported browsers
      if (tag.tag === 'meta'
        && (tag.props.property === 'og:image' || tag.props.name === 'twitter:image')) {
        // Check for WebP support
        const supportsWebP = document.createElement('canvas')
          .toDataURL('image/webp')
          .indexOf('data:image/webp') === 0

        // Modify image URLs for WebP-capable browsers
        if (supportsWebP && tag.props.content
          && !tag.props.content.endsWith('.webp')
          && !tag.props.content.includes('?format=')) {
          tag.props.content = \`${tag.props.content}?format=webp\`
        }
      }
    }
  }
})
```

### [Advanced Event Handling](#advanced-event-handling)

Set up sophisticated event handling for rendered elements:

```
import { defineHeadPlugin } from '#imports'

export const eventHandlingPlugin = defineHeadPlugin({
  hooks: {
    'dom:renderTag': (ctx) => {
      const { tag, $el } = ctx

      // Handle script loading with retries
      if (tag.tag === 'script' && tag.props.src) {
        ctx.markSideEffect(\`script-retry-${tag.props.src}\`, () => {
          // Track failures
          let attempts = 0
          const maxAttempts = 3

          // Function to handle errors and retry loading
          function handleScriptError() {
            attempts++
            if (attempts < maxAttempts) {
              console.warn(\`Script failed to load, retrying (${attempts}/${maxAttempts}):\`, tag.props.src)

              // Create a new script element
              const newScript = document.createElement('script')

              // Copy all attributes
              Array.from($el.attributes).forEach((attr) => {
                newScript.setAttribute(attr.name, attr.value)
              })

              // Replace the failed script
              $el.parentNode?.insertBefore(newScript, $el)
              $el.parentNode?.removeChild($el)

              // Update reference to the new element
              ctx.$el = newScript
            }
            else {
              console.error(\`Script failed to load after ${maxAttempts} attempts:\`, tag.props.src)
            }
          }

          // Add error handler
          $el.addEventListener('error', handleScriptError, { once: true })
        })
      }
    }
  }
})
```

[Edit this page](https://github.com/unjs/unhead/edit/v2.1.2/docs/v2/head/7.api/hooks/10.dom-render-tag.md)

**Did this page help you? **

[**dom:beforeRender** Learn about the dom:beforeRender hook in Unhead that runs before tags are rendered to the DOM](https://unhead.unjs.io/docs/v2/head/api/hooks/dom-before-render) [**dom:rendered** Learn about the dom:rendered hook in Unhead that's called after all tags have been rendered to the DOM](https://unhead.unjs.io/docs/v2/head/api/hooks/dom-rendered)

**On this page **

- [Hook Signature](#hook-signature)
- [Usage Example](#usage-example)
- [Use Cases](#use-cases)