Hooks
dom:renderTag Hook
On this page
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
export interface Hook {
'dom:renderTag': (ctx: DomRenderTagContext, document: Document, track: any) => HookResult
}
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
HookResult
which is either void
or Promise<void>
Usage Example
import { createHead } from '@unhead/react'
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
Custom Element Creation
Customize how DOM elements are created and configured:
import { defineHeadPlugin } from '@unhead/react'
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
Conditionally render tags based on browser capabilities:
import { defineHeadPlugin } from '@unhead/react'
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
Set up sophisticated event handling for rendered elements:
import { defineHeadPlugin } from '@unhead/react'
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 })
})
}
}
}
})
Did this page help you?