chore(deps): update unhead monorepo to v3 - #945

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo
Open

chore(deps): update unhead monorepo to v3#945
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo

Conversation

@renovate

@renovaterenovateBot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

PackageChangeAgeConfidence
@unhead/vue (source)^2.0.14^3.0.0ageconfidence
unhead (source)2.1.173.4.0ageconfidence

Release Notes

unjs/unhead (@​unhead/vue)

v3.4.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.3.0

Compare Source

Projects using the Unhead Bundler with Vite 6 or 7, webpack, Rspack, Rollup, or another build without Rolldown no longer receive oxc-parser from @unhead/bundler. Builds that use Unhead transforms must install it directly.

📝 Migration
pnpm add -D oxc-parser

Runtime-only Unhead projects and builds with Rolldown installed need no change.

🚨 Breaking Changes
🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.8

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.7

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.6

Compare Source

🏎 Performance
View changes on GitHub

v3.1.5

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.4

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.0

Compare Source

🛠️ Unhead CLI

To assist with migrations and overall DX a CLI has been introduced: @unhead/cli.

npx -y @unhead/cli 

It lets you do the following:

 audit Lint your codebase for unhead misuse, type-narrowing issues, and SEO/perf foot-guns. migrate Apply autofixes forv2-to-v3 migration: rewrite deprecated props and wrap tag literalsin defineX helpers.
validate-html Run the runtime ValidatePlugin over prerendered HTML files (e.g. dist/, .output/, build/). validate-url Fetch a rendered URL and run unhead\'s SEO/perf validation rules over its <head>. 

For example, try running audit on your own project for hints on how to improve your SEO.

✔️ Unhead ESLint

Knowing that your useHead() and useSeoMeta() code is right while your coding is important. While type-narrowing solves many broken cases, we introduce an ESLint plugin to help catch anything that the typechecker can't catch.

These rules are shared from the runtime ValidatePlugin

# flat-config ESLint plugin with v2→v3 migration autofixes
npm i -D @unhead/eslint-plugin
```ts[eslint.config.ts]import{configs}from'@&#8203;unhead/eslint-plugin'exportdefault[configs.recommended,]

🌊 Streaming SSR non-Vite support

The streaming plugin lived only at unhead/stream/vite previously, leaving non-Vite users with no way to wire the bootstrap. The plugin is now a bundler-agnostic unplugin factory with first-class webpack and Vite entries, and the framework packages compose it behind Unhead({ streaming: true }).

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'exportdefault{plugins: [vue(),Unhead({streaming: true})]}// webpack.config.tsimport{Unhead}from'@&#8203;unhead/vue/bundler'exportdefault{plugins: [...Unhead({streaming: true}).webpack()]}

Streaming also gains a nonce option (forwarded on every injected <script> for CSP support), a fixed async mode for production Vite builds (the IIFE is now emitted via this.emitFile() so the script src references a real hashed asset), a dev-mode warning when the client IIFE runs against an empty server queue, and a shared StreamingGlobal type so the server bootstrap, client, and injected IIFE agree on the shape of window.__unhead__. Default mode changed from async to inline for smaller TTFB.

Changelog

🚀 Features
🐞 Bug Fixes
View changes on GitHub

v3.0.5

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.4

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.0

Compare Source

Unhead v3 rebuilds the rendering engine from the ground up. The motivation: streaming SSR. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager.

📣 Highlights

🌊 Streaming SSR

Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new <title>, <meta>, and <link> tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load.

// entry-server.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/server'const{ head, wrapStream }=createStreamableHead()app.use(head)// wraps the Vue stream, injecting head updates as chunks resolvereturnwrapStream(renderToWebStream(app),template)
// entry-client.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/client'consthead=createStreamableHead()app.use(head)

Under the hood: a queue stub (window.__unhead__) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing.

Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See PR #​537.

🛠️ Unified Vite Plugin + DevTools

A single @unhead/{framework}/vite plugin replaces the old manual composition of @unhead/addons + streaming plugin + framework glue. One import, one call, and you get tree-shaking, useSeoMetauseHead transform, inline minification, streaming SSR, dev-mode ValidatePlugin auto-injection, and Vite DevTools integration.

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'importvuefrom'@&#8203;vitejs/plugin-vue'exportdefaultdefineConfig({plugins: [vue(),Unhead()],})

The DevTools panel surfaces live head state during development: every useHead() / useSeoMeta() call with its source file and line number, resolved tags, SEO overview (title, description, canonical, Open Graph), useScript() load status, active plugins, template params, and warnings from the Validate plugin. Source tracing lets you click through from any tag back to the exact line that created it.

Available for Vue, React, Svelte, Solid, and vanilla via @unhead/bundler/vite (the renamed @unhead/addons package; the old name still works with a deprecation warning).

See PRs #​726, #​733, #​731.

🎯 useHead() Type Narrowing

useHead() now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong.

useHead({link: [// Narrows to StylesheetLink: requires href, offers media, integrity, etc.{rel: 'stylesheet',href: '/styles.css'},// Narrows to PreloadLink: requires as attribute{rel: 'preload',as: 'font',href: '/font.woff2',crossorigin: 'anonymous'},],script: [// Narrows to ModuleScript{src: '/app.mjs',type: 'module'},// Narrows to JsonLdScript{type: 'application/ld+json',innerHTML: '{}'},],})

See PRs #​627, #​665, #​729.

✅ ValidatePlugin

New optional ValidatePlugin that inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, render-blocking scripts, late <meta charset>, too many fetchpriority="high" hints, preconnect without crossorigin, and more. Also includes v2 migration rules that detect deprecated property names (children, hid/vmid, body: true), missing TemplateParamsPlugin, and missing AliasSortingPlugin — all of which cause silent breakage on upgrade. Auto-injected in dev by the unified Vite plugin so warnings surface in the browser console without any manual setup. Fully tree-shakeable. Rules use ESLint-style flat config:

import{ValidatePlugin}from'unhead/plugins'createHead({plugins: [ValidatePlugin({rules: {'missing-description': 'off',}})]})

See PRs #​690, #​691, #​716, #​722, #​725, #​732.

🔗 Canonical Plugin

New built-in CanonicalPlugin that auto-generates <link rel="canonical"> tags and resolves relative URLs to absolute in og:image, twitter:image, and og:url. Includes query parameter filtering (strips tracking params like utm_source, fbclid, gclid by default), trailing slash normalization, and automatic hash fragment stripping. Essential for SEO and social sharing.

import{CanonicalPlugin}from'unhead/plugins'createHead({plugins: [CanonicalPlugin({canonicalHost: 'https://mysite.com',trailingSlash: true,queryWhitelist: ['page','sort'],})]})

See PRs #​492, #​713.

🗜️ MinifyPlugin

New optional MinifyPlugin that minifies inline <script> and <style> tag content during SSR. Uses lightweight pure-JS minifiers with zero native dependencies, safe for edge and serverless runtimes. A companion build-time transform (MinifyTransform in @unhead/bundler) pre-minifies static innerHTML literals at compile time. Standalone utilities (minifyJS, minifyCSS, minifyJSON) are also available via unhead/minify.

import{MinifyPlugin}from'unhead/plugins'createHead({plugins: [MinifyPlugin()]})

See PR #​705.

📦 Performance

Buildv3 sizev2 sizeDelta gz
client1025411513-534 (-11.2%)
server989410361-194 (-4.6%)
vueClient1132312567-533 (-10.2%)
vueServer1084911312-191 (-4.1%)
Benchmarkv2 meanv3 meanDelta
@unhead/vue0.106ms0.072ms32% faster
core0.088ms0.073ms17% faster

Key optimizations:

  • Client-only CAPO sorting (#​626)
  • Pure, tree-shakeable core with no side effects (#​632)
  • Minified internal DOM state properties (#​635)
  • Migrated unplugins from estree-walker/acorn-loose to oxc-walker (#​663)
  • Walker-based transformHtmlTemplate (#​581)
  • TemplateParamsPlugin and AliasSortingPlugin made opt-in for smaller bundles (#​493, #​494)

📊 Schema.org

  • 12 new nodes: Dataset, MusicAlbum, MusicGroup, MusicPlaylist, MusicRecording, PodcastEpisode, PodcastSeason, PodcastSeries, Service, TVEpisode, TVSeason, TVSeries (#​612)
  • Graph resolution rewrite for correctness and performance (#​616)
  • Removed ohash and defu dependencies (#​605)

🔄 Other Changes

  • renderDOMHead() / renderSSRHead() are now fully synchronous, single-pass via a composable resolveTags() pipeline; the head instance exposes a pluggable render() function for framework integrations (#​619, #​622, #​628, #​629, #​630)
  • @unhead/react/helmet drop-in compat export for users migrating from react-helmet (#​719)
  • useHeadSafe() now whitelists CSS styles (#​491)
  • Support for blocking attribute on scripts and stylesheets (#​489)
  • useScript() consolidated back into core, legacy support dropped (#​498)
  • fediverse:creator meta tag support (#​703)
  • Switched from hookable to lighter HookableCore with sync-only hooks (#​631)
  • Deprecation warnings added to aliased packages (@unhead/schema, @unhead/shared) (#​678)
  • templateParams extensible via module augmentation (#​679)
  • Respect user-provided twitter:card in InferSeoMetaPlugin (#​681)
  • Enforce as attribute for preload links (#​683)
  • onRendered callback option on useHead() for synchronizing with DOM head updates (#​712)
  • tagWeight option on createHead() to override default CAPO tag weight function (#​716)

🐛 Bug Fixes

  • Hydration race condition with deferred patches (#​634)
  • Process pending patches even when dirty is false (#​636)
  • Deduplicate matching tags inside same render cycle (#​668)
  • Dedupe <link rel="alternate"> correctly (#​655, #​656, #​658)
  • React: dispose head entries on unmount in StrictMode (#​664)
  • React: force invalidation on entry disposal (#​559)
  • Vue: support computed getter trigger (#​638)
  • Vue: expose @unhead/vue/stream/iife with correct types (#​707)
  • Scripts: prevent scope disposal from aborting unrelated trigger (#​660)
  • Schema.org: allow null to opt out of default values (#​680)
  • Schema.org: normalize target to array before merging potentialAction (#​709)
  • Avoid mutating cached titleTemplate tag in resolveTitleTemplate (#​715)

⚠️ Breaking Changes

Synchronous rendering

renderDOMHead() and renderSSRHead() no longer return promises. Remove await.

Build plugins: @unhead/addons@unhead/bundler (#​726, #​733)

The @unhead/addons package has been renamed to @unhead/bundler (the old name still works with a deprecation warning). Framework Vite plugins now use a namedUnhead export and ship from each framework's /vite subpath:

- import unhead from '@&#8203;unhead/addons/vite'+ import { Unhead } from '@&#8203;unhead/vue/vite'
// or @&#8203;unhead/react/vite, @&#8203;unhead/svelte/vite, @&#8203;unhead/solid-js/vite
export default defineConfig({
- plugins: [unhead()],+ plugins: [Unhead()],
})
Strict Link / Script / Meta types (#​729)

Link and Script unions no longer fall back to GenericLink / GenericScript, so the type system enforces per-tag constraints (e.g. preload + as: 'font' requires crossorigin). Meta content is now required; use content: null explicitly to remove a meta tag. Custom rel / type values need satisfies GenericLink / satisfies GenericScript.

Dropped deprecations (#​624)
OldNew

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • "on Monday"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@netlify

netlifyBot commented Apr 13, 2026

Copy link
Copy Markdown

Deploy Preview for friendly-lamington-fb5690 ready!

NameLink
🔨 Latest commit520747b
🔍 Latest deploy loghttps://app.netlify.com/projects/friendly-lamington-fb5690/deploys/6a95463d08572200086d390f
😎 Deploy Previewhttps://deploy-preview-945--friendly-lamington-fb5690.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changesRun an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 9 times, most recently from 1c6f416 to 1fde2a7CompareApril 20, 2026 05:58
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 68dc7c3 to 67ba4f7CompareApril 28, 2026 17:52
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 7 times, most recently from a176ffc to 443f048CompareMay 7, 2026 06:49
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b6aec16 to 56c374bCompareMay 11, 2026 07:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 48be561 to 8c00a0cCompareMay 28, 2026 13:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 8 times, most recently from 6e9d9a7 to 55ba0bcCompareJune 8, 2026 21:26
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 12 times, most recently from 23bb71d to 8006a92CompareJune 16, 2026 08:22
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b643394 to 795393fCompareJune 25, 2026 03:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

chore(deps): update unhead monorepo to v3 - #945

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo
Open

chore(deps): update unhead monorepo to v3#945
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo

Conversation

@renovate

@renovaterenovateBot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

PackageChangeAgeConfidence
@unhead/vue (source)^2.0.14^3.0.0ageconfidence
unhead (source)2.1.173.4.0ageconfidence

Release Notes

unjs/unhead (@​unhead/vue)

v3.4.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.3.0

Compare Source

Projects using the Unhead Bundler with Vite 6 or 7, webpack, Rspack, Rollup, or another build without Rolldown no longer receive oxc-parser from @unhead/bundler. Builds that use Unhead transforms must install it directly.

📝 Migration
pnpm add -D oxc-parser

Runtime-only Unhead projects and builds with Rolldown installed need no change.

🚨 Breaking Changes
🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.8

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.7

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.6

Compare Source

🏎 Performance
View changes on GitHub

v3.1.5

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.4

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.0

Compare Source

🛠️ Unhead CLI

To assist with migrations and overall DX a CLI has been introduced: @unhead/cli.

npx -y @unhead/cli 

It lets you do the following:

 audit Lint your codebase for unhead misuse, type-narrowing issues, and SEO/perf foot-guns. migrate Apply autofixes forv2-to-v3 migration: rewrite deprecated props and wrap tag literalsin defineX helpers.
validate-html Run the runtime ValidatePlugin over prerendered HTML files (e.g. dist/, .output/, build/). validate-url Fetch a rendered URL and run unhead\'s SEO/perf validation rules over its <head>. 

For example, try running audit on your own project for hints on how to improve your SEO.

✔️ Unhead ESLint

Knowing that your useHead() and useSeoMeta() code is right while your coding is important. While type-narrowing solves many broken cases, we introduce an ESLint plugin to help catch anything that the typechecker can't catch.

These rules are shared from the runtime ValidatePlugin

# flat-config ESLint plugin with v2→v3 migration autofixes
npm i -D @unhead/eslint-plugin
```ts[eslint.config.ts]import{configs}from'@&#8203;unhead/eslint-plugin'exportdefault[configs.recommended,]

