Svelte
Hooks

dom:rendered Hook

The dom:rendered hook is called after all tags have been rendered to the DOM. This hook provides access to the rendered elements and is useful for post-rendering operations, measurements, or triggering side effects that depend on the DOM being updated.

Hook Signature

export interface Hook {
  'dom:rendered': (ctx: { renders: DomRenderTagContext[] }) => HookResult
}

Parameters

NameTypeDescription
ctxObjectContext object containing rendering information
ctx.rendersDomRenderTagContext[]Array of rendered tag contexts

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:rendered': (ctx) => {
      // Log total number of elements rendered
      console.log(`Finished rendering ${ctx.renders.length} tags to the DOM`)

      // Add a class to the document to indicate rendering is complete
      document.documentElement.classList.add('head-rendered')

      // Dispatch an event that other code can listen for
      window.dispatchEvent(new CustomEvent('head-rendered', {
        detail: { count: ctx.renders.length }
      }))
    }
  }
})

Use Cases

Performance Monitoring

Track and report rendering performance:

import { defineHeadPlugin } from '@unhead/svelte'

export const performanceMonitorPlugin = defineHeadPlugin({
  hooks: {
    'dom:rendered': (ctx) => {
      // Calculate rendering metrics
      const renderCount = ctx.renders.length

      // Record performance entry
      if (window.performance && window.performance.mark) {
        window.performance.mark('unhead-dom-rendered')

        // Calculate time since navigation start
        const navStart = window.performance.timing.navigationStart
        const renderTime = Date.now() - navStart

        // Log performance data
        console.log(`Head rendering complete: ${renderCount} tags in ${renderTime}ms`)

        // Create a performance measure
        window.performance.measure('unhead-render-time', 'navigationStart', 'unhead-dom-rendered')
      }

      // Report to analytics if available
      if (window.dataLayer) {
        window.dataLayer.push({
          event: 'unhead_rendered',
          unhead_render_count: renderCount
        })
      }
    }
  }
})

Post-rendering Operations

Perform operations that need to happen after all tags are rendered:

import { defineHeadPlugin } from '@unhead/svelte'

export const postRenderPlugin = defineHeadPlugin({
  hooks: {
    'dom:rendered': (ctx) => {
      // Find all script tags that were rendered
      const scriptElements = ctx.renders
        .filter(render => render.tag.tag === 'script')
        .map(render => render.$el)

      // Set up monitoring for script completion
      if (scriptElements.length > 0) {
        let loadedCount = 0

        // Track when all scripts have loaded
        const scriptLoadHandler = () => {
          loadedCount++
          if (loadedCount === scriptElements.length) {
            console.log('All head scripts have loaded')
            document.body.classList.add('scripts-ready')

            // Notify application that scripts are ready
            window.dispatchEvent(new CustomEvent('head-scripts-loaded'))
          }
        }

        // Monitor each script
        scriptElements.forEach((script) => {
          if (script.hasAttribute('async') || script.hasAttribute('defer')) {
            script.addEventListener('load', scriptLoadHandler, { once: true })
            script.addEventListener('error', scriptLoadHandler, { once: true })
          }
          else {
            // Synchronous scripts are already loaded at this point
            loadedCount++
          }
        })

        // If all scripts were synchronous, trigger the event now
        if (loadedCount === scriptElements.length) {
          scriptLoadHandler()
        }
      }
    }
  }
})

DOM Cleanup

Clean up old or unused elements after rendering:

import { defineHeadPlugin } from '@unhead/svelte'

export const domCleanupPlugin = defineHeadPlugin({
  hooks: {
    'dom:rendered': (ctx) => {
      // Get IDs of all rendered elements
      const renderedIds = new Set(ctx.renders.map(render => render.id))

      // Find elements with data-unhead-id that weren't in this render batch
      const outdatedElements = document.querySelectorAll('[data-unhead-id]')

      Array.from(outdatedElements).forEach((element) => {
        const id = element.getAttribute('data-unhead-id')

        if (id && !renderedIds.has(id)) {
          // This element was from a previous render and is no longer needed
          console.log('Removing outdated head element:', element)
          element.parentNode?.removeChild(element)
        }
      })

      // Clean up any temporary markers
      document.querySelectorAll('[data-unhead-temp]').forEach((el) => {
        el.parentNode?.removeChild(el)
      })
    }
  }
})
Did this page help you?