Svelte
Hooks

dom:renderTag Hook

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

NameTypeDescription
ctxDomRenderTagContextContext object with tag information
documentDocumentThe DOM document where rendering occurs
trackanyUtility 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/svelte'

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/svelte'

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/svelte'

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/svelte'

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?