From 3c3798b197a99f17a5771126245ceeda78813491 Mon Sep 17 00:00:00 2001
From: SGavrl <277843151+SGavrl@users.noreply.github.com>
Date: Sun, 31 May 2026 11:16:35 +0000
Subject: [PATCH] fix(js): HTMLMetaElement.content is read/write (#210) (#211)
`.content` was a getter-only stub. `next/head` and `next/document`
iterate tags and assign `.content` during hydration, which threw
`Cannot set property content`, React caught and retried in a tight loop,
the page never mounted, and the error spammed the console thousands of
times. Traveloka and other Next.js sites doing meta management were unusable.
Now reflects the content attribute with both get and set, per spec.
Template's `.content` (DocumentFragment) is unchanged.
Closes #210.
---
crates/obscura-js/js/bootstrap.js | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/crates/obscura-js/js/bootstrap.js b/crates/obscura-js/js/bootstrap.js
index a9a6e77..4940f7a 100644
--- a/crates/obscura-js/js/bootstrap.js
+++ b/crates/obscura-js/js/bootstrap.js
@@ -515,9 +515,23 @@ class Element extends Node {
return ids.map(_wrapEl).filter(Boolean);
}
get content() {
- if (this.localName !== 'template') return undefined;
- if (!this._templateContent) this._templateContent = document.createDocumentFragment();
- return this._templateContent;
+ // .content is a DocumentFragment; .content reflects
+ // the content attribute (read/write per spec). Next.js' next/head
+ // iterates tags and sets .content during hydration, which
+ // threw with the previous getter-only stub and put React into an
+ // infinite retry loop (issue #210).
+ const tag = this.localName;
+ if (tag === 'template') {
+ if (!this._templateContent) this._templateContent = document.createDocumentFragment();
+ return this._templateContent;
+ }
+ if (tag === 'meta') return this.getAttribute('content') || '';
+ return undefined;
+ }
+ set content(v) {
+ if (this.localName === 'meta') {
+ this.setAttribute('content', v == null ? '' : String(v));
+ }
}
get childElementCount() { return this.children.length; }
get firstElementChild() { return this.children[0] || null; }