Skip to content

fix(unhead): dedupe scalar Open Graph and Twitter metadata - #880

Merged
harlan-zw merged 4 commits into
mainfrom
fix/twitter-meta-dedupe
Jul 24, 2026
Merged

fix(unhead): dedupe scalar Open Graph and Twitter metadata#880
harlan-zw merged 4 commits into
mainfrom
fix/twitter-meta-dedupe

Conversation

@harlan-zw

@harlan-zw harlan-zw commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

🔗 Linked issue

Related to #842

❓ Type of change

  • 📖 Documentation
  • 🐞 Bug fix
  • 👌 Enhancement
  • ✨ New feature
  • 🧹 Chore
  • ⚠️ Breaking change

📚 Description

Unhead kept duplicate og:title, article:section, and other scalar properties because array handling applied to whole namespaces.

Array handling now follows the schema: Open Graph media and alternate locales, article and book authors and tags, and deprecated Twitter image groups remain repeatable. Other duplicates use the existing last-tag-wins rule in SSR and the DOM.

Summary by CodeRabbit

  • Bug Fixes
    • Improved duplicate-handling for Open Graph and Twitter meta tags so scalar values keep only the latest entry.
    • Preserves repeated structured image metadata (including corresponding alt text) in the correct order.
    • Refined which specific repeated fields are treated as multi-value.
  • Documentation
    • Clarified which metadata fields support multiple values within a single head call, and how other duplicates are resolved.
  • Tests
    • Expanded unit and SSR/CSR test coverage for the updated deduping behavior.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d20e71c8-c7d4-47a2-88b4-96021da2c063

📥 Commits

Reviewing files that changed from the base of the PR and between eff3d41 and 81fd32e.

📒 Files selected for processing (2)
  • packages/unhead/test/unit/server/deduping.test.ts
  • packages/vue/test/unit/e2e/basic.test.ts
💤 Files with no reviewable changes (1)
  • packages/vue/test/unit/e2e/basic.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/unhead/test/unit/server/deduping.test.ts

📝 Walkthrough

Walkthrough

Social metadata deduplication now treats scalar Open Graph and Twitter fields as single-valued while preserving repeatable structured image fields. Unit, client, SSR, snapshot, and documentation changes cover the updated rules.

Changes

Social metadata deduplication

Layer / File(s) Summary
Explicit metadata dedupe rules
packages/unhead/src/utils/const.ts, packages/unhead/src/utils/dedupe.ts, packages/unhead/test/unit/dedupe.test.ts
Explicit repeatable metadata identifiers and structured image prefixes are preserved, while scalar fields use the latest value.
Client and SSR rendering validation
packages/unhead/test/unit/client/duplicates.test.ts, packages/unhead/test/unit/server/deduping.test.ts, packages/vue/test/unit/e2e/basic.test.ts
Client and SSR tests verify scalar replacement and ordered structured image tags; snapshots omit the deduped og:locale tag.
Duplicate-handling documentation
docs/head/.../6.handling-duplicates.md
The guide lists supported repeated metadata fields and documents last-tag-wins behavior for other duplicate names.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: bug, v3

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, conventional, and accurately summarizes the main change: deduping scalar Open Graph and Twitter metadata.
Description check ✅ Passed The description includes the required linked issue, type of change, and a clear summary of the bug fix and repeatable fields.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/twitter-meta-dedupe

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size

⚠️ 12 bundles grew · net +0.4 kB gz

Bundle Gzipped Δ
Client (Minimal) 5.3 kB → 5.3 kB 🔴 +40 B (+0.7%)
Client (Full) 9.4 kB → 9.5 kB 🔴 +34 B (+0.4%)
Client (Self-Contained) 5.7 kB → 5.7 kB 🔴 +43 B (+0.7%)
Server (Minimal) 4.8 kB → 4.9 kB 🔴 +41 B (+0.8%)
Server (Self-Contained) 5.2 kB → 5.2 kB 🔴 +46 B (+0.9%)
Vue Client (Minimal) 5.8 kB → 5.9 kB 🔴 +38 B (+0.6%)
Vue Client (Full) 10.5 kB → 10.5 kB 🔴 +28 B (+0.3%)
Vue Server (Minimal) 5.3 kB → 5.3 kB 🔴 +40 B (+0.7%)
React Client (Minimal) 5.8 kB → 5.8 kB 🔴 +42 B (+0.7%)
React Client (Full) 10.5 kB → 10.6 kB 🔴 +31 B (+0.3%)
React Server (Minimal) 5.1 kB → 5.2 kB 🔴 +41 B (+0.8%)
Schema.org (Minimal) 10 kB → 10 kB 🔴 +31 B (+0.3%)
All bundles (14)
Bundle Gzipped Brotli Raw
Core
Client (Minimal) 5.3 kB 4.8 kB 13.5 kB 🔴
Client (Full) 9.5 kB 8.6 kB 24.8 kB 🔴
Client (Self-Contained) 5.7 kB 5.1 kB 14.2 kB 🔴
Server (Minimal) 4.9 kB 4.4 kB 12.3 kB 🔴
Server (Self-Contained) 5.2 kB 4.7 kB 13 kB 🔴
Vue
Vue Client (Minimal) 5.9 kB 5.3 kB 14.6 kB 🔴
Vue Client (Full) 10.5 kB 9.5 kB 27.6 kB 🔴
Vue Server (Minimal) 5.3 kB 4.8 kB 13.3 kB 🔴
React
React Client (Minimal) 5.8 kB 5.3 kB 14.7 kB 🔴
React Client (Full) 10.6 kB 9.6 kB 27.9 kB 🔴
React Server (Minimal) 5.2 kB 4.7 kB 13 kB 🔴
Schema.org
Schema.org (Minimal) 10 kB 9.1 kB 27.9 kB 🔴
Schema.org Imports 0.1 kB 0.1 kB 0.1 kB
Schema.org Vue Meta 0.4 kB 0.3 kB 0.8 kB