🌊 Streaming SSR non-Vite support

The streaming plugin lived only at unhead/stream/vite previously, leaving non-Vite users with no way to wire the bootstrap. The plugin is now a bundler-agnostic unplugin factory with first-class webpack and Vite entries, and the framework packages compose it behind Unhead({ streaming: true }).

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'exportdefault{plugins: [vue(),Unhead({streaming: true})]}// webpack.config.tsimport{Unhead}from'@&#8203;unhead/vue/bundler'exportdefault{plugins: [...Unhead({streaming: true}).webpack()]}

Streaming also gains a nonce option (forwarded on every injected <script> for CSP support), a fixed async mode for production Vite builds (the IIFE is now emitted via this.emitFile() so the script src references a real hashed asset), a dev-mode warning when the client IIFE runs against an empty server queue, and a shared StreamingGlobal type so the server bootstrap, client, and injected IIFE agree on the shape of window.__unhead__. Default mode changed from async to inline for smaller TTFB.

Changelog

🚀 Features
🐞 Bug Fixes
View changes on GitHub

v3.0.5

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.4

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.0

Compare Source

Unhead v3 rebuilds the rendering engine from the ground up. The motivation: streaming SSR. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager.

📣 Highlights

🌊 Streaming SSR

Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new <title>, <meta>, and <link> tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load.

// entry-server.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/server'const{ head, wrapStream }=createStreamableHead()app.use(head)// wraps the Vue stream, injecting head updates as chunks resolvereturnwrapStream(renderToWebStream(app),template)
// entry-client.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/client'consthead=createStreamableHead()app.use(head)

Under the hood: a queue stub (window.__unhead__) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing.

Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See PR #​537.

🛠️ Unified Vite Plugin + DevTools

A single @unhead/{framework}/vite plugin replaces the old manual composition of @unhead/addons + streaming plugin + framework glue. One import, one call, and you get tree-shaking, useSeoMetauseHead transform, inline minification, streaming SSR, dev-mode ValidatePlugin auto-injection, and Vite DevTools integration.

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'importvuefrom'@&#8203;vitejs/plugin-vue'exportdefaultdefineConfig({plugins: [vue(),Unhead()],})

The DevTools panel surfaces live head state during development: every useHead() / useSeoMeta() call with its source file and line number, resolved tags, SEO overview (title, description, canonical, Open Graph), useScript() load status, active plugins, template params, and warnings from the Validate plugin. Source tracing lets you click through from any tag back to the exact line that created it.

Available for Vue, React, Svelte, Solid, and vanilla via @unhead/bundler/vite (the renamed @unhead/addons package; the old name still works with a deprecation warning).

See PRs #​726, #​733, #​731.

🎯 useHead() Type Narrowing

useHead() now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong.

