Hooks
ssr:rendered Hook
On this page
The ssr:rendered
hook is called after the server-side rendering process has completed and all head tags have been converted to HTML strings. This hook provides access to the final HTML output and allows for post-processing of the rendered HTML.
Hook Signature
export interface Hook {
'ssr:rendered': (ctx: SSRRenderContext) => HookResult
}
Parameters
Name | Type | Description |
---|---|---|
ctx | SSRRenderContext | Context object with rendering results |
The SSRRenderContext
interface is defined as:
interface SSRRenderContext {
tags: HeadTag[]
html: SSRHeadPayload
}
interface SSRHeadPayload {
headTags: string
bodyTags: string
bodyTagsOpen: string
htmlAttrs: string
bodyAttrs: string
}
Returns
HookResult
which is either void
or Promise<void>
Usage Example
import { createHead } from 'unhead'
const head = createHead({
hooks: {
'ssr:rendered': (ctx) => {
// Log the rendered HTML
console.log('Head rendering complete:')
console.log('HTML attributes:', ctx.html.htmlAttrs)
console.log('Head tags:', ctx.html.headTags)
// Modify rendered HTML if needed
ctx.html.headTags += `<!-- Server rendered at ${new Date().toISOString()} -->`
}
}
})
Use Cases
HTML Post-processing
Apply final transformations to the rendered HTML:
import { defineHeadPlugin } from 'unhead'
export const htmlPostProcessingPlugin = defineHeadPlugin({
hooks: {
'ssr:rendered': (ctx) => {
// Add server timing information as an HTML comment
const renderTime = process.hrtime(globalThis.__UNHEAD_RENDER_START || [0, 0])
const renderTimeMs = Math.round((renderTime[0] * 1000) + (renderTime[1] / 1000000))
ctx.html.headTags += `\n<!-- Unhead SSR render time: ${renderTimeMs}ms -->`
// Add server information for debugging in staging environments
if (process.env.NODE_ENV === 'staging') {
ctx.html.headTags += `\n<!-- Server: ${process.env.SERVER_ID || 'unknown'}, Node: ${process.version} -->`
}
// Apply minification to HTML in production
if (process.env.NODE_ENV === 'production') {
// Simple minification - remove unnecessary whitespace
ctx.html.headTags = ctx.html.headTags
.replace(/>\s+</g, '><')
.replace(/\s{2,}/g, ' ')
.trim()
}
}
}
})
Caching Rendered Output
Cache the rendered HTML for performance optimization:
import { defineHeadPlugin } from 'unhead'
export const ssrCachePlugin = defineHeadPlugin({
hooks: {
'ssr:rendered': (ctx) => {
// Get the current cache key (set in ssr:beforeRender)
const cacheKey = globalThis.__UNHEAD_CURRENT_CACHE_KEY
if (cacheKey) {
// Initialize cache if needed
globalThis.__UNHEAD_CACHE = globalThis.__UNHEAD_CACHE || {}
// Store rendered HTML in cache
globalThis.__UNHEAD_CACHE[cacheKey] = {
html: { ...ctx.html },
timestamp: Date.now(),
tags: ctx.tags.length
}
// Log caching information
console.log(`Cached head HTML for key: ${cacheKey} (${ctx.tags.length} tags)`)
// Set expiration time
setTimeout(() => {
if (globalThis.__UNHEAD_CACHE && globalThis.__UNHEAD_CACHE[cacheKey]) {
delete globalThis.__UNHEAD_CACHE[cacheKey]
console.log(`Expired head HTML cache for key: ${cacheKey}`)
}
}, 300000) // Expire after 5 minutes
}
}
}
})
Analytics and Monitoring
Collect metrics about the server rendering process:
import { defineHeadPlugin } from 'unhead'
export const ssrAnalyticsPlugin = defineHeadPlugin({
hooks: {
'ssr:rendered': (ctx) => {
// Calculate sizes for monitoring
const metrics = {
tagsCount: ctx.tags.length,
htmlAttrsSize: ctx.html.htmlAttrs.length,
headTagsSize: ctx.html.headTags.length,
bodyTagsSize: ctx.html.bodyTags.length,
bodyAttrsSize: ctx.html.bodyAttrs.length,
totalSize: ctx.html.htmlAttrs.length
+ ctx.html.headTags.length
+ ctx.html.bodyTags.length
+ ctx.html.bodyAttrs.length
+ ctx.html.bodyTagsOpen.length
}
// Record metrics
if (process.env.COLLECT_METRICS) {
recordMetrics('unhead_ssr', metrics)
}
// Log warnings for unusually large payloads
if (metrics.totalSize > 20000) {
console.warn(`Large head payload detected: ${metrics.totalSize} bytes`)
console.warn(`Tags breakdown: HTML attrs (${metrics.htmlAttrsSize}), `
+ `Head tags (${metrics.headTagsSize}), `
+ `Body tags (${metrics.bodyTagsSize})`)
}
}
}
})
// Placeholder for your metrics collection system
function recordMetrics(name, data) {
console.log(`Recording metrics for ${name}:`, data)
// Your actual metrics recording logic here
}
Did this page help you?