Fix global CSS wipe: inject head/body code via DOM, not <head dangerouslySetInnerHTML>

Rendering <head dangerouslySetInnerHTML={headCode}> in the App Router root
layout let the manual <head> overwrite the React-hoisted <link rel=stylesheet>
during hydration — all Tailwind styles dropped for real users once JS ran
(headless snapshots taken before hydration looked fine). headCode held the
Yandex.Metrika snippet. Replace with HtmlInjector client component that appends
the code through the DOM post-hydration, recreating <script> nodes so they run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 12:13:58 +05:00
parent a239607360
commit 3e2c70dd51
2 changed files with 50 additions and 6 deletions
+6 -6
View File
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Fira_Mono } from "next/font/google";
import "./globals.css";
import { getSettings } from "@/lib/settings";
import { HtmlInjector } from "@/components/layout/html-injector";
const firaMono = Fira_Mono({
weight: ["400", "500", "700"],
@@ -28,14 +29,13 @@ export default async function RootLayout({
return (
<html lang="ru" className={`${firaMono.variable} h-full antialiased`}>
{settings.headCode ? (
<head dangerouslySetInnerHTML={{ __html: settings.headCode }} />
) : null}
<body className="min-h-full flex flex-col">
{/* Код внедрения инжектится через DOM после гидрации (HtmlInjector),
а НЕ через <head dangerouslySetInnerHTML> — иначе ручной <head>
затирает хойстнутый React'ом <link rel="stylesheet"> и слетает дизайн. */}
{settings.headCode ? <HtmlInjector html={settings.headCode} target="head" /> : null}
{children}
{settings.bodyCode ? (
<div dangerouslySetInnerHTML={{ __html: settings.bodyCode }} />
) : null}
{settings.bodyCode ? <HtmlInjector html={settings.bodyCode} target="body" /> : null}
</body>
</html>
);