Next.js · SEO · Dane strukturalne · TypeScript
· 2 min czytania
Podczas audytu tej strony zrobiłem prosty test: pobrałem podstronę przez curl i poszukałem <script type="application/ld+json">. Nie było ani jednego. Dane strukturalne były widoczne w przeglądarce, narzędzia Google widziały je po wyrenderowaniu, ale surowy HTML nie zawierał żadnego tagu JSON-LD.
Dane strukturalne były renderowane przez next/script:
import Script from "next/script";
<Script
id="site-structured-data"
type="application/ld+json"
strategy="beforeInteractive"
dangerouslySetInnerHTML={{ __html: json }}
/>
Wygląda rozsądnie i ten wzorzec pojawia się w wielu tutorialach. W App Routerze skrypt z beforeInteractive nie jest jednak zapisywany w HTML-u jako tag. Next.js serializuje go do wywołania inline (self.__next_s.push(...)), a prawdziwy element <script> wstawia dopiero po stronie klienta.
Dla przeglądarki efekt jest ten sam. Dla wszystkiego, co czyta HTML bez uruchamiania JavaScriptu, dane strukturalne po prostu nie istnieją. Dotyczy to wielu crawlerów AI, botów generujących podglądy linków i sporej części narzędzi SEO.
JSON-LD to dane, a nie skrypt do wykonania, więc w ogóle nie potrzebuje next/script. Dokumentacja Next.js zaleca zwykły tag <script> renderowany w komponencie serwerowym:
export function StructuredDataScript({ id, json }: { id: string; json: string }) {
return (
<script
id={id}
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: json.replace(/</g, "\\u003c") }}
/>
);
}
replace escapuje znak <, żeby ciąg typu </script> w danych nie mógł przedwcześnie zamknąć tagu.
curl -s https://twoja-strona.pl/ | grep -c 'type="application/ld+json"'
Jeśli wynik to 0, a DevTools w przeglądarce pokazują schema, masz ten sam problem. Dokładniejszy test parsuje każdy blok JSON-LD i wypisuje typy:
import json, re, urllib.request
html = urllib.request.urlopen("https://twoja-strona.pl/").read().decode()
for block in re.findall(r'<script[^>]*application/ld\+json[^>]*>(.*?)</script>', html, re.S):
data = json.loads(block)
items = data if isinstance(data, list) else data.get("@graph", [data])
print([item.get("@type") for item in items])
next/script służy do sterowania ładowaniem kodu wykonywalnego. Dane inline należą do zwykłego tagu.Po zmianie każda podstrona tego serwisu wysyła dane Person, WebSite, ProfilePage i BreadcrumbList bezpośrednio w HTML-u z serwera. Jeśli chcesz, żebym spojrzał na technical SEO Twojej strony w Next.js, napisz do mnie.
Wyślij mi opis roli albo problemu. Odpowiadam w ciągu kilku dni roboczych, po polsku lub angielsku.