⚡ Performance (directional)

No significant change (within CI noise)

All benchmarks (14)
Benchmark PR Δ RME
SSR render (CPU) 0.365 ms ~ noise ±9.1%
SSR render (wall) 0.265 ms ~ noise ±4.7%
SSR allocated / render 236.1 KiB ~ noise ±7.0%
Schema.org cached render (CPU) 0.334 ms ~ noise ±7.0%
Schema.org cached render (wall) 0.240 ms ~ noise ±3.3%
Schema.org cached allocated / render 133.8 KiB ~ noise ±0.9%
Streaming wrapStream drain (CPU) 0.238 ms ~ noise ±3.8%
Streaming wrapStream drain (wall) 0.150 ms ~ noise ±4.2%
Streaming allocated / drain 149.2 KiB ~ noise ±0.6%
Streaming suspense chunk (CPU) 0.012 ms ~ noise ±4.8%
Streaming allocated / suspense chunk 5.1 KiB ~ noise ±0.7%
CSR DOM mutations / nav 38 ~ noise
CSR re-render (CPU) 0.863 ms ~ noise ±4.8%
CSR re-render (wall) 0.487 ms ~ noise ±2.4%

Baseline: main @ bf71e22 · 2026-07-23 · gzipped is the headline size metric · perf is directional (shared-runner, gated)

@harlan-zw harlan-zw added v3 Targets Unhead v3 or the main branch bug Something isn't working labels Jul 24, 2026
@harlan-zw
harlan-zw requested review from TheAlexLichter and removed request for TheAlexLichter July 24, 2026 05:33
@harlan-zw harlan-zw changed the title fix(unhead): dedupe scalar Twitter meta tags fix(unhead): dedupe scalar Open Graph and Twitter metadata Jul 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/unhead/test/unit/client/duplicates.test.ts (1)

6-49: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Avoid a fixed sleep for DOM synchronization.

The short timeout makes this test timing-dependent; slower CI may assert before the head update completes. Await the head’s actual update/flush promise instead of relying on elapsed time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/unhead/test/unit/client/duplicates.test.ts` around lines 6 - 49,
Replace the fixed setTimeout delay in the test around useSeoMeta with the head
instance’s actual update or flush promise, awaiting the DOM synchronization
mechanism exposed by useDOMHead. Keep the existing assertions unchanged and
ensure they run only after the metadata update has completed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/unhead/test/unit/server/deduping.test.ts`:
- Around line 40-48: Update the deduping test assertions around the Open Graph
and Twitter image tags to validate serialized tag order or grouped
image-plus-alt pairs, rather than checking image and alt values as independent
lists. Preserve the expected first-image/first-alt and second-image/second-alt
associations for both og:image and twitter:image.

---

Outside diff comments:
In `@packages/unhead/test/unit/client/duplicates.test.ts`:
- Around line 6-49: Replace the fixed setTimeout delay in the test around
useSeoMeta with the head instance’s actual update or flush promise, awaiting the
DOM synchronization mechanism exposed by useDOMHead. Keep the existing
assertions unchanged and ensure they run only after the metadata update has
completed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c91a728-ea70-4a33-a23c-067fa76ab8cd

📥 Commits

Reviewing files that changed from the base of the PR and between 027ed2e and eff3d41.

📒 Files selected for processing (6)
  • docs/head/1.guides/1.core-concepts/6.handling-duplicates.md
  • packages/unhead/src/utils/const.ts
  • packages/unhead/src/utils/dedupe.ts
  • packages/unhead/test/unit/client/duplicates.test.ts
  • packages/unhead/test/unit/dedupe.test.ts
  • packages/unhead/test/unit/server/deduping.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/unhead/test/unit/dedupe.test.ts

Comment thread packages/unhead/test/unit/server/deduping.test.ts Outdated
@harlan-zw
harlan-zw merged commit 72d9843 into main Jul 24, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working v3 Targets Unhead v3 or the main branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant