Quick Answer: In Angular, use signals with useHead() inside effect(). Pass getter functions to make values reactive. Use injectHead() to get the head instance.
When using Unhead with Angular, you get seamless integration with Angular's reactivity system. This guide explains how reactivity works in the Angular implementation and provides best practices for managing reactive head content.
Unhead's Angular integration leverages Angular's built-in reactivity features:
The simplest way to use reactivity is with Angular's signals:
import { Component, signal } from '@angular/core'
import { useHead } from '@unhead/angular'
@Component({
selector: 'app-counter',
template: `
<button (click)="incrementCounter()">Count: {{ counter() }}</button>
`
})
export class CounterComponent {
counter = signal(0)
constructor() {
useHead({
title: () => `Counter: ${this.counter()}`
})
}
incrementCounter() {
this.counter.update(value => value + 1)
}
}
In this example, every time the counter updates, the page title will automatically update to reflect the new value.
If you need to update head content after initialization, store the reference returned by useHead():
import { Component, signal } from '@angular/core'
import { useHead } from '@unhead/angular'
@Component({
// ...
})
export class MyComponent {
pageTitle = signal('Initial Title')
head = useHead({
title: () => this.pageTitle()
})
updateTitle(newTitle: string) {
this.pageTitle.set(newTitle)
}
// For more complex updates, use patch()
updateHeadContent() {
this.head.patch({
meta: [
{ name: 'description', content: 'New description' }
]
})
}
}
Unhead with Angular provides several ways to implement reactivity:
import { Component, signal } from '@angular/core'
import { useHead } from '@unhead/angular'
@Component({
// ...
})
export class MyComponent {
description = signal('Page description')
constructor() {
useHead({
meta: [
{ name: 'description', content: () => this.description() }
]
})
}
}
import { Component, effect, signal } from '@angular/core'
import { useHead } from '@unhead/angular'
@Component({
// ...
})
export class MyComponent {
title = signal('Page Title')
head = useHead()
constructor() {
// Not recommended pattern
effect(() => {
this.head.patch({
title: this.title()
})
})
}
}
This approach is less efficient as it creates additional effects and can lead to performance issues.
When using SSR with Angular, Unhead works differently:
To set initial SSR values, use the provideServerHead provider:
// app.config.server.ts
import { provideServerHead } from '@unhead/angular/server'
const serverConfig = {
providers: [
provideServerHead({
init: [
{
htmlAttrs: {
lang: 'en',
},
title: 'Server Default Title',
meta: [
{ name: 'description', content: 'Server default description' }
]
}
]
}),
]
}
() => mySignal()head = useHead()patch(): head.patch({ title: 'New Title' })effect() to watch and update head contentsUnhead's Angular integration automatically handles cleanup when components are destroyed. You don't need to manually dispose of head entries.
Under the hood, Unhead uses Angular's DestroyRef to register cleanup functions that remove head entries when components are destroyed.
Here's a complete example showing how to implement reactivity in a component:
import { Component, computed, signal } from '@angular/core'
import { useHead, useSeoMeta } from '@unhead/angular'
@Component({
selector: 'app-page',
template: `
<h1>{{ pageTitle() }}</h1>
<input [ngModel]="pageTitle()" (ngModelChange)="setPageTitle($event)" />
<button (click)="toggleDarkMode()">
Toggle {{ isDarkMode() ? 'Light' : 'Dark' }} Mode
</button>
`
})
export class PageComponent {
pageTitle = signal('My Reactive Page')
isDarkMode = signal(false)
// Computed values work well with reactivity
bodyClass = computed(() => this.isDarkMode() ? 'dark-theme' : 'light-theme')
// Store the head entry for later updates
head = useHead({
title: () => this.pageTitle(),
bodyAttrs: {
class: () => this.bodyClass()
}
})
// Also using useSeoMeta for SEO-specific tags
constructor() {
useSeoMeta({
description: () => `${this.pageTitle()} - Learn more about it!`
})
}
setPageTitle(newTitle: string) {
this.pageTitle.set(newTitle)
}
toggleDarkMode() {
this.isDarkMode.update(current => !current)
}
}
This component demonstrates: