---
title: "Unhead v2: The full-stack <head> package for any framework."
meta:
  "og:description": "Unhead v2 is here! With first-class support for all major frameworks, a complete core rewrite, and a focus on performance, Unhead is the ultimate <head> manager."
  "og:title": "Unhead v2: The full-stack <head> package for any framework."
  author: "Harlan Wilton"
  description: "Unhead v2 is here! With first-class support for all major frameworks, a complete core rewrite, and a focus on performance, Unhead is the ultimate <head> manager."
---

# **Unhead v2: The full-stack <head> package for any framework.**

[![Harlan Wilton](https://avatars.githubusercontent.com/u/5326365?v=4)Harlan Wilton](https://bsky.app/)

**5 min read**

Published March 24, 2024

I'm thrilled to announce the release of Unhead v2, a major milestone in Unhead becoming _the_ most performant and feature-complete head manager for _all reactive JavaScript frameworks_.

## [TL;DR - What's New in v2](#tldr-whats-new-in-v2)

- **Multi-framework Support**: Added support for [**React**](https://react.dev), [**Svelte**](https://svelte.dev), Solid.js, and [**Angular**](https://angular.dev)
- **Performance**: 51% faster SSR, 64% faster page switching, 21% smaller bundle size
- **Capo.js Integration**: Automatic tag sorting for optimal page loading
- **Leaner Package**: 14kb reduction in node_modules, single dependency
- **New Docs**: 👋

## [State of head management](#state-of-head-management)

All JavaScript frameworks will run into the same challenge: how can they effectively manage tags and attributes outside the DOM entry in a server-side rendered world?

```
<!DOCTYPE html>
<html>
  <head>
    <!-- JavaScript frameworks need to manage tags here server & client-side -->
  </head>
  <body> <!-- And probably body attributes -->
    <!-- But also this -->
    <div id="your-javascript-framework"></div>
    <!-- and this... -->
    <script src="/your-javascript-framework.js"></script>
  </body>
</html>
```

As each framework has its unique constraints, they have all come up with their own solutions to this problem. Most of them converged on a head provider pattern, where the framework allows you to wrap tags in a specific component (or provider) and they figure out the rest.

ComponentFoo

```
<HeadProvider>
  <title>My Page</title>
  <meta name="description" content="My page description" />
  <link rel="canonical" href="https://example.com/my-page" />
</HeadProvider>
```

Seen as: `<Helmet>`, `<Head>`, `<svelte:head>` or more granular tags such as `<Title>`, `<Meta>`, `<Link>`, etc.

### [Ecosystem](#ecosystem)

Traditionally frameworks like React, [**Vue**](https://vuejs.org) and Angular have left it up to their respective ecosystems to solve this problem. Vue had [**Vue Meta**](https://vue-meta.nuxtjs.org/) and [**VueUse Head**](https://github.com/vueuse/head) and React had and continues to have [**React Helmet**](https://github.com/nfl/react-helmet).

We see some frameworks shifting towards providing simple support as part of their core, such as in [**React v19**](https://react.dev/blog/2024/12/05/react-19) and in [**Angular v14**](https://angular.io/guide/releases#angular-v14-0-0).

These solutions tend to be reliant on the component pattern, which is limited in its capabilities.

## [A quick recap on Unhead](#a-quick-recap-on-unhead)

Unhead started out humbly 5 years ago as [`@vueuse/head`](https://github.com/vueuse/head). Since then it's joined [**UnJS**](https://unjs.io/) as a high-quality, single-purpose package that works anywhere and is downloaded over ~100k times a day.

Built from bullet-proof primitives such as `useHead()` and `useSeoMeta()`, Unhead is building out the ecosystem of head management which is being realized through the v2 release.

## [v2 Release](#v2-release)

For the full changelog of changes please reference the [**v2 roadmap**](https://github.com/unjs/unhead/issues/395) GitHub Issue.

### [New Framework Supported](#new-framework-supported)

Unhead started as a Vue-focused solution, but with v2, we've expanded to support all major frontend frameworks with deep reactivity integrations.

```
import { useHead } from '@unhead/dynamic-import'

useHead({
  title: '👋 @FRAMEWORK_NAME@'
})
```

- **Vue** - `@unhead/vue`: [**Installation**](https://unhead.unjs.io/docs/vue/head/guides/get-started/installation) & [**Reactivity Guide**](https://unhead.unjs.io/docs/vue/head/guides/core-concepts/reactivity-and-context)
- **React** - `@unhead/react`: [**Installation**](https://unhead.unjs.io/docs/react/head/guides/get-started/installation) & [**Reactivity Guide**](https://unhead.unjs.io/docs/react/head/guides/core-concepts/reactivity)
- **Svelte** - `@unhead/svelte`: [**Installation**](https://unhead.unjs.io/docs/svelte/head/guides/get-started/installation) & [**Reactivity Guide**](https://unhead.unjs.io/docs/svelte/head/guides/core-concepts/reactivity)
- **Solid.js** - `@unhead/solid-js`: [**Installation**](https://unhead.unjs.io/docs/solid-js/head/guides/get-started/installation) & [**Reactivity Guide**](https://unhead.unjs.io/docs/solid-js/head/guides/core-concepts/reactivity)
- **Angular** - `@unhead/angular`: [**Installation**](https://unhead.unjs.io/docs/angular/head/guides/get-started/installation) & [**Reactivity Guide**](https://unhead.unjs.io/docs/angular/head/guides/core-concepts/reactivity)

Each framework integration provides the same powerful features with idiomatic patterns for that ecosystem.

### [⚡ Runtime Performance Improvements](#runtime-performance-improvements)

Every tenth of a millisecond counts performance. In this release, much of the core has been rewritten to improve performance and tree-shakability.

**Rewritten Core**: With the [**core being rewritten**](https://github.com/unjs/unhead/pull/488), optimizations were done to improve the fast-path times.

- ✅ SSR **+51% Faster** 0.34ms ⇢ 0.20ms ([**Benchmark: Medium Site**](https://github.com/unjs/unhead/blob/main/bench/ssr-harlanzw-com-e2e.bench.ts))

**Faster INP**: Tag resolves are now [**cached between DOM renders**](https://github.com/unjs/unhead/pull/504). For large sites, this _can_ lead to lower INP when switching between pages.

- ✅ CSR Page Switching **+64% Faster** 0.43ms ⇢ 0.22ms ([**Benchmark: 10 Page Changes x useHead()**](https://github.com/unjs/unhead/blob/main/packages/unhead/test/bench/ssr-perf.bench.ts))

**Leaner Core**: Through aggressive code optimizations and moving some features to opt-in, we see an improvement in the bundle size.

- ✅ Client: **21% smaller** - 13.6 kB (gz 5.3 kB) ⇢ 10.7 kB (gz 4.5 kB) ([**Minimal: useHead() + createUnhead()**](https://github.com/nuxt/nuxt/pull/31169/files#diff-04a7585c5d6ddd5cf80321c69bc6e07cd85e9e86ae35fa5f9c036651f137c312R26) )
- ✅ Server: **22% smaller** - 10.3 kB (gz 4.1 kB) ⇢ 8 kB (gz 3.4 kB) ([**Minimal: useHead() + createUnhead()**](https://github.com/nuxt/nuxt/pull/31169/files#diff-04a7585c5d6ddd5cf80321c69bc6e07cd85e9e86ae35fa5f9c036651f137c312R26))

Benchmarks do not reflect real-world performance.

0

0.1ms

0.2ms

0.3ms

0.4ms

0.34ms

0.20ms

+51%

SSR Time

Medium Site

0.43ms

0.22ms

+64%

Page Changes

10x useHead()

13.6kB

10.7kB

-21%

Client Size

Minimal Build

10.3kB

8.0kB

-22%

Server Size

Minimal Build

Before

After

### [Capo.js Sorting](#capojs-sorting)

Unhead v2 now applies capo.js sorting to all tags by default which provides optimizations to improve the performance of your site for end users. This sorting follows best practices for resource loading order in the document head.

<head><scriptdefersrc="defer-script.js"></script><scriptsrc="sync-script.js"></script><style>.sync-style{color:red}</style><linkrel="modulepreload"href="modulepreload.js"><scriptsrc="async-script.js"async></script><linkrel="preload"href="preload.js"><linkrel="stylesheet"href="sync-styles.css"><title>title</title><linkrel="preconnect"href="https://example.com"><linkrel="dns-prefetch"href="https://example.com"><linkrel="prefetch"href="https://example.com"><linkrel="prerender"href="https://example.com"><metaname="description"content="description"><metaname="viewport"content="width=device-width, initial-scale=1.0"></head>

**Enable Capo.js**

### [📦 Bundle Improvements](#bundle-improvements)

**Single dependency** Unhead now relies on a single dependency, [**hookable**](https://github.com/unjs/hookable), used for pluggability.

- ✅ **5 fewer** dependencies

**ESM & dropped workspace packages** Only ESM is now published, workspace packages are deprecated for subpath exports.

- ✅ **14kb reduction** in `node_modules`

## [🔄 Upgrading to v2](#upgrading-to-v2)

The migration path is straightforward; Unhead provides a `legacy` subpath build to ease the transition.

- [**TypeScript**](https://unhead.unjs.io/docs/typescript/head/guides/get-started/migration)
- [**Vue**](https://unhead.unjs.io/docs/vue/head/guides/get-started/migration)
- [**Nuxt**](https://unhead.unjs.io/docs/nuxt/head/guides/get-started/migration)

## [🔮 The Future](#the-future)

Now that Unhead has a battle-tested core and supports all major frameworks, we can start to build out the ecosystem of head management. Here's what's on our roadmap:

### [Short Term: Improved Tag Validation](#short-term-improved-tag-validation)

For several tags, the spec requires certain attributes when you use another attribute. For example, the `<link>` tag only requires an `as` attribute when you use `rel="preload"`.

The type system does not enforce this to avoid potential type juggling; however, it can lead to mistakes.

To catch broken tags we have several options:

- Improve the type system for optionally required attributes
- Implement [**ESLint**](https://eslint.org) rules to catch anything the types haven't
- Use a runtime plugin in development to validate resolved tags

### [Medium Term: Third-Party Scripts](#medium-term-third-party-scripts)

While Unhead already has a lower-level way to work with third-party scripts `useScript()`, we can make them easier and more secure to use by introducing higher-level composables, such as `useGoogleAnalytics()`, `useFacebookPixel()`, etc.

These would hook into framework lifecycles to ensure optimal performance and security while providing improved developer experience.

```
const { proxy } = useGoogleAnalytics({
  id: 'UA-123456789',
})

proxy.gtag('page_view', {
  page_path: '/my-page',
})
```

### [Exploratory: OG Image](#exploratory-og-image)

Turn HTML templates into OG images with a simple composable.

```
useOgImage('<div class="bg-red-500">my image :)</div>')
```

## [🙏 Thank You](#thank-you)

This release wouldn't be possible without our amazing community. Special thanks to all contributors who helped with code, testing, and documentation.