useHead({link: [// Narrows to StylesheetLink: requires href, offers media, integrity, etc.{rel: 'stylesheet',href: '/styles.css'},// Narrows to PreloadLink: requires as attribute{rel: 'preload',as: 'font',href: '/font.woff2',crossorigin: 'anonymous'},],script: [// Narrows to ModuleScript{src: '/app.mjs',type: 'module'},// Narrows to JsonLdScript{type: 'application/ld+json',innerHTML: '{}'},],})

See PRs #​627, #​665, #​729.

✅ ValidatePlugin

New optional ValidatePlugin that inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, render-blocking scripts, late <meta charset>, too many fetchpriority="high" hints, preconnect without crossorigin, and more. Also includes v2 migration rules that detect deprecated property names (children, hid/vmid, body: true), missing TemplateParamsPlugin, and missing AliasSortingPlugin — all of which cause silent breakage on upgrade. Auto-injected in dev by the unified Vite plugin so warnings surface in the browser console without any manual setup. Fully tree-shakeable. Rules use ESLint-style flat config:

import{ValidatePlugin}from'unhead/plugins'createHead({plugins: [ValidatePlugin({rules: {'missing-description': 'off',}})]})

See PRs #​690, #​691, #​716, #​722, #​725, #​732.

🔗 Canonical Plugin

New built-in CanonicalPlugin that auto-generates <link rel="canonical"> tags and resolves relative URLs to absolute in og:image, twitter:image, and og:url. Includes query parameter filtering (strips tracking params like utm_source, fbclid, gclid by default), trailing slash normalization, and automatic hash fragment stripping. Essential for SEO and social sharing.

import{CanonicalPlugin}from'unhead/plugins'createHead({plugins: [CanonicalPlugin({canonicalHost: 'https://mysite.com',trailingSlash: true,queryWhitelist: ['page','sort'],})]})

See PRs #​492, #​713.

🗜️ MinifyPlugin

New optional MinifyPlugin that minifies inline <script> and <style> tag content during SSR. Uses lightweight pure-JS minifiers with zero native dependencies, safe for edge and serverless runtimes. A companion build-time transform (MinifyTransform in @unhead/bundler) pre-minifies static innerHTML literals at compile time. Standalone utilities (minifyJS, minifyCSS, minifyJSON) are also available via unhead/minify.

import{MinifyPlugin}from'unhead/plugins'createHead({plugins: [MinifyPlugin()]})

See PR #​705.

📦 Performance

Buildv3 sizev2 sizeDelta gz
client1025411513-534 (-11.2%)
server989410361-194 (-4.6%)
vueClient1132312567-533 (-10.2%)
vueServer1084911312-191 (-4.1%)
Benchmarkv2 meanv3 meanDelta
@unhead/vue0.106ms0.072ms32% faster
core0.088ms0.073ms17% faster

Key optimizations:

  • Client-only CAPO sorting (#​626)
  • Pure, tree-shakeable core with no side effects (#​632)
  • Minified internal DOM state properties (#​635)
  • Migrated unplugins from estree-walker/acorn-loose to oxc-walker (#​663)
  • Walker-based transformHtmlTemplate (#​581)
  • TemplateParamsPlugin and AliasSortingPlugin made opt-in for smaller bundles (#​493, #​494)

📊 Schema.org

  • 12 new nodes: Dataset, MusicAlbum, MusicGroup, MusicPlaylist, MusicRecording, PodcastEpisode, PodcastSeason, PodcastSeries, Service, TVEpisode, TVSeason, TVSeries (#​612)
  • Graph resolution rewrite for correctness and performance (#​616)
  • Removed ohash and defu dependencies (#​605)

🔄 Other Changes

  • renderDOMHead() / renderSSRHead() are now fully synchronous, single-pass via a composable resolveTags() pipeline; the head instance exposes a pluggable render() function for framework integrations (#​619, #​622, #​628, #​629, #​630)
  • @unhead/react/helmet drop-in compat export for users migrating from react-helmet (#​719)
  • useHeadSafe() now whitelists CSS styles (#​491)
  • Support for blocking attribute on scripts and stylesheets (#​489)
  • useScript() consolidated back into core, legacy support dropped (#​498)
  • fediverse:creator meta tag support (#​703)
  • Switched from hookable to lighter HookableCore with sync-only hooks (#​631)
  • Deprecation warnings added to aliased packages (@unhead/schema, @unhead/shared) (#​678)
  • templateParams extensible via module augmentation (#​679)
  • Respect user-provided twitter:card in InferSeoMetaPlugin (#​681)
  • Enforce as attribute for preload links (#​683)
  • onRendered callback option on useHead() for synchronizing with DOM head updates (#​712)
  • tagWeight option on createHead() to override default CAPO tag weight function (#​716)

🐛 Bug Fixes

  • Hydration race condition with deferred patches (#​634)
  • Process pending patches even when dirty is false (#​636)
  • Deduplicate matching tags inside same render cycle (#​668)
  • Dedupe <link rel="alternate"> correctly (#​655, #​656, #​658)
  • React: dispose head entries on unmount in StrictMode (#​664)
  • React: force invalidation on entry disposal (#​559)
  • Vue: support computed getter trigger (#​638)
  • Vue: expose @unhead/vue/stream/iife with correct types (#​707)
  • Scripts: prevent scope disposal from aborting unrelated trigger (#​660)
  • Schema.org: allow null to opt out of default values (#​680)
  • Schema.org: normalize target to array before merging potentialAction (#​709)
  • Avoid mutating cached titleTemplate tag in resolveTitleTemplate (#​715)

⚠️ Breaking Changes

Synchronous rendering

renderDOMHead() and renderSSRHead() no longer return promises. Remove await.

Build plugins: @unhead/addons@unhead/bundler (#​726, #​733)

The @unhead/addons package has been renamed to @unhead/bundler (the old name still works with a deprecation warning). Framework Vite plugins now use a namedUnhead export and ship from each framework's /vite subpath:

- import unhead from '@&#8203;unhead/addons/vite'+ import { Unhead } from '@&#8203;unhead/vue/vite'
// or @&#8203;unhead/react/vite, @&#8203;unhead/svelte/vite, @&#8203;unhead/solid-js/vite
export default defineConfig({
- plugins: [unhead()],+ plugins: [Unhead()],
})
Strict Link / Script / Meta types (#​729)

Link and Script unions no longer fall back to GenericLink / GenericScript, so the type system enforces per-tag constraints (e.g. preload + as: 'font' requires crossorigin). Meta content is now required; use content: null explicitly to remove a meta tag. Custom rel / type values need satisfies GenericLink / satisfies GenericScript.

Dropped deprecations (#​624)
OldNew

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • "on Monday"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@netlify

netlifyBot commented Apr 13, 2026

Copy link
Copy Markdown

Deploy Preview for friendly-lamington-fb5690 ready!

NameLink
🔨 Latest commit520747b
🔍 Latest deploy loghttps://app.netlify.com/projects/friendly-lamington-fb5690/deploys/6a95463d08572200086d390f
😎 Deploy Previewhttps://deploy-preview-945--friendly-lamington-fb5690.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changesRun an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 9 times, most recently from 1c6f416 to 1fde2a7CompareApril 20, 2026 05:58
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 68dc7c3 to 67ba4f7CompareApril 28, 2026 17:52
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 7 times, most recently from a176ffc to 443f048CompareMay 7, 2026 06:49
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b6aec16 to 56c374bCompareMay 11, 2026 07:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 48be561 to 8c00a0cCompareMay 28, 2026 13:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 8 times, most recently from 6e9d9a7 to 55ba0bcCompareJune 8, 2026 21:26
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 12 times, most recently from 23bb71d to 8006a92CompareJune 16, 2026 08:22
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b643394 to 795393fCompareJune 25, 2026 03:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(deps): update unhead monorepo to v3 - #945

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo
Open

chore(deps): update unhead monorepo to v3#945
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo

Conversation

@renovate

@renovaterenovateBot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

PackageChangeAgeConfidence
@unhead/vue (source)^2.0.14^3.0.0ageconfidence
unhead (source)2.1.173.4.0ageconfidence

Release Notes

unjs/unhead (@​unhead/vue)

v3.4.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.3.0

Compare Source

Projects using the Unhead Bundler with Vite 6 or 7, webpack, Rspack, Rollup, or another build without Rolldown no longer receive oxc-parser from @unhead/bundler. Builds that use Unhead transforms must install it directly.

📝 Migration
pnpm add -D oxc-parser

Runtime-only Unhead projects and builds with Rolldown installed need no change.

🚨 Breaking Changes
🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.8

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.7

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.6

Compare Source

🏎 Performance
View changes on GitHub

v3.1.5

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.4

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.0

Compare Source

🛠️ Unhead CLI

To assist with migrations and overall DX a CLI has been introduced: @unhead/cli.

npx -y @unhead/cli 

It lets you do the following:

 audit Lint your codebase for unhead misuse, type-narrowing issues, and SEO/perf foot-guns. migrate Apply autofixes forv2-to-v3 migration: rewrite deprecated props and wrap tag literalsin defineX helpers.
validate-html Run the runtime ValidatePlugin over prerendered HTML files (e.g. dist/, .output/, build/). validate-url Fetch a rendered URL and run unhead\'s SEO/perf validation rules over its <head>. 

For example, try running audit on your own project for hints on how to improve your SEO.

✔️ Unhead ESLint

Knowing that your useHead() and useSeoMeta() code is right while your coding is important. While type-narrowing solves many broken cases, we introduce an ESLint plugin to help catch anything that the typechecker can't catch.

These rules are shared from the runtime ValidatePlugin

# flat-config ESLint plugin with v2→v3 migration autofixes
npm i -D @unhead/eslint-plugin
```ts[eslint.config.ts]import{configs}from'@&#8203;unhead/eslint-plugin'exportdefault[configs.recommended,]

🌊 Streaming SSR non-Vite support

The streaming plugin lived only at unhead/stream/vite previously, leaving non-Vite users with no way to wire the bootstrap. The plugin is now a bundler-agnostic unplugin factory with first-class webpack and Vite entries, and the framework packages compose it behind Unhead({ streaming: true }).

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'exportdefault{plugins: [vue(),Unhead({streaming: true})]}// webpack.config.tsimport{Unhead}from'@&#8203;unhead/vue/bundler'exportdefault{plugins: [...Unhead({streaming: true}).webpack()]}

Streaming also gains a nonce option (forwarded on every injected <script> for CSP support), a fixed async mode for production Vite builds (the IIFE is now emitted via this.emitFile() so the script src references a real hashed asset), a dev-mode warning when the client IIFE runs against an empty server queue, and a shared StreamingGlobal type so the server bootstrap, client, and injected IIFE agree on the shape of window.__unhead__. Default mode changed from async to inline for smaller TTFB.

Changelog

🚀 Features
🐞 Bug Fixes
View changes on GitHub

v3.0.5

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.4

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.0

Compare Source

Unhead v3 rebuilds the rendering engine from the ground up. The motivation: streaming SSR. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager.

📣 Highlights

🌊 Streaming SSR

Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new <title>, <meta>, and <link> tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load.

// entry-server.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/server'const{ head, wrapStream }=createStreamableHead()app.use(head)// wraps the Vue stream, injecting head updates as chunks resolvereturnwrapStream(renderToWebStream(app),template)
// entry-client.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/client'consthead=createStreamableHead()app.use(head)

Under the hood: a queue stub (window.__unhead__) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing.

Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See PR #​537.

🛠️ Unified Vite Plugin + DevTools

A single @unhead/{framework}/vite plugin replaces the old manual composition of @unhead/addons + streaming plugin + framework glue. One import, one call, and you get tree-shaking, useSeoMetauseHead transform, inline minification, streaming SSR, dev-mode ValidatePlugin auto-injection, and Vite DevTools integration.

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'importvuefrom'@&#8203;vitejs/plugin-vue'exportdefaultdefineConfig({plugins: [vue(),Unhead()],})

The DevTools panel surfaces live head state during development: every useHead() / useSeoMeta() call with its source file and line number, resolved tags, SEO overview (title, description, canonical, Open Graph), useScript() load status, active plugins, template params, and warnings from the Validate plugin. Source tracing lets you click through from any tag back to the exact line that created it.

Available for Vue, React, Svelte, Solid, and vanilla via @unhead/bundler/vite (the renamed @unhead/addons package; the old name still works with a deprecation warning).

See PRs #​726, #​733, #​731.

🎯 useHead() Type Narrowing

useHead() now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong.

useHead({link: [// Narrows to StylesheetLink: requires href, offers media, integrity, etc.{rel: 'stylesheet',href: '/styles.css'},// Narrows to PreloadLink: requires as attribute{rel: 'preload',as: 'font',href: '/font.woff2',crossorigin: 'anonymous'},],script: [// Narrows to ModuleScript{src: '/app.mjs',type: 'module'},// Narrows to JsonLdScript{type: 'application/ld+json',innerHTML: '{}'},],})

See PRs #​627, #​665, #​729.

✅ ValidatePlugin

New optional ValidatePlugin that inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, render-blocking scripts, late <meta charset>, too many fetchpriority="high" hints, preconnect without crossorigin, and more. Also includes v2 migration rules that detect deprecated property names (children, hid/vmid, body: true), missing TemplateParamsPlugin, and missing AliasSortingPlugin — all of which cause silent breakage on upgrade. Auto-injected in dev by the unified Vite plugin so warnings surface in the browser console without any manual setup. Fully tree-shakeable. Rules use ESLint-style flat config:

import{ValidatePlugin}from'unhead/plugins'createHead({plugins: [ValidatePlugin({rules: {'missing-description': 'off',}})]})

See PRs #​690, #​691, #​716, #​722, #​725, #​732.

🔗 Canonical Plugin

New built-in CanonicalPlugin that auto-generates <link rel="canonical"> tags and resolves relative URLs to absolute in og:image, twitter:image, and og:url. Includes query parameter filtering (strips tracking params like utm_source, fbclid, gclid by default), trailing slash normalization, and automatic hash fragment stripping. Essential for SEO and social sharing.

import{CanonicalPlugin}from'unhead/plugins'createHead({plugins: [CanonicalPlugin({canonicalHost: 'https://mysite.com',trailingSlash: true,queryWhitelist: ['page','sort'],})]})

See PRs #​492, #​713.

🗜️ MinifyPlugin

New optional MinifyPlugin that minifies inline <script> and <style> tag content during SSR. Uses lightweight pure-JS minifiers with zero native dependencies, safe for edge and serverless runtimes. A companion build-time transform (MinifyTransform in @unhead/bundler) pre-minifies static innerHTML literals at compile time. Standalone utilities (minifyJS, minifyCSS, minifyJSON) are also available via unhead/minify.

import{MinifyPlugin}from'unhead/plugins'createHead({plugins: [MinifyPlugin()]})

See PR #​705.

📦 Performance

Buildv3 sizev2 sizeDelta gz
client1025411513-534 (-11.2%)
server989410361-194 (-4.6%)
vueClient1132312567-533 (-10.2%)
vueServer1084911312-191 (-4.1%)
Benchmarkv2 meanv3 meanDelta
@unhead/vue0.106ms0.072ms32% faster
core0.088ms0.073ms17% faster

Key optimizations:

  • Client-only CAPO sorting (#​626)
  • Pure, tree-shakeable core with no side effects (#​632)
  • Minified internal DOM state properties (#​635)
  • Migrated unplugins from estree-walker/acorn-loose to oxc-walker (#​663)
  • Walker-based transformHtmlTemplate (#​581)
  • TemplateParamsPlugin and AliasSortingPlugin made opt-in for smaller bundles (#​493, #​494)

📊 Schema.org

  • 12 new nodes: Dataset, MusicAlbum, MusicGroup, MusicPlaylist, MusicRecording, PodcastEpisode, PodcastSeason, PodcastSeries, Service, TVEpisode, TVSeason, TVSeries (#​612)
  • Graph resolution rewrite for correctness and performance (#​616)
  • Removed ohash and defu dependencies (#​605)

🔄 Other Changes

  • renderDOMHead() / renderSSRHead() are now fully synchronous, single-pass via a composable resolveTags() pipeline; the head instance exposes a pluggable render() function for framework integrations (#​619, #​622, #​628, #​629, #​630)
  • @unhead/react/helmet drop-in compat export for users migrating from react-helmet (#​719)
  • useHeadSafe() now whitelists CSS styles (#​491)
  • Support for blocking attribute on scripts and stylesheets (#​489)
  • useScript() consolidated back into core, legacy support dropped (#​498)
  • fediverse:creator meta tag support (#​703)
  • Switched from hookable to lighter HookableCore with sync-only hooks (#​631)
  • Deprecation warnings added to aliased packages (@unhead/schema, @unhead/shared) (#​678)
  • templateParams extensible via module augmentation (#​679)
  • Respect user-provided twitter:card in InferSeoMetaPlugin (#​681)
  • Enforce as attribute for preload links (#​683)
  • onRendered callback option on useHead() for synchronizing with DOM head updates (#​712)
  • tagWeight option on createHead() to override default CAPO tag weight function (#​716)

🐛 Bug Fixes

  • Hydration race condition with deferred patches (#​634)
  • Process pending patches even when dirty is false (#​636)
  • Deduplicate matching tags inside same render cycle (#​668)
  • Dedupe <link rel="alternate"> correctly (#​655, #​656, #​658)
  • React: dispose head entries on unmount in StrictMode (#​664)
  • React: force invalidation on entry disposal (#​559)
  • Vue: support computed getter trigger (#​638)
  • Vue: expose @unhead/vue/stream/iife with correct types (#​707)
  • Scripts: prevent scope disposal from aborting unrelated trigger (#​660)
  • Schema.org: allow null to opt out of default values (#​680)
  • Schema.org: normalize target to array before merging potentialAction (#​709)
  • Avoid mutating cached titleTemplate tag in resolveTitleTemplate (#​715)

⚠️ Breaking Changes

Synchronous rendering

renderDOMHead() and renderSSRHead() no longer return promises. Remove await.

Build plugins: @unhead/addons@unhead/bundler (#​726, #​733)

The @unhead/addons package has been renamed to @unhead/bundler (the old name still works with a deprecation warning). Framework Vite plugins now use a namedUnhead export and ship from each framework's /vite subpath:

- import unhead from '@&#8203;unhead/addons/vite'+ import { Unhead } from '@&#8203;unhead/vue/vite'
// or @&#8203;unhead/react/vite, @&#8203;unhead/svelte/vite, @&#8203;unhead/solid-js/vite
export default defineConfig({
- plugins: [unhead()],+ plugins: [Unhead()],
})
Strict Link / Script / Meta types (#​729)

Link and Script unions no longer fall back to GenericLink / GenericScript, so the type system enforces per-tag constraints (e.g. preload + as: 'font' requires crossorigin). Meta content is now required; use content: null explicitly to remove a meta tag. Custom rel / type values need satisfies GenericLink / satisfies GenericScript.

Dropped deprecations (#​624)
OldNew

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • "on Monday"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@netlify

netlifyBot commented Apr 13, 2026

Copy link
Copy Markdown

Deploy Preview for friendly-lamington-fb5690 ready!

NameLink
🔨 Latest commit520747b
🔍 Latest deploy loghttps://app.netlify.com/projects/friendly-lamington-fb5690/deploys/6a95463d08572200086d390f
😎 Deploy Previewhttps://deploy-preview-945--friendly-lamington-fb5690.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changesRun an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 9 times, most recently from 1c6f416 to 1fde2a7CompareApril 20, 2026 05:58
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 68dc7c3 to 67ba4f7CompareApril 28, 2026 17:52
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 7 times, most recently from a176ffc to 443f048CompareMay 7, 2026 06:49
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b6aec16 to 56c374bCompareMay 11, 2026 07:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 48be561 to 8c00a0cCompareMay 28, 2026 13:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 8 times, most recently from 6e9d9a7 to 55ba0bcCompareJune 8, 2026 21:26
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 12 times, most recently from 23bb71d to 8006a92CompareJune 16, 2026 08:22
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b643394 to 795393fCompareJune 25, 2026 03:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(deps): update unhead monorepo to v3 - #945

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo
Open

chore(deps): update unhead monorepo to v3#945
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo

Conversation

@renovate

@renovaterenovateBot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

PackageChangeAgeConfidence
@unhead/vue (source)^2.0.14^3.0.0ageconfidence
unhead (source)2.1.173.4.0ageconfidence

Release Notes

unjs/unhead (@​unhead/vue)

v3.4.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.3.0

Compare Source

Projects using the Unhead Bundler with Vite 6 or 7, webpack, Rspack, Rollup, or another build without Rolldown no longer receive oxc-parser from @unhead/bundler. Builds that use Unhead transforms must install it directly.

📝 Migration
pnpm add -D oxc-parser

Runtime-only Unhead projects and builds with Rolldown installed need no change.

🚨 Breaking Changes
🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.8

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.7

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.6

Compare Source

🏎 Performance
View changes on GitHub

v3.1.5

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.4

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.0

Compare Source

🛠️ Unhead CLI

To assist with migrations and overall DX a CLI has been introduced: @unhead/cli.

npx -y @unhead/cli 

It lets you do the following:

 audit Lint your codebase for unhead misuse, type-narrowing issues, and SEO/perf foot-guns. migrate Apply autofixes forv2-to-v3 migration: rewrite deprecated props and wrap tag literalsin defineX helpers.
validate-html Run the runtime ValidatePlugin over prerendered HTML files (e.g. dist/, .output/, build/). validate-url Fetch a rendered URL and run unhead\'s SEO/perf validation rules over its <head>. 

For example, try running audit on your own project for hints on how to improve your SEO.

✔️ Unhead ESLint

Knowing that your useHead() and useSeoMeta() code is right while your coding is important. While type-narrowing solves many broken cases, we introduce an ESLint plugin to help catch anything that the typechecker can't catch.

These rules are shared from the runtime ValidatePlugin

# flat-config ESLint plugin with v2→v3 migration autofixes
npm i -D @unhead/eslint-plugin
```ts[eslint.config.ts]import{configs}from'@&#8203;unhead/eslint-plugin'exportdefault[configs.recommended,]

🌊 Streaming SSR non-Vite support

The streaming plugin lived only at unhead/stream/vite previously, leaving non-Vite users with no way to wire the bootstrap. The plugin is now a bundler-agnostic unplugin factory with first-class webpack and Vite entries, and the framework packages compose it behind Unhead({ streaming: true }).

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'exportdefault{plugins: [vue(),Unhead({streaming: true})]}// webpack.config.tsimport{Unhead}from'@&#8203;unhead/vue/bundler'exportdefault{plugins: [...Unhead({streaming: true}).webpack()]}

Streaming also gains a nonce option (forwarded on every injected <script> for CSP support), a fixed async mode for production Vite builds (the IIFE is now emitted via this.emitFile() so the script src references a real hashed asset), a dev-mode warning when the client IIFE runs against an empty server queue, and a shared StreamingGlobal type so the server bootstrap, client, and injected IIFE agree on the shape of window.__unhead__. Default mode changed from async to inline for smaller TTFB.

Changelog

🚀 Features
🐞 Bug Fixes
View changes on GitHub

v3.0.5

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.4

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.0

Compare Source

Unhead v3 rebuilds the rendering engine from the ground up. The motivation: streaming SSR. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager.

📣 Highlights

🌊 Streaming SSR

Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new <title>, <meta>, and <link> tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load.

// entry-server.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/server'const{ head, wrapStream }=createStreamableHead()app.use(head)// wraps the Vue stream, injecting head updates as chunks resolvereturnwrapStream(renderToWebStream(app),template)
// entry-client.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/client'consthead=createStreamableHead()app.use(head)

Under the hood: a queue stub (window.__unhead__) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing.

Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See PR #​537.

🛠️ Unified Vite Plugin + DevTools

A single @unhead/{framework}/vite plugin replaces the old manual composition of @unhead/addons + streaming plugin + framework glue. One import, one call, and you get tree-shaking, useSeoMetauseHead transform, inline minification, streaming SSR, dev-mode ValidatePlugin auto-injection, and Vite DevTools integration.

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'importvuefrom'@&#8203;vitejs/plugin-vue'exportdefaultdefineConfig({plugins: [vue(),Unhead()],})

The DevTools panel surfaces live head state during development: every useHead() / useSeoMeta() call with its source file and line number, resolved tags, SEO overview (title, description, canonical, Open Graph), useScript() load status, active plugins, template params, and warnings from the Validate plugin. Source tracing lets you click through from any tag back to the exact line that created it.

Available for Vue, React, Svelte, Solid, and vanilla via @unhead/bundler/vite (the renamed @unhead/addons package; the old name still works with a deprecation warning).

See PRs #​726, #​733, #​731.

🎯 useHead() Type Narrowing

useHead() now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong.

useHead({link: [// Narrows to StylesheetLink: requires href, offers media, integrity, etc.{rel: 'stylesheet',href: '/styles.css'},// Narrows to PreloadLink: requires as attribute{rel: 'preload',as: 'font',href: '/font.woff2',crossorigin: 'anonymous'},],script: [// Narrows to ModuleScript{src: '/app.mjs',type: 'module'},// Narrows to JsonLdScript{type: 'application/ld+json',innerHTML: '{}'},],})

See PRs #​627, #​665, #​729.

✅ ValidatePlugin

New optional ValidatePlugin that inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, render-blocking scripts, late <meta charset>, too many fetchpriority="high" hints, preconnect without crossorigin, and more. Also includes v2 migration rules that detect deprecated property names (children, hid/vmid, body: true), missing TemplateParamsPlugin, and missing AliasSortingPlugin — all of which cause silent breakage on upgrade. Auto-injected in dev by the unified Vite plugin so warnings surface in the browser console without any manual setup. Fully tree-shakeable. Rules use ESLint-style flat config:

import{ValidatePlugin}from'unhead/plugins'createHead({plugins: [ValidatePlugin({rules: {'missing-description': 'off',}})]})

See PRs #​690, #​691, #​716, #​722, #​725, #​732.

🔗 Canonical Plugin

New built-in CanonicalPlugin that auto-generates <link rel="canonical"> tags and resolves relative URLs to absolute in og:image, twitter:image, and og:url. Includes query parameter filtering (strips tracking params like utm_source, fbclid, gclid by default), trailing slash normalization, and automatic hash fragment stripping. Essential for SEO and social sharing.

import{CanonicalPlugin}from'unhead/plugins'createHead({plugins: [CanonicalPlugin({canonicalHost: 'https://mysite.com',trailingSlash: true,queryWhitelist: ['page','sort'],})]})

See PRs #​492, #​713.

🗜️ MinifyPlugin

New optional MinifyPlugin that minifies inline <script> and <style> tag content during SSR. Uses lightweight pure-JS minifiers with zero native dependencies, safe for edge and serverless runtimes. A companion build-time transform (MinifyTransform in @unhead/bundler) pre-minifies static innerHTML literals at compile time. Standalone utilities (minifyJS, minifyCSS, minifyJSON) are also available via unhead/minify.

import{MinifyPlugin}from'unhead/plugins'createHead({plugins: [MinifyPlugin()]})

See PR #​705.

📦 Performance

Buildv3 sizev2 sizeDelta gz
client1025411513-534 (-11.2%)
server989410361-194 (-4.6%)
vueClient1132312567-533 (-10.2%)
vueServer1084911312-191 (-4.1%)
Benchmarkv2 meanv3 meanDelta
@unhead/vue0.106ms0.072ms32% faster
core0.088ms0.073ms17% faster

Key optimizations:

  • Client-only CAPO sorting (#​626)
  • Pure, tree-shakeable core with no side effects (#​632)
  • Minified internal DOM state properties (#​635)
  • Migrated unplugins from estree-walker/acorn-loose to oxc-walker (#​663)
  • Walker-based transformHtmlTemplate (#​581)
  • TemplateParamsPlugin and AliasSortingPlugin made opt-in for smaller bundles (#​493, #​494)

📊 Schema.org

  • 12 new nodes: Dataset, MusicAlbum, MusicGroup, MusicPlaylist, MusicRecording, PodcastEpisode, PodcastSeason, PodcastSeries, Service, TVEpisode, TVSeason, TVSeries (#​612)
  • Graph resolution rewrite for correctness and performance (#​616)
  • Removed ohash and defu dependencies (#​605)

🔄 Other Changes

  • renderDOMHead() / renderSSRHead() are now fully synchronous, single-pass via a composable resolveTags() pipeline; the head instance exposes a pluggable render() function for framework integrations (#​619, #​622, #​628, #​629, #​630)
  • @unhead/react/helmet drop-in compat export for users migrating from react-helmet (#​719)
  • useHeadSafe() now whitelists CSS styles (#​491)
  • Support for blocking attribute on scripts and stylesheets (#​489)
  • useScript() consolidated back into core, legacy support dropped (#​498)
  • fediverse:creator meta tag support (#​703)
  • Switched from hookable to lighter HookableCore with sync-only hooks (#​631)
  • Deprecation warnings added to aliased packages (@unhead/schema, @unhead/shared) (#​678)
  • templateParams extensible via module augmentation (#​679)
  • Respect user-provided twitter:card in InferSeoMetaPlugin (#​681)
  • Enforce as attribute for preload links (#​683)
  • onRendered callback option on useHead() for synchronizing with DOM head updates (#​712)
  • tagWeight option on createHead() to override default CAPO tag weight function (#​716)

🐛 Bug Fixes

  • Hydration race condition with deferred patches (#​634)
  • Process pending patches even when dirty is false (#​636)
  • Deduplicate matching tags inside same render cycle (#​668)
  • Dedupe <link rel="alternate"> correctly (#​655, #​656, #​658)
  • React: dispose head entries on unmount in StrictMode (#​664)
  • React: force invalidation on entry disposal (#​559)
  • Vue: support computed getter trigger (#​638)
  • Vue: expose @unhead/vue/stream/iife with correct types (#​707)
  • Scripts: prevent scope disposal from aborting unrelated trigger (#​660)
  • Schema.org: allow null to opt out of default values (#​680)
  • Schema.org: normalize target to array before merging potentialAction (#​709)
  • Avoid mutating cached titleTemplate tag in resolveTitleTemplate (#​715)

⚠️ Breaking Changes

Synchronous rendering

renderDOMHead() and renderSSRHead() no longer return promises. Remove await.

Build plugins: @unhead/addons@unhead/bundler (#​726, #​733)

The @unhead/addons package has been renamed to @unhead/bundler (the old name still works with a deprecation warning). Framework Vite plugins now use a namedUnhead export and ship from each framework's /vite subpath:

- import unhead from '@&#8203;unhead/addons/vite'+ import { Unhead } from '@&#8203;unhead/vue/vite'
// or @&#8203;unhead/react/vite, @&#8203;unhead/svelte/vite, @&#8203;unhead/solid-js/vite
export default defineConfig({
- plugins: [unhead()],+ plugins: [Unhead()],
})
Strict Link / Script / Meta types (#​729)

Link and Script unions no longer fall back to GenericLink / GenericScript, so the type system enforces per-tag constraints (e.g. preload + as: 'font' requires crossorigin). Meta content is now required; use content: null explicitly to remove a meta tag. Custom rel / type values need satisfies GenericLink / satisfies GenericScript.

Dropped deprecations (#​624)
OldNew

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • "on Monday"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@netlify

netlifyBot commented Apr 13, 2026

Copy link
Copy Markdown

Deploy Preview for friendly-lamington-fb5690 ready!

NameLink
🔨 Latest commit520747b
🔍 Latest deploy loghttps://app.netlify.com/projects/friendly-lamington-fb5690/deploys/6a95463d08572200086d390f
😎 Deploy Previewhttps://deploy-preview-945--friendly-lamington-fb5690.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changesRun an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 9 times, most recently from 1c6f416 to 1fde2a7CompareApril 20, 2026 05:58
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 68dc7c3 to 67ba4f7CompareApril 28, 2026 17:52
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 7 times, most recently from a176ffc to 443f048CompareMay 7, 2026 06:49
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b6aec16 to 56c374bCompareMay 11, 2026 07:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 48be561 to 8c00a0cCompareMay 28, 2026 13:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 8 times, most recently from 6e9d9a7 to 55ba0bcCompareJune 8, 2026 21:26
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 12 times, most recently from 23bb71d to 8006a92CompareJune 16, 2026 08:22
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b643394 to 795393fCompareJune 25, 2026 03:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

chore(deps): update unhead monorepo to v3 - #945

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo
Open

chore(deps): update unhead monorepo to v3#945
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo

Conversation

@renovate

@renovaterenovateBot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

PackageChangeAgeConfidence
@unhead/vue (source)^2.0.14^3.0.0ageconfidence
unhead (source)2.1.173.4.0ageconfidence

Release Notes

unjs/unhead (@​unhead/vue)

v3.4.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.3.0

Compare Source

Projects using the Unhead Bundler with Vite 6 or 7, webpack, Rspack, Rollup, or another build without Rolldown no longer receive oxc-parser from @unhead/bundler. Builds that use Unhead transforms must install it directly.

📝 Migration
pnpm add -D oxc-parser

Runtime-only Unhead projects and builds with Rolldown installed need no change.

🚨 Breaking Changes
🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.8

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.7

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.6

Compare Source

🏎 Performance
View changes on GitHub

v3.1.5

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.4

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.0

Compare Source

🛠️ Unhead CLI

To assist with migrations and overall DX a CLI has been introduced: @unhead/cli.

npx -y @unhead/cli 

It lets you do the following:

 audit Lint your codebase for unhead misuse, type-narrowing issues, and SEO/perf foot-guns. migrate Apply autofixes forv2-to-v3 migration: rewrite deprecated props and wrap tag literalsin defineX helpers.
validate-html Run the runtime ValidatePlugin over prerendered HTML files (e.g. dist/, .output/, build/). validate-url Fetch a rendered URL and run unhead\'s SEO/perf validation rules over its <head>. 

For example, try running audit on your own project for hints on how to improve your SEO.

✔️ Unhead ESLint

Knowing that your useHead() and useSeoMeta() code is right while your coding is important. While type-narrowing solves many broken cases, we introduce an ESLint plugin to help catch anything that the typechecker can't catch.

These rules are shared from the runtime ValidatePlugin

# flat-config ESLint plugin with v2→v3 migration autofixes
npm i -D @unhead/eslint-plugin
```ts[eslint.config.ts]import{configs}from'@&#8203;unhead/eslint-plugin'exportdefault[configs.recommended,]

🌊 Streaming SSR non-Vite support

The streaming plugin lived only at unhead/stream/vite previously, leaving non-Vite users with no way to wire the bootstrap. The plugin is now a bundler-agnostic unplugin factory with first-class webpack and Vite entries, and the framework packages compose it behind Unhead({ streaming: true }).

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'exportdefault{plugins: [vue(),Unhead({streaming: true})]}// webpack.config.tsimport{Unhead}from'@&#8203;unhead/vue/bundler'exportdefault{plugins: [...Unhead({streaming: true}).webpack()]}

Streaming also gains a nonce option (forwarded on every injected <script> for CSP support), a fixed async mode for production Vite builds (the IIFE is now emitted via this.emitFile() so the script src references a real hashed asset), a dev-mode warning when the client IIFE runs against an empty server queue, and a shared StreamingGlobal type so the server bootstrap, client, and injected IIFE agree on the shape of window.__unhead__. Default mode changed from async to inline for smaller TTFB.

Changelog

🚀 Features
🐞 Bug Fixes
View changes on GitHub

v3.0.5

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.4

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.0

Compare Source

Unhead v3 rebuilds the rendering engine from the ground up. The motivation: streaming SSR. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager.

📣 Highlights

🌊 Streaming SSR

Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new <title>, <meta>, and <link> tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load.

// entry-server.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/server'const{ head, wrapStream }=createStreamableHead()app.use(head)// wraps the Vue stream, injecting head updates as chunks resolvereturnwrapStream(renderToWebStream(app),template)
// entry-client.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/client'consthead=createStreamableHead()app.use(head)

Under the hood: a queue stub (window.__unhead__) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing.

Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See PR #​537.

🛠️ Unified Vite Plugin + DevTools

A single @unhead/{framework}/vite plugin replaces the old manual composition of @unhead/addons + streaming plugin + framework glue. One import, one call, and you get tree-shaking, useSeoMetauseHead transform, inline minification, streaming SSR, dev-mode ValidatePlugin auto-injection, and Vite DevTools integration.

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'importvuefrom'@&#8203;vitejs/plugin-vue'exportdefaultdefineConfig({plugins: [vue(),Unhead()],})

The DevTools panel surfaces live head state during development: every useHead() / useSeoMeta() call with its source file and line number, resolved tags, SEO overview (title, description, canonical, Open Graph), useScript() load status, active plugins, template params, and warnings from the Validate plugin. Source tracing lets you click through from any tag back to the exact line that created it.

Available for Vue, React, Svelte, Solid, and vanilla via @unhead/bundler/vite (the renamed @unhead/addons package; the old name still works with a deprecation warning).

See PRs #​726, #​733, #​731.

🎯 useHead() Type Narrowing

useHead() now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong.

useHead({link: [// Narrows to StylesheetLink: requires href, offers media, integrity, etc.{rel: 'stylesheet',href: '/styles.css'},// Narrows to PreloadLink: requires as attribute{rel: 'preload',as: 'font',href: '/font.woff2',crossorigin: 'anonymous'},],script: [// Narrows to ModuleScript{src: '/app.mjs',type: 'module'},// Narrows to JsonLdScript{type: 'application/ld+json',innerHTML: '{}'},],})

See PRs #​627, #​665, #​729.

✅ ValidatePlugin

New optional ValidatePlugin that inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, render-blocking scripts, late <meta charset>, too many fetchpriority="high" hints, preconnect without crossorigin, and more. Also includes v2 migration rules that detect deprecated property names (children, hid/vmid, body: true), missing TemplateParamsPlugin, and missing AliasSortingPlugin — all of which cause silent breakage on upgrade. Auto-injected in dev by the unified Vite plugin so warnings surface in the browser console without any manual setup. Fully tree-shakeable. Rules use ESLint-style flat config:

import{ValidatePlugin}from'unhead/plugins'createHead({plugins: [ValidatePlugin({rules: {'missing-description': 'off',}})]})

See PRs #​690, #​691, #​716, #​722, #​725, #​732.

🔗 Canonical Plugin

New built-in CanonicalPlugin that auto-generates <link rel="canonical"> tags and resolves relative URLs to absolute in og:image, twitter:image, and og:url. Includes query parameter filtering (strips tracking params like utm_source, fbclid, gclid by default), trailing slash normalization, and automatic hash fragment stripping. Essential for SEO and social sharing.

import{CanonicalPlugin}from'unhead/plugins'createHead({plugins: [CanonicalPlugin({canonicalHost: 'https://mysite.com',trailingSlash: true,queryWhitelist: ['page','sort'],})]})

See PRs #​492, #​713.

🗜️ MinifyPlugin

New optional MinifyPlugin that minifies inline <script> and <style> tag content during SSR. Uses lightweight pure-JS minifiers with zero native dependencies, safe for edge and serverless runtimes. A companion build-time transform (MinifyTransform in @unhead/bundler) pre-minifies static innerHTML literals at compile time. Standalone utilities (minifyJS, minifyCSS, minifyJSON) are also available via unhead/minify.

import{MinifyPlugin}from'unhead/plugins'createHead({plugins: [MinifyPlugin()]})

See PR #​705.

📦 Performance

Buildv3 sizev2 sizeDelta gz
client1025411513-534 (-11.2%)
server989410361-194 (-4.6%)
vueClient1132312567-533 (-10.2%)
vueServer1084911312-191 (-4.1%)
Benchmarkv2 meanv3 meanDelta
@unhead/vue0.106ms0.072ms32% faster
core0.088ms0.073ms17% faster

Key optimizations:

  • Client-only CAPO sorting (#​626)
  • Pure, tree-shakeable core with no side effects (#​632)
  • Minified internal DOM state properties (#​635)
  • Migrated unplugins from estree-walker/acorn-loose to oxc-walker (#​663)
  • Walker-based transformHtmlTemplate (#​581)
  • TemplateParamsPlugin and AliasSortingPlugin made opt-in for smaller bundles (#​493, #​494)

📊 Schema.org

  • 12 new nodes: Dataset, MusicAlbum, MusicGroup, MusicPlaylist, MusicRecording, PodcastEpisode, PodcastSeason, PodcastSeries, Service, TVEpisode, TVSeason, TVSeries (#​612)
  • Graph resolution rewrite for correctness and performance (#​616)
  • Removed ohash and defu dependencies (#​605)

🔄 Other Changes

  • renderDOMHead() / renderSSRHead() are now fully synchronous, single-pass via a composable resolveTags() pipeline; the head instance exposes a pluggable render() function for framework integrations (#​619, #​622, #​628, #​629, #​630)
  • @unhead/react/helmet drop-in compat export for users migrating from react-helmet (#​719)
  • useHeadSafe() now whitelists CSS styles (#​491)
  • Support for blocking attribute on scripts and stylesheets (#​489)
  • useScript() consolidated back into core, legacy support dropped (#​498)
  • fediverse:creator meta tag support (#​703)
  • Switched from hookable to lighter HookableCore with sync-only hooks (#​631)
  • Deprecation warnings added to aliased packages (@unhead/schema, @unhead/shared) (#​678)
  • templateParams extensible via module augmentation (#​679)
  • Respect user-provided twitter:card in InferSeoMetaPlugin (#​681)
  • Enforce as attribute for preload links (#​683)
  • onRendered callback option on useHead() for synchronizing with DOM head updates (#​712)
  • tagWeight option on createHead() to override default CAPO tag weight function (#​716)

🐛 Bug Fixes

  • Hydration race condition with deferred patches (#​634)
  • Process pending patches even when dirty is false (#​636)
  • Deduplicate matching tags inside same render cycle (#​668)
  • Dedupe <link rel="alternate"> correctly (#​655, #​656, #​658)
  • React: dispose head entries on unmount in StrictMode (#​664)
  • React: force invalidation on entry disposal (#​559)
  • Vue: support computed getter trigger (#​638)
  • Vue: expose @unhead/vue/stream/iife with correct types (#​707)
  • Scripts: prevent scope disposal from aborting unrelated trigger (#​660)
  • Schema.org: allow null to opt out of default values (#​680)
  • Schema.org: normalize target to array before merging potentialAction (#​709)
  • Avoid mutating cached titleTemplate tag in resolveTitleTemplate (#​715)

⚠️ Breaking Changes

Synchronous rendering

renderDOMHead() and renderSSRHead() no longer return promises. Remove await.

Build plugins: @unhead/addons@unhead/bundler (#​726, #​733)

The @unhead/addons package has been renamed to @unhead/bundler (the old name still works with a deprecation warning). Framework Vite plugins now use a namedUnhead export and ship from each framework's /vite subpath:

- import unhead from '@&#8203;unhead/addons/vite'+ import { Unhead } from '@&#8203;unhead/vue/vite'
// or @&#8203;unhead/react/vite, @&#8203;unhead/svelte/vite, @&#8203;unhead/solid-js/vite
export default defineConfig({
- plugins: [unhead()],+ plugins: [Unhead()],
})
Strict Link / Script / Meta types (#​729)

Link and Script unions no longer fall back to GenericLink / GenericScript, so the type system enforces per-tag constraints (e.g. preload + as: 'font' requires crossorigin). Meta content is now required; use content: null explicitly to remove a meta tag. Custom rel / type values need satisfies GenericLink / satisfies GenericScript.

Dropped deprecations (#​624)
OldNew

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • "on Monday"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@netlify

netlifyBot commented Apr 13, 2026

Copy link
Copy Markdown

Deploy Preview for friendly-lamington-fb5690 ready!

NameLink
🔨 Latest commit520747b
🔍 Latest deploy loghttps://app.netlify.com/projects/friendly-lamington-fb5690/deploys/6a95463d08572200086d390f
😎 Deploy Previewhttps://deploy-preview-945--friendly-lamington-fb5690.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changesRun an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 9 times, most recently from 1c6f416 to 1fde2a7CompareApril 20, 2026 05:58
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 68dc7c3 to 67ba4f7CompareApril 28, 2026 17:52
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 7 times, most recently from a176ffc to 443f048CompareMay 7, 2026 06:49
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b6aec16 to 56c374bCompareMay 11, 2026 07:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 48be561 to 8c00a0cCompareMay 28, 2026 13:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 8 times, most recently from 6e9d9a7 to 55ba0bcCompareJune 8, 2026 21:26
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 12 times, most recently from 23bb71d to 8006a92CompareJune 16, 2026 08:22
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b643394 to 795393fCompareJune 25, 2026 03:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(deps): update unhead monorepo to v3 - #945

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo
Open

chore(deps): update unhead monorepo to v3#945
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo

Conversation

@renovate

@renovaterenovateBot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

PackageChangeAgeConfidence
@unhead/vue (source)^2.0.14^3.0.0ageconfidence
unhead (source)2.1.173.4.0ageconfidence

Release Notes

unjs/unhead (@​unhead/vue)

v3.4.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.3.0

Compare Source

Projects using the Unhead Bundler with Vite 6 or 7, webpack, Rspack, Rollup, or another build without Rolldown no longer receive oxc-parser from @unhead/bundler. Builds that use Unhead transforms must install it directly.

📝 Migration
pnpm add -D oxc-parser

Runtime-only Unhead projects and builds with Rolldown installed need no change.

🚨 Breaking Changes
🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.8

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.7

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.6

Compare Source

🏎 Performance
View changes on GitHub

v3.1.5

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.4

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.0

Compare Source

🛠️ Unhead CLI

To assist with migrations and overall DX a CLI has been introduced: @unhead/cli.

npx -y @unhead/cli 

It lets you do the following:

 audit Lint your codebase for unhead misuse, type-narrowing issues, and SEO/perf foot-guns. migrate Apply autofixes forv2-to-v3 migration: rewrite deprecated props and wrap tag literalsin defineX helpers.
validate-html Run the runtime ValidatePlugin over prerendered HTML files (e.g. dist/, .output/, build/). validate-url Fetch a rendered URL and run unhead\'s SEO/perf validation rules over its <head>. 

For example, try running audit on your own project for hints on how to improve your SEO.

✔️ Unhead ESLint

Knowing that your useHead() and useSeoMeta() code is right while your coding is important. While type-narrowing solves many broken cases, we introduce an ESLint plugin to help catch anything that the typechecker can't catch.

These rules are shared from the runtime ValidatePlugin

# flat-config ESLint plugin with v2→v3 migration autofixes
npm i -D @unhead/eslint-plugin
```ts[eslint.config.ts]import{configs}from'@&#8203;unhead/eslint-plugin'exportdefault[configs.recommended,]

🌊 Streaming SSR non-Vite support

The streaming plugin lived only at unhead/stream/vite previously, leaving non-Vite users with no way to wire the bootstrap. The plugin is now a bundler-agnostic unplugin factory with first-class webpack and Vite entries, and the framework packages compose it behind Unhead({ streaming: true }).

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'exportdefault{plugins: [vue(),Unhead({streaming: true})]}// webpack.config.tsimport{Unhead}from'@&#8203;unhead/vue/bundler'exportdefault{plugins: [...Unhead({streaming: true}).webpack()]}

Streaming also gains a nonce option (forwarded on every injected <script> for CSP support), a fixed async mode for production Vite builds (the IIFE is now emitted via this.emitFile() so the script src references a real hashed asset), a dev-mode warning when the client IIFE runs against an empty server queue, and a shared StreamingGlobal type so the server bootstrap, client, and injected IIFE agree on the shape of window.__unhead__. Default mode changed from async to inline for smaller TTFB.

Changelog

🚀 Features
🐞 Bug Fixes
View changes on GitHub

v3.0.5

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.4

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.0

Compare Source

Unhead v3 rebuilds the rendering engine from the ground up. The motivation: streaming SSR. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager.

📣 Highlights

🌊 Streaming SSR

Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new <title>, <meta>, and <link> tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load.

// entry-server.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/server'const{ head, wrapStream }=createStreamableHead()app.use(head)// wraps the Vue stream, injecting head updates as chunks resolvereturnwrapStream(renderToWebStream(app),template)
// entry-client.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/client'consthead=createStreamableHead()app.use(head)

Under the hood: a queue stub (window.__unhead__) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing.

Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See PR #​537.

🛠️ Unified Vite Plugin + DevTools

A single @unhead/{framework}/vite plugin replaces the old manual composition of @unhead/addons + streaming plugin + framework glue. One import, one call, and you get tree-shaking, useSeoMetauseHead transform, inline minification, streaming SSR, dev-mode ValidatePlugin auto-injection, and Vite DevTools integration.

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'importvuefrom'@&#8203;vitejs/plugin-vue'exportdefaultdefineConfig({plugins: [vue(),Unhead()],})

The DevTools panel surfaces live head state during development: every useHead() / useSeoMeta() call with its source file and line number, resolved tags, SEO overview (title, description, canonical, Open Graph), useScript() load status, active plugins, template params, and warnings from the Validate plugin. Source tracing lets you click through from any tag back to the exact line that created it.

Available for Vue, React, Svelte, Solid, and vanilla via @unhead/bundler/vite (the renamed @unhead/addons package; the old name still works with a deprecation warning).

See PRs #​726, #​733, #​731.

🎯 useHead() Type Narrowing

useHead() now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong.

useHead({link: [// Narrows to StylesheetLink: requires href, offers media, integrity, etc.{rel: 'stylesheet',href: '/styles.css'},// Narrows to PreloadLink: requires as attribute{rel: 'preload',as: 'font',href: '/font.woff2',crossorigin: 'anonymous'},],script: [// Narrows to ModuleScript{src: '/app.mjs',type: 'module'},// Narrows to JsonLdScript{type: 'application/ld+json',innerHTML: '{}'},],})

See PRs #​627, #​665, #​729.

✅ ValidatePlugin

New optional ValidatePlugin that inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, render-blocking scripts, late <meta charset>, too many fetchpriority="high" hints, preconnect without crossorigin, and more. Also includes v2 migration rules that detect deprecated property names (children, hid/vmid, body: true), missing TemplateParamsPlugin, and missing AliasSortingPlugin — all of which cause silent breakage on upgrade. Auto-injected in dev by the unified Vite plugin so warnings surface in the browser console without any manual setup. Fully tree-shakeable. Rules use ESLint-style flat config:

import{ValidatePlugin}from'unhead/plugins'createHead({plugins: [ValidatePlugin({rules: {'missing-description': 'off',}})]})

See PRs #​690, #​691, #​716, #​722, #​725, #​732.

🔗 Canonical Plugin

New built-in CanonicalPlugin that auto-generates <link rel="canonical"> tags and resolves relative URLs to absolute in og:image, twitter:image, and og:url. Includes query parameter filtering (strips tracking params like utm_source, fbclid, gclid by default), trailing slash normalization, and automatic hash fragment stripping. Essential for SEO and social sharing.

import{CanonicalPlugin}from'unhead/plugins'createHead({plugins: [CanonicalPlugin({canonicalHost: 'https://mysite.com',trailingSlash: true,queryWhitelist: ['page','sort'],})]})

See PRs #​492, #​713.

🗜️ MinifyPlugin

New optional MinifyPlugin that minifies inline <script> and <style> tag content during SSR. Uses lightweight pure-JS minifiers with zero native dependencies, safe for edge and serverless runtimes. A companion build-time transform (MinifyTransform in @unhead/bundler) pre-minifies static innerHTML literals at compile time. Standalone utilities (minifyJS, minifyCSS, minifyJSON) are also available via unhead/minify.

import{MinifyPlugin}from'unhead/plugins'createHead({plugins: [MinifyPlugin()]})

See PR #​705.

📦 Performance

Buildv3 sizev2 sizeDelta gz
client1025411513-534 (-11.2%)
server989410361-194 (-4.6%)
vueClient1132312567-533 (-10.2%)
vueServer1084911312-191 (-4.1%)
Benchmarkv2 meanv3 meanDelta
@unhead/vue0.106ms0.072ms32% faster
core0.088ms0.073ms17% faster

Key optimizations:

  • Client-only CAPO sorting (#​626)
  • Pure, tree-shakeable core with no side effects (#​632)
  • Minified internal DOM state properties (#​635)
  • Migrated unplugins from estree-walker/acorn-loose to oxc-walker (#​663)
  • Walker-based transformHtmlTemplate (#​581)
  • TemplateParamsPlugin and AliasSortingPlugin made opt-in for smaller bundles (#​493, #​494)

📊 Schema.org

  • 12 new nodes: Dataset, MusicAlbum, MusicGroup, MusicPlaylist, MusicRecording, PodcastEpisode, PodcastSeason, PodcastSeries, Service, TVEpisode, TVSeason, TVSeries (#​612)
  • Graph resolution rewrite for correctness and performance (#​616)
  • Removed ohash and defu dependencies (#​605)

🔄 Other Changes

  • renderDOMHead() / renderSSRHead() are now fully synchronous, single-pass via a composable resolveTags() pipeline; the head instance exposes a pluggable render() function for framework integrations (#​619, #​622, #​628, #​629, #​630)
  • @unhead/react/helmet drop-in compat export for users migrating from react-helmet (#​719)
  • useHeadSafe() now whitelists CSS styles (#​491)
  • Support for blocking attribute on scripts and stylesheets (#​489)
  • useScript() consolidated back into core, legacy support dropped (#​498)
  • fediverse:creator meta tag support (#​703)
  • Switched from hookable to lighter HookableCore with sync-only hooks (#​631)
  • Deprecation warnings added to aliased packages (@unhead/schema, @unhead/shared) (#​678)
  • templateParams extensible via module augmentation (#​679)
  • Respect user-provided twitter:card in InferSeoMetaPlugin (#​681)
  • Enforce as attribute for preload links (#​683)
  • onRendered callback option on useHead() for synchronizing with DOM head updates (#​712)
  • tagWeight option on createHead() to override default CAPO tag weight function (#​716)

🐛 Bug Fixes

  • Hydration race condition with deferred patches (#​634)
  • Process pending patches even when dirty is false (#​636)
  • Deduplicate matching tags inside same render cycle (#​668)
  • Dedupe <link rel="alternate"> correctly (#​655, #​656, #​658)
  • React: dispose head entries on unmount in StrictMode (#​664)
  • React: force invalidation on entry disposal (#​559)
  • Vue: support computed getter trigger (#​638)
  • Vue: expose @unhead/vue/stream/iife with correct types (#​707)
  • Scripts: prevent scope disposal from aborting unrelated trigger (#​660)
  • Schema.org: allow null to opt out of default values (#​680)
  • Schema.org: normalize target to array before merging potentialAction (#​709)
  • Avoid mutating cached titleTemplate tag in resolveTitleTemplate (#​715)

⚠️ Breaking Changes

Synchronous rendering

renderDOMHead() and renderSSRHead() no longer return promises. Remove await.

Build plugins: @unhead/addons@unhead/bundler (#​726, #​733)

The @unhead/addons package has been renamed to @unhead/bundler (the old name still works with a deprecation warning). Framework Vite plugins now use a namedUnhead export and ship from each framework's /vite subpath:

- import unhead from '@&#8203;unhead/addons/vite'+ import { Unhead } from '@&#8203;unhead/vue/vite'
// or @&#8203;unhead/react/vite, @&#8203;unhead/svelte/vite, @&#8203;unhead/solid-js/vite
export default defineConfig({
- plugins: [unhead()],+ plugins: [Unhead()],
})
Strict Link / Script / Meta types (#​729)

Link and Script unions no longer fall back to GenericLink / GenericScript, so the type system enforces per-tag constraints (e.g. preload + as: 'font' requires crossorigin). Meta content is now required; use content: null explicitly to remove a meta tag. Custom rel / type values need satisfies GenericLink / satisfies GenericScript.

Dropped deprecations (#​624)
OldNew

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • "on Monday"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@netlify

netlifyBot commented Apr 13, 2026

Copy link
Copy Markdown

Deploy Preview for friendly-lamington-fb5690 ready!

NameLink
🔨 Latest commit520747b
🔍 Latest deploy loghttps://app.netlify.com/projects/friendly-lamington-fb5690/deploys/6a95463d08572200086d390f
😎 Deploy Previewhttps://deploy-preview-945--friendly-lamington-fb5690.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changesRun an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 9 times, most recently from 1c6f416 to 1fde2a7CompareApril 20, 2026 05:58
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 68dc7c3 to 67ba4f7CompareApril 28, 2026 17:52
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 7 times, most recently from a176ffc to 443f048CompareMay 7, 2026 06:49
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b6aec16 to 56c374bCompareMay 11, 2026 07:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 48be561 to 8c00a0cCompareMay 28, 2026 13:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 8 times, most recently from 6e9d9a7 to 55ba0bcCompareJune 8, 2026 21:26
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 12 times, most recently from 23bb71d to 8006a92CompareJune 16, 2026 08:22
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b643394 to 795393fCompareJune 25, 2026 03:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(deps): update unhead monorepo to v3 - #945

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo
Open

chore(deps): update unhead monorepo to v3#945
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo

Conversation

@renovate

@renovaterenovateBot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

PackageChangeAgeConfidence
@unhead/vue (source)^2.0.14^3.0.0ageconfidence
unhead (source)2.1.173.4.0ageconfidence

Release Notes

unjs/unhead (@​unhead/vue)

v3.4.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.3.0

Compare Source

Projects using the Unhead Bundler with Vite 6 or 7, webpack, Rspack, Rollup, or another build without Rolldown no longer receive oxc-parser from @unhead/bundler. Builds that use Unhead transforms must install it directly.

📝 Migration
pnpm add -D oxc-parser

Runtime-only Unhead projects and builds with Rolldown installed need no change.

🚨 Breaking Changes
🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.8

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.7

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.6

Compare Source

🏎 Performance
View changes on GitHub

v3.1.5

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.4

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.0

Compare Source

🛠️ Unhead CLI

To assist with migrations and overall DX a CLI has been introduced: @unhead/cli.

npx -y @unhead/cli 

It lets you do the following:

 audit Lint your codebase for unhead misuse, type-narrowing issues, and SEO/perf foot-guns. migrate Apply autofixes forv2-to-v3 migration: rewrite deprecated props and wrap tag literalsin defineX helpers.
validate-html Run the runtime ValidatePlugin over prerendered HTML files (e.g. dist/, .output/, build/). validate-url Fetch a rendered URL and run unhead\'s SEO/perf validation rules over its <head>. 

For example, try running audit on your own project for hints on how to improve your SEO.

✔️ Unhead ESLint

Knowing that your useHead() and useSeoMeta() code is right while your coding is important. While type-narrowing solves many broken cases, we introduce an ESLint plugin to help catch anything that the typechecker can't catch.

These rules are shared from the runtime ValidatePlugin

# flat-config ESLint plugin with v2→v3 migration autofixes
npm i -D @unhead/eslint-plugin
```ts[eslint.config.ts]import{configs}from'@&#8203;unhead/eslint-plugin'exportdefault[configs.recommended,]

🌊 Streaming SSR non-Vite support

The streaming plugin lived only at unhead/stream/vite previously, leaving non-Vite users with no way to wire the bootstrap. The plugin is now a bundler-agnostic unplugin factory with first-class webpack and Vite entries, and the framework packages compose it behind Unhead({ streaming: true }).

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'exportdefault{plugins: [vue(),Unhead({streaming: true})]}// webpack.config.tsimport{Unhead}from'@&#8203;unhead/vue/bundler'exportdefault{plugins: [...Unhead({streaming: true}).webpack()]}

Streaming also gains a nonce option (forwarded on every injected <script> for CSP support), a fixed async mode for production Vite builds (the IIFE is now emitted via this.emitFile() so the script src references a real hashed asset), a dev-mode warning when the client IIFE runs against an empty server queue, and a shared StreamingGlobal type so the server bootstrap, client, and injected IIFE agree on the shape of window.__unhead__. Default mode changed from async to inline for smaller TTFB.

Changelog

🚀 Features
🐞 Bug Fixes
View changes on GitHub

v3.0.5

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.4

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.0

Compare Source

Unhead v3 rebuilds the rendering engine from the ground up. The motivation: streaming SSR. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager.

📣 Highlights

🌊 Streaming SSR

Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new <title>, <meta>, and <link> tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load.

// entry-server.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/server'const{ head, wrapStream }=createStreamableHead()app.use(head)// wraps the Vue stream, injecting head updates as chunks resolvereturnwrapStream(renderToWebStream(app),template)
// entry-client.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/client'consthead=createStreamableHead()app.use(head)

Under the hood: a queue stub (window.__unhead__) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing.

Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See PR #​537.

🛠️ Unified Vite Plugin + DevTools

A single @unhead/{framework}/vite plugin replaces the old manual composition of @unhead/addons + streaming plugin + framework glue. One import, one call, and you get tree-shaking, useSeoMetauseHead transform, inline minification, streaming SSR, dev-mode ValidatePlugin auto-injection, and Vite DevTools integration.

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'importvuefrom'@&#8203;vitejs/plugin-vue'exportdefaultdefineConfig({plugins: [vue(),Unhead()],})

The DevTools panel surfaces live head state during development: every useHead() / useSeoMeta() call with its source file and line number, resolved tags, SEO overview (title, description, canonical, Open Graph), useScript() load status, active plugins, template params, and warnings from the Validate plugin. Source tracing lets you click through from any tag back to the exact line that created it.

Available for Vue, React, Svelte, Solid, and vanilla via @unhead/bundler/vite (the renamed @unhead/addons package; the old name still works with a deprecation warning).

See PRs #​726, #​733, #​731.

🎯 useHead() Type Narrowing

useHead() now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong.

useHead({link: [// Narrows to StylesheetLink: requires href, offers media, integrity, etc.{rel: 'stylesheet',href: '/styles.css'},// Narrows to PreloadLink: requires as attribute{rel: 'preload',as: 'font',href: '/font.woff2',crossorigin: 'anonymous'},],script: [// Narrows to ModuleScript{src: '/app.mjs',type: 'module'},// Narrows to JsonLdScript{type: 'application/ld+json',innerHTML: '{}'},],})

See PRs #​627, #​665, #​729.

✅ ValidatePlugin

New optional ValidatePlugin that inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, render-blocking scripts, late <meta charset>, too many fetchpriority="high" hints, preconnect without crossorigin, and more. Also includes v2 migration rules that detect deprecated property names (children, hid/vmid, body: true), missing TemplateParamsPlugin, and missing AliasSortingPlugin — all of which cause silent breakage on upgrade. Auto-injected in dev by the unified Vite plugin so warnings surface in the browser console without any manual setup. Fully tree-shakeable. Rules use ESLint-style flat config:

import{ValidatePlugin}from'unhead/plugins'createHead({plugins: [ValidatePlugin({rules: {'missing-description': 'off',}})]})

See PRs #​690, #​691, #​716, #​722, #​725, #​732.

🔗 Canonical Plugin

New built-in CanonicalPlugin that auto-generates <link rel="canonical"> tags and resolves relative URLs to absolute in og:image, twitter:image, and og:url. Includes query parameter filtering (strips tracking params like utm_source, fbclid, gclid by default), trailing slash normalization, and automatic hash fragment stripping. Essential for SEO and social sharing.

import{CanonicalPlugin}from'unhead/plugins'createHead({plugins: [CanonicalPlugin({canonicalHost: 'https://mysite.com',trailingSlash: true,queryWhitelist: ['page','sort'],})]})

See PRs #​492, #​713.

🗜️ MinifyPlugin

New optional MinifyPlugin that minifies inline <script> and <style> tag content during SSR. Uses lightweight pure-JS minifiers with zero native dependencies, safe for edge and serverless runtimes. A companion build-time transform (MinifyTransform in @unhead/bundler) pre-minifies static innerHTML literals at compile time. Standalone utilities (minifyJS, minifyCSS, minifyJSON) are also available via unhead/minify.

import{MinifyPlugin}from'unhead/plugins'createHead({plugins: [MinifyPlugin()]})

See PR #​705.

📦 Performance

Buildv3 sizev2 sizeDelta gz
client1025411513-534 (-11.2%)
server989410361-194 (-4.6%)
vueClient1132312567-533 (-10.2%)
vueServer1084911312-191 (-4.1%)
Benchmarkv2 meanv3 meanDelta
@unhead/vue0.106ms0.072ms32% faster
core0.088ms0.073ms17% faster

Key optimizations:

  • Client-only CAPO sorting (#​626)
  • Pure, tree-shakeable core with no side effects (#​632)
  • Minified internal DOM state properties (#​635)
  • Migrated unplugins from estree-walker/acorn-loose to oxc-walker (#​663)
  • Walker-based transformHtmlTemplate (#​581)
  • TemplateParamsPlugin and AliasSortingPlugin made opt-in for smaller bundles (#​493, #​494)

📊 Schema.org

  • 12 new nodes: Dataset, MusicAlbum, MusicGroup, MusicPlaylist, MusicRecording, PodcastEpisode, PodcastSeason, PodcastSeries, Service, TVEpisode, TVSeason, TVSeries (#​612)
  • Graph resolution rewrite for correctness and performance (#​616)
  • Removed ohash and defu dependencies (#​605)

🔄 Other Changes

  • renderDOMHead() / renderSSRHead() are now fully synchronous, single-pass via a composable resolveTags() pipeline; the head instance exposes a pluggable render() function for framework integrations (#​619, #​622, #​628, #​629, #​630)
  • @unhead/react/helmet drop-in compat export for users migrating from react-helmet (#​719)
  • useHeadSafe() now whitelists CSS styles (#​491)
  • Support for blocking attribute on scripts and stylesheets (#​489)
  • useScript() consolidated back into core, legacy support dropped (#​498)
  • fediverse:creator meta tag support (#​703)
  • Switched from hookable to lighter HookableCore with sync-only hooks (#​631)
  • Deprecation warnings added to aliased packages (@unhead/schema, @unhead/shared) (#​678)
  • templateParams extensible via module augmentation (#​679)
  • Respect user-provided twitter:card in InferSeoMetaPlugin (#​681)
  • Enforce as attribute for preload links (#​683)
  • onRendered callback option on useHead() for synchronizing with DOM head updates (#​712)
  • tagWeight option on createHead() to override default CAPO tag weight function (#​716)

🐛 Bug Fixes

  • Hydration race condition with deferred patches (#​634)
  • Process pending patches even when dirty is false (#​636)
  • Deduplicate matching tags inside same render cycle (#​668)
  • Dedupe <link rel="alternate"> correctly (#​655, #​656, #​658)
  • React: dispose head entries on unmount in StrictMode (#​664)
  • React: force invalidation on entry disposal (#​559)
  • Vue: support computed getter trigger (#​638)
  • Vue: expose @unhead/vue/stream/iife with correct types (#​707)
  • Scripts: prevent scope disposal from aborting unrelated trigger (#​660)
  • Schema.org: allow null to opt out of default values (#​680)
  • Schema.org: normalize target to array before merging potentialAction (#​709)
  • Avoid mutating cached titleTemplate tag in resolveTitleTemplate (#​715)

⚠️ Breaking Changes

Synchronous rendering

renderDOMHead() and renderSSRHead() no longer return promises. Remove await.

Build plugins: @unhead/addons@unhead/bundler (#​726, #​733)

The @unhead/addons package has been renamed to @unhead/bundler (the old name still works with a deprecation warning). Framework Vite plugins now use a namedUnhead export and ship from each framework's /vite subpath:

- import unhead from '@&#8203;unhead/addons/vite'+ import { Unhead } from '@&#8203;unhead/vue/vite'
// or @&#8203;unhead/react/vite, @&#8203;unhead/svelte/vite, @&#8203;unhead/solid-js/vite
export default defineConfig({
- plugins: [unhead()],+ plugins: [Unhead()],
})
Strict Link / Script / Meta types (#​729)

Link and Script unions no longer fall back to GenericLink / GenericScript, so the type system enforces per-tag constraints (e.g. preload + as: 'font' requires crossorigin). Meta content is now required; use content: null explicitly to remove a meta tag. Custom rel / type values need satisfies GenericLink / satisfies GenericScript.

Dropped deprecations (#​624)
OldNew

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • "on Monday"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@netlify

netlifyBot commented Apr 13, 2026

Copy link
Copy Markdown

Deploy Preview for friendly-lamington-fb5690 ready!

NameLink
🔨 Latest commit520747b
🔍 Latest deploy loghttps://app.netlify.com/projects/friendly-lamington-fb5690/deploys/6a95463d08572200086d390f
😎 Deploy Previewhttps://deploy-preview-945--friendly-lamington-fb5690.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changesRun an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 9 times, most recently from 1c6f416 to 1fde2a7CompareApril 20, 2026 05:58
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 68dc7c3 to 67ba4f7CompareApril 28, 2026 17:52
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 7 times, most recently from a176ffc to 443f048CompareMay 7, 2026 06:49
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b6aec16 to 56c374bCompareMay 11, 2026 07:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 48be561 to 8c00a0cCompareMay 28, 2026 13:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 8 times, most recently from 6e9d9a7 to 55ba0bcCompareJune 8, 2026 21:26
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 12 times, most recently from 23bb71d to 8006a92CompareJune 16, 2026 08:22
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b643394 to 795393fCompareJune 25, 2026 03:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

chore(deps): update unhead monorepo to v3 - #945

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo
Open

chore(deps): update unhead monorepo to v3#945
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/major-unhead-monorepo

Conversation

@renovate

@renovaterenovateBot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

PackageChangeAgeConfidence
@unhead/vue (source)^2.0.14^3.0.0ageconfidence
unhead (source)2.1.173.4.0ageconfidence

Release Notes

unjs/unhead (@​unhead/vue)

v3.4.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.3.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.3.0

Compare Source

Projects using the Unhead Bundler with Vite 6 or 7, webpack, Rspack, Rollup, or another build without Rolldown no longer receive oxc-parser from @unhead/bundler. Builds that use Unhead transforms must install it directly.

📝 Migration
pnpm add -D oxc-parser

Runtime-only Unhead projects and builds with Rolldown installed need no change.

🚨 Breaking Changes
🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.2

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.2.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.2.0

Compare Source

🚀 Features
🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.8

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.7

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.6

Compare Source

🏎 Performance
View changes on GitHub

v3.1.5

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.4

Compare Source

🐞 Bug Fixes
🏎 Performance
View changes on GitHub

v3.1.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.1.0

Compare Source

🛠️ Unhead CLI

To assist with migrations and overall DX a CLI has been introduced: @unhead/cli.

npx -y @unhead/cli 

It lets you do the following:

 audit Lint your codebase for unhead misuse, type-narrowing issues, and SEO/perf foot-guns. migrate Apply autofixes forv2-to-v3 migration: rewrite deprecated props and wrap tag literalsin defineX helpers.
validate-html Run the runtime ValidatePlugin over prerendered HTML files (e.g. dist/, .output/, build/). validate-url Fetch a rendered URL and run unhead\'s SEO/perf validation rules over its <head>. 

For example, try running audit on your own project for hints on how to improve your SEO.

✔️ Unhead ESLint

Knowing that your useHead() and useSeoMeta() code is right while your coding is important. While type-narrowing solves many broken cases, we introduce an ESLint plugin to help catch anything that the typechecker can't catch.

These rules are shared from the runtime ValidatePlugin

# flat-config ESLint plugin with v2→v3 migration autofixes
npm i -D @unhead/eslint-plugin
```ts[eslint.config.ts]import{configs}from'@&#8203;unhead/eslint-plugin'exportdefault[configs.recommended,]

🌊 Streaming SSR non-Vite support

The streaming plugin lived only at unhead/stream/vite previously, leaving non-Vite users with no way to wire the bootstrap. The plugin is now a bundler-agnostic unplugin factory with first-class webpack and Vite entries, and the framework packages compose it behind Unhead({ streaming: true }).

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'exportdefault{plugins: [vue(),Unhead({streaming: true})]}// webpack.config.tsimport{Unhead}from'@&#8203;unhead/vue/bundler'exportdefault{plugins: [...Unhead({streaming: true}).webpack()]}

Streaming also gains a nonce option (forwarded on every injected <script> for CSP support), a fixed async mode for production Vite builds (the IIFE is now emitted via this.emitFile() so the script src references a real hashed asset), a dev-mode warning when the client IIFE runs against an empty server queue, and a shared StreamingGlobal type so the server bootstrap, client, and injected IIFE agree on the shape of window.__unhead__. Default mode changed from async to inline for smaller TTFB.

Changelog

🚀 Features
🐞 Bug Fixes
View changes on GitHub

v3.0.5

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.4

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.3

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.2

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.1

Compare Source

🐞 Bug Fixes
View changes on GitHub

v3.0.0

Compare Source

Unhead v3 rebuilds the rendering engine from the ground up. The motivation: streaming SSR. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager.

📣 Highlights

🌊 Streaming SSR

Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new <title>, <meta>, and <link> tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load.

// entry-server.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/server'const{ head, wrapStream }=createStreamableHead()app.use(head)// wraps the Vue stream, injecting head updates as chunks resolvereturnwrapStream(renderToWebStream(app),template)
// entry-client.tsimport{createStreamableHead}from'@&#8203;unhead/vue/stream/client'consthead=createStreamableHead()app.use(head)

Under the hood: a queue stub (window.__unhead__) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing.

Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See PR #​537.

🛠️ Unified Vite Plugin + DevTools

A single @unhead/{framework}/vite plugin replaces the old manual composition of @unhead/addons + streaming plugin + framework glue. One import, one call, and you get tree-shaking, useSeoMetauseHead transform, inline minification, streaming SSR, dev-mode ValidatePlugin auto-injection, and Vite DevTools integration.

// vite.config.tsimport{Unhead}from'@&#8203;unhead/vue/vite'importvuefrom'@&#8203;vitejs/plugin-vue'exportdefaultdefineConfig({plugins: [vue(),Unhead()],})

The DevTools panel surfaces live head state during development: every useHead() / useSeoMeta() call with its source file and line number, resolved tags, SEO overview (title, description, canonical, Open Graph), useScript() load status, active plugins, template params, and warnings from the Validate plugin. Source tracing lets you click through from any tag back to the exact line that created it.

Available for Vue, React, Svelte, Solid, and vanilla via @unhead/bundler/vite (the renamed @unhead/addons package; the old name still works with a deprecation warning).

See PRs #​726, #​733, #​731.

🎯 useHead() Type Narrowing

useHead() now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong.

useHead({link: [// Narrows to StylesheetLink: requires href, offers media, integrity, etc.{rel: 'stylesheet',href: '/styles.css'},// Narrows to PreloadLink: requires as attribute{rel: 'preload',as: 'font',href: '/font.woff2',crossorigin: 'anonymous'},],script: [// Narrows to ModuleScript{src: '/app.mjs',type: 'module'},// Narrows to JsonLdScript{type: 'application/ld+json',innerHTML: '{}'},],})

See PRs #​627, #​665, #​729.

✅ ValidatePlugin

New optional ValidatePlugin that inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, render-blocking scripts, late <meta charset>, too many fetchpriority="high" hints, preconnect without crossorigin, and more. Also includes v2 migration rules that detect deprecated property names (children, hid/vmid, body: true), missing TemplateParamsPlugin, and missing AliasSortingPlugin — all of which cause silent breakage on upgrade. Auto-injected in dev by the unified Vite plugin so warnings surface in the browser console without any manual setup. Fully tree-shakeable. Rules use ESLint-style flat config:

import{ValidatePlugin}from'unhead/plugins'createHead({plugins: [ValidatePlugin({rules: {'missing-description': 'off',}})]})

See PRs #​690, #​691, #​716, #​722, #​725, #​732.

🔗 Canonical Plugin

New built-in CanonicalPlugin that auto-generates <link rel="canonical"> tags and resolves relative URLs to absolute in og:image, twitter:image, and og:url. Includes query parameter filtering (strips tracking params like utm_source, fbclid, gclid by default), trailing slash normalization, and automatic hash fragment stripping. Essential for SEO and social sharing.

import{CanonicalPlugin}from'unhead/plugins'createHead({plugins: [CanonicalPlugin({canonicalHost: 'https://mysite.com',trailingSlash: true,queryWhitelist: ['page','sort'],})]})

See PRs #​492, #​713.

🗜️ MinifyPlugin

New optional MinifyPlugin that minifies inline <script> and <style> tag content during SSR. Uses lightweight pure-JS minifiers with zero native dependencies, safe for edge and serverless runtimes. A companion build-time transform (MinifyTransform in @unhead/bundler) pre-minifies static innerHTML literals at compile time. Standalone utilities (minifyJS, minifyCSS, minifyJSON) are also available via unhead/minify.

import{MinifyPlugin}from'unhead/plugins'createHead({plugins: [MinifyPlugin()]})

See PR #​705.

📦 Performance

Buildv3 sizev2 sizeDelta gz
client1025411513-534 (-11.2%)
server989410361-194 (-4.6%)
vueClient1132312567-533 (-10.2%)
vueServer1084911312-191 (-4.1%)
Benchmarkv2 meanv3 meanDelta
@unhead/vue0.106ms0.072ms32% faster
core0.088ms0.073ms17% faster

Key optimizations:

  • Client-only CAPO sorting (#​626)
  • Pure, tree-shakeable core with no side effects (#​632)
  • Minified internal DOM state properties (#​635)
  • Migrated unplugins from estree-walker/acorn-loose to oxc-walker (#​663)
  • Walker-based transformHtmlTemplate (#​581)
  • TemplateParamsPlugin and AliasSortingPlugin made opt-in for smaller bundles (#​493, #​494)

📊 Schema.org

  • 12 new nodes: Dataset, MusicAlbum, MusicGroup, MusicPlaylist, MusicRecording, PodcastEpisode, PodcastSeason, PodcastSeries, Service, TVEpisode, TVSeason, TVSeries (#​612)
  • Graph resolution rewrite for correctness and performance (#​616)
  • Removed ohash and defu dependencies (#​605)

🔄 Other Changes

  • renderDOMHead() / renderSSRHead() are now fully synchronous, single-pass via a composable resolveTags() pipeline; the head instance exposes a pluggable render() function for framework integrations (#​619, #​622, #​628, #​629, #​630)
  • @unhead/react/helmet drop-in compat export for users migrating from react-helmet (#​719)
  • useHeadSafe() now whitelists CSS styles (#​491)
  • Support for blocking attribute on scripts and stylesheets (#​489)
  • useScript() consolidated back into core, legacy support dropped (#​498)
  • fediverse:creator meta tag support (#​703)
  • Switched from hookable to lighter HookableCore with sync-only hooks (#​631)
  • Deprecation warnings added to aliased packages (@unhead/schema, @unhead/shared) (#​678)
  • templateParams extensible via module augmentation (#​679)
  • Respect user-provided twitter:card in InferSeoMetaPlugin (#​681)
  • Enforce as attribute for preload links (#​683)
  • onRendered callback option on useHead() for synchronizing with DOM head updates (#​712)
  • tagWeight option on createHead() to override default CAPO tag weight function (#​716)

🐛 Bug Fixes

  • Hydration race condition with deferred patches (#​634)
  • Process pending patches even when dirty is false (#​636)
  • Deduplicate matching tags inside same render cycle (#​668)
  • Dedupe <link rel="alternate"> correctly (#​655, #​656, #​658)
  • React: dispose head entries on unmount in StrictMode (#​664)
  • React: force invalidation on entry disposal (#​559)
  • Vue: support computed getter trigger (#​638)
  • Vue: expose @unhead/vue/stream/iife with correct types (#​707)
  • Scripts: prevent scope disposal from aborting unrelated trigger (#​660)
  • Schema.org: allow null to opt out of default values (#​680)
  • Schema.org: normalize target to array before merging potentialAction (#​709)
  • Avoid mutating cached titleTemplate tag in resolveTitleTemplate (#​715)

⚠️ Breaking Changes

Synchronous rendering

renderDOMHead() and renderSSRHead() no longer return promises. Remove await.

Build plugins: @unhead/addons@unhead/bundler (#​726, #​733)

The @unhead/addons package has been renamed to @unhead/bundler (the old name still works with a deprecation warning). Framework Vite plugins now use a namedUnhead export and ship from each framework's /vite subpath:

- import unhead from '@&#8203;unhead/addons/vite'+ import { Unhead } from '@&#8203;unhead/vue/vite'
// or @&#8203;unhead/react/vite, @&#8203;unhead/svelte/vite, @&#8203;unhead/solid-js/vite
export default defineConfig({
- plugins: [unhead()],+ plugins: [Unhead()],
})
Strict Link / Script / Meta types (#​729)

Link and Script unions no longer fall back to GenericLink / GenericScript, so the type system enforces per-tag constraints (e.g. preload + as: 'font' requires crossorigin). Meta content is now required; use content: null explicitly to remove a meta tag. Custom rel / type values need satisfies GenericLink / satisfies GenericScript.

Dropped deprecations (#​624)
OldNew

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • "on Monday"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@netlify

netlifyBot commented Apr 13, 2026

Copy link
Copy Markdown

Deploy Preview for friendly-lamington-fb5690 ready!

NameLink
🔨 Latest commit520747b
🔍 Latest deploy loghttps://app.netlify.com/projects/friendly-lamington-fb5690/deploys/6a95463d08572200086d390f
😎 Deploy Previewhttps://deploy-preview-945--friendly-lamington-fb5690.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changesRun an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 9 times, most recently from 1c6f416 to 1fde2a7CompareApril 20, 2026 05:58
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 68dc7c3 to 67ba4f7CompareApril 28, 2026 17:52
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 7 times, most recently from a176ffc to 443f048CompareMay 7, 2026 06:49
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b6aec16 to 56c374bCompareMay 11, 2026 07:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 5 times, most recently from 48be561 to 8c00a0cCompareMay 28, 2026 13:00
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 8 times, most recently from 6e9d9a7 to 55ba0bcCompareJune 8, 2026 21:26
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 12 times, most recently from 23bb71d to 8006a92CompareJune 16, 2026 08:22
@renovate
renovateBotforce-pushed the renovate/major-unhead-monorepo branch 4 times, most recently from b643394 to 795393fCompareJune 25, 2026 03:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants