Angular
You're viewing Unhead v3 beta documentation.
Core Concepts

Reactivity in Angular

Quick Answer: In Angular, use signals with useHead() inside effect(). Pass getter functions to make values reactive. Use injectHead() to get the head instance.

Introduction

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.

How Does Reactivity Work in Angular?

Unhead's Angular integration leverages Angular's built-in reactivity features:

  1. Angular Signals: For reactive state management
  2. Angular Effects: For tracking changes and updating the DOM
  3. Component Lifecycle: For proper cleanup and disposal

How Do I Make Head Tags Reactive?

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.

How Do I Update Head Content After Initialization?

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' }
      ]
    })
  }
}

What Are the Different Reactivity Methods?

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.

How Does SSR Work With Angular Reactivity?

When using SSR with Angular, Unhead works differently:

  1. In SSR, reactive values are resolved once when the page is rendered
  2. Changes to signals after initial render won't affect the server output
  3. The client will hydrate and take over reactivity after loading

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' }
          ]
        }
      ]
    }),
  ]
}

What Are the Best Practices?

DO:

  • Use function references with signals: () => mySignal()
  • Store the head entry for later updates: head = useHead()
  • Update reactively using patch(): head.patch({ title: 'New Title' })
  • Set default values in server configuration

DON'T:

  • Create new head entries on every change
  • Use Angular's effect() to watch and update head contents
  • Store signal values directly in head entries
  • Create nested reactive functions (performance impact)

Do I Need to Handle Cleanup Manually?

Unhead'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.

Full Example

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:

  • Reactive title based on a signal
  • Computed body classes based on a dark mode state
  • Two-way input binding with reactive updates
  • SEO metadata that reacts to title changes
Did this page help you?