Release: Unhead v2
Unhead v2 adds adapters for React, Svelte, Solid.js, and Angular alongside the existing Vue integration. It also rewrites the core rendering path.
Highlights
- Multi-framework Support: Added support for React, Svelte, Solid.js, and Angular
- Rendering: A rewritten core and cached tag resolution
- Capo.js Integration: Automatic tag ordering based on Capo.js recommendations
- Dependencies: A smaller runtime dependency set
- Documentation: New framework-specific installation and reactivity guides
State of head management
JavaScript frameworks need to manage document metadata outside the application's DOM mount point, on both the server and client:
<!DOCTYPE html>
<html>
<head>
<!-- Managed during server rendering and client navigation. -->
</head>
<body class="page">
<div id="your-javascript-framework"></div>
<script src="/your-javascript-framework.js"></script>
</body>
</html>
Frameworks commonly expose a component or provider that collects these tags during rendering:
<HeadProvider>
<title>My Page</title>
<meta name="description" content="My page description" />
<link rel="canonical" href="https://example.com/my-page" />
</HeadProvider>
Examples include <Helmet>, <Head>, <svelte:head>, and granular components such as <Title>, <Meta>, and <Link>.
Ecosystem
Framework ecosystems supplied many of the early solutions. Vue had Vue Meta and VueUse Head, while React applications often used React Helmet.
React v19 added native metadata elements. Angular Router also exposes a title property on routes.
A quick recap on Unhead
Unhead started as @vueuse/head and later moved to UnJS. The npm registry recorded 948,265 downloads of unhead in the week before this release, an average of more than 135,000 per day.
Its main APIs are useHead() for document tags and useSeoMeta() for typed SEO metadata.
v2 Release
For the original scope and discussion, see the v2 roadmap.
New Framework Support
Unhead v2 adds React, Svelte, Solid.js, and Angular adapters with framework-specific reactivity and lifecycle handling.
import { useHead } from 'unhead'
useHead(unheadInstance, {
title: '๐ @FRAMEWORK_NAME@'
})
- Vue (
@unhead/vue): Installation and Reactivity Guide - React (
@unhead/react): Installation and Reactivity Guide - Svelte (
@unhead/svelte): Installation and Reactivity Guide - Solid.js (
@unhead/solid-js): Installation and Reactivity Guide - Angular (
@unhead/angular): Installation and Reactivity Guide
Each adapter exposes the same core tag APIs through its framework's context and lifecycle primitives.
โก Runtime Performance Improvements
This release rewrites much of the core to reduce rendering work and improve tree-shaking.
Rewritten Core: The core rewrite reduces work in the rendering path.
Cached tag resolution: Tags are now cached between DOM renders, reducing the work performed while switching pages.
Leaner Core: Code changes and opt-in features reduce the client and server bundle surface. The original release benchmarks and comparison environment were not retained, so exact historical timing and size claims have been omitted here.
Capo.js Sorting
Unhead v2 applies Capo.js sorting to tags by default. This places resource hints, stylesheets, synchronous scripts, and metadata in Capo.js's recommended order.
<head>
<script defer src="defer-script.js"></script>
<script src="sync-script.js"></script>
<style>.sync-style { color: red }</style>
<link rel="modulepreload" href="modulepreload.js">
<script src="async-script.js" async></script>
<link rel="preload" href="preload.js">
<link rel="stylesheet" href="sync-styles.css">
<title>title</title>
<link rel="preconnect" href="https://example.com">
<link rel="dns-prefetch" href="https://example.com">
<link rel="prefetch" href="https://example.com">
<link rel="prerender" href="https://example.com">
<meta name="description" content="description">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
๐ฆ Bundle Improvements
Single dependency: Unhead relies on hookable for pluggability.
ESM and dropped workspace packages: Only ESM is now published, and workspace packages are deprecated in favor of subpath exports.
๐ Upgrading to v2
Unhead provides a legacy subpath build for staged migrations.
See the Migrate to v2 guide.
Roadmap at Release
At release, the roadmap included validation, higher-level script integrations, and Open Graph image generation.
Short Term: Improved Tag Validation
Some tag attributes depend on another attribute. For example, a <link rel="preload"> needs an as value.
At the time of the v2 release, Unhead's types did not enforce every such combination.
The roadmap considered three ways to catch invalid tag combinations:
- Improve the type system for optionally required attributes
- Implement ESLint rules to catch anything the types haven't
- Use a runtime plugin in development to validate resolved tags
Medium Term: Third-Party Scripts
The roadmap proposed service-specific wrappers around useScript(), such as useGoogleAnalytics() and useFacebookPixel().
These wrappers would connect script loading to framework lifecycles and expose typed service APIs.
const { proxy } = useGoogleAnalytics({
id: 'UA-123456789',
})
proxy.gtag('page_view', {
page_path: '/my-page',
})
Exploratory: OG Image
The roadmap also explored turning HTML templates into Open Graph images from a composable.
useOgImage('<div class="bg-red-500">my image :)</div>')
๐ Thank You
Thank you to everyone who contributed code, tests, and documentation.
Release: Unhead v3
Unhead v3 release notes: streaming SSR, a unified Vite plugin with DevTools, and rendering and bundle optimizations.
Unhead: Head Management for Vue, React, Svelte & More
Framework-agnostic library for managing HTML head tags. Titles, meta tags, scripts, and structured data with SSR support for Vue, React, Svelte, Solid, Angular.