Hooks
tags:resolve Hook
On this page
The tags:resolve
hook is one of the most important hooks in Unhead, called during the main tag resolution process. This hook provides access to all collected tags after basic normalization but before final rendering, allowing for comprehensive transformations, deduplication, and other processing.
Hook Signature
export interface Hook {
'tags:resolve': (ctx: TagResolveContext) => HookResult
}
Parameters
Name | Type | Description |
---|---|---|
ctx | TagResolveContext | Context object with the tag collection |
The TagResolveContext
interface is defined as:
interface TagResolveContext {
tagMap: Map<string, HeadTag>
tags: HeadTag[]
}
Returns
HookResult
which is either void
or Promise<void>
Usage Example
import { createHead } from '@unhead/react'
const head = createHead({
hooks: {
'tags:resolve': (ctx) => {
// Inspect all tags during resolution
console.log(`Resolving ${ctx.tags.length} tags`)
// Process specific tags
ctx.tags.forEach((tag) => {
if (tag.tag === 'meta' && tag.props.name === 'description') {
// Ensure descriptions don't exceed a certain length
if (tag.props.content && tag.props.content.length > 160) {
tag.props.content = `${tag.props.content.substring(0, 157)}...`
}
}
})
}
}
})
Use Cases
Custom Deduplication Logic
Implement custom deduplication for specific tag types:
import { defineHeadPlugin } from '@unhead/react'
export const customDedupePlugin = defineHeadPlugin({
hooks: {
'tags:resolve': (ctx) => {
// Custom deduplication for script tags with the same src
const seenScripts = new Map()
ctx.tags.forEach((tag) => {
if (tag.tag === 'script' && tag.props.src) {
const src = tag.props.src
if (seenScripts.has(src)) {
// Keep the script with higher priority or more attributes
const existing = seenScripts.get(src)
const existingProps = Object.keys(existing.props).length
const newProps = Object.keys(tag.props).length
if (tag.tagPriority === 'critical'
|| (tag.tagPriority === 'high' && existing.tagPriority !== 'critical')
|| (newProps > existingProps && existing.tagPriority === tag.tagPriority)) {
seenScripts.set(src, tag)
}
}
else {
seenScripts.set(src, tag)
}
}
})
// Replace script tags with deduplicated versions
ctx.tags = ctx.tags.map((tag) => {
if (tag.tag === 'script' && tag.props.src && seenScripts.has(tag.props.src)) {
return seenScripts.get(tag.props.src) === tag ? tag : null
}
return tag
}).filter(Boolean)
}
}
})
Transforming Tags Based on Content
Process and transform tags based on their content:
import { defineHeadPlugin } from '@unhead/react'
export const imageOptimizationPlugin = defineHeadPlugin({
hooks: {
'tags:resolve': (ctx) => {
// Find all image-related meta tags
ctx.tags.forEach((tag) => {
if (tag.tag === 'meta'
&& (tag.props.property === 'og:image' || tag.props.name === 'twitter:image')) {
// Process image URLs
const imageUrl = tag.props.content
// Skip already processed or external URLs
if (!imageUrl || imageUrl.startsWith('https://')
|| imageUrl.includes('?processed=true')) {
return
}
// Apply image optimization parameters
tag.props.content = `${imageUrl}?processed=true&width=1200&quality=80`
}
})
}
}
})
Implementing Template Parameter Processing
A real-world example of template parameter processing:
import { defineHeadPlugin } from '@unhead/react'
export const templateParamsPlugin = defineHeadPlugin({
hooks: {
'tags:resolve': (ctx) => {
// Extract template parameters
const templateParamsTag = ctx.tags.find(tag => tag.tag === 'templateParams')
if (!templateParamsTag)
return
// Get parameters
const params = templateParamsTag.props || {}
const separator = params.separator || '|'
// Remove the templateParams tag as it's not meant for rendering
ctx.tags = ctx.tags.filter(tag => tag.tag !== 'templateParams')
// Process tags with template parameters
ctx.tags.forEach((tag) => {
// Process meta content
if (tag.tag === 'meta' && tag.props.content
&& typeof tag.props.content === 'string') {
tag.props.content = processTemplateParams(tag.props.content, params, separator)
}
// Process title text
if (tag.tag === 'title' && tag.textContent) {
tag.textContent = processTemplateParams(tag.textContent, params, separator)
}
// Process other text content
if (tag.innerHTML && typeof tag.innerHTML === 'string') {
tag.innerHTML = processTemplateParams(tag.innerHTML, params, separator)
}
})
}
}
})
// Helper function to process template parameters
function processTemplateParams(text, params, separator) {
if (!text || typeof text !== 'string')
return text
return text.replace(/%(\w+)%/g, (match, key) => {
return params[key] !== undefined ? params[key] : match
})
}
Did this page help you?