Skip to content

fix(router,ssr): render the httpEquiv meta prop as the http-equiv attribute - #253

Merged
dangreen merged 1 commit into
mainfrom
fix/head-http-equiv
Sep 8, 2026
Merged

fix(router,ssr): render the httpEquiv meta prop as the http-equiv attribute#253
dangreen merged 1 commit into
mainfrom
fix/head-http-equiv

Conversation

@dangreen

@dangreen dangreen commented Sep 8, 2026

Copy link
Copy Markdown
Member

Why

Head descriptor props follow the React naming, and httpEquiv is the only prop whose HTML attribute name is not just its lowercase form. syncHead passed the prop name straight to setAttribute and into the hydration selector, and headDescriptorToHtml only lowercased it, so meta({ httpEquiv: 'refresh', content: '...' }) produced <meta httpequiv="refresh"> both on the client and in server HTML. Browsers ignore that attribute, and on hydration the selector meta[httpEquiv="refresh"] did not match the server tag, so a second meta was appended.

What

  • @nano_kit/router exports toHtmlAttribute(prop): httpEquiv becomes http-equiv, everything else is lowercased. syncHead uses it for the hydration selector and setAttribute.
  • headDescriptorToHtml in @nano_kit/ssr uses the same helper instead of its own toLowerCase().
  • Tests: toHtmlAttribute unit tests, should render httpEquiv as the http-equiv attribute and should hydrate existing http-equiv meta tag in the router meta suite (both fail on main: httpequiv, and a duplicated meta), and the SSR counterpart in headDescriptorToHtml.

Size

The public helper costs bytes in the router bundle; the limit is re-pinned on the 0.05 kB step:

bundle before after limit
All publics (Gzip) 4121 B 4172 B 4.15 kB → 4.2 kB
All publics (Brotli) 3703 B 3748 B 3.75 kB
Minimal set (Gzip) 1543 B 1543 B 1.55 kB
Minimal set (Brotli) 1370 B 1368 B 1.4 kB

Checks

  • oxlint, tsc --noEmit and vitest run pass in packages/router (135 tests) and packages/ssr (15 tests); size-limit passes in packages/router.

… attribute

Head descriptor props are camelCase, and `httpEquiv` is the only one whose attribute name is not its lowercase form. `syncHead` passed the prop name to `setAttribute` and to the hydration selector, and `headDescriptorToHtml` lowercased it, so both sides produced `httpequiv`, which browsers ignore, and hydration appended a second `meta` next to the server-rendered one. The new `toHtmlAttribute` helper in `@nano_kit/router` maps a prop name to its attribute name and is shared by the client and the SSR renderer. The `router` size limit is re-pinned for the helper.
@codecov

codecov Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.33%. Comparing base (ab83437) to head (478f627).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #253   +/-   ##
=======================================
  Coverage   83.33%   83.33%           
=======================================
  Files          98       98           
  Lines        2556     2557    +1     
  Branches      551      552    +1     
=======================================
+ Hits         2130     2131    +1     
  Misses        314      314           
  Partials      112      112           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dangreen
dangreen merged commit 568df71 into main Sep 8, 2026
10 checks passed
@dangreen
dangreen deleted the fix/head-http-equiv branch September 8, 2026 19:09
@github-actions github-actions Bot mentioned this pull request Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant