--- title: "tags:resolve Hook" description: "Learn about the tags:resolve hook in Unhead that processes tags during the main resolution phase" navigation: title: "tags:resolve" --- 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 ```ts 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: ```ts interface TagResolveContext { tagMap: Map tags: HeadTag[] } ``` ### Returns `HookResult` which is either `void` or `Promise` ## Usage Example ```ts import { createHead } from '@unhead/dynamic-import' 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: ```ts import { defineHeadPlugin } from '@unhead/dynamic-import' 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: ```ts import { defineHeadPlugin } from '@unhead/dynamic-import' 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: ```ts import { defineHeadPlugin } from '@unhead/dynamic-import' 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 }) } ```