perf: convert Hero to server component for faster LCP
All checks were successful
Gitea CI / test-build (push) Successful in 11m9s

- Hero now renders server-side, eliminating JS dependency for LCP text
- CMS messages fetched server-side instead of client useEffect
- Removes Hero from client JS bundle (~5KB less)
- LCP element (hero paragraph) now in initial HTML response
- Eliminates 2,380ms 'element rendering delay' reported by PSI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-03-04 14:16:58 +01:00
parent 60ea4e99be
commit 9fd530c68f
5 changed files with 30 additions and 57 deletions

View File

@@ -7,9 +7,7 @@
import { NextIntlClientProvider } from 'next-intl';
import dynamic from 'next/dynamic';
import Hero from './Hero';
import type {
HeroTranslations,
AboutTranslations,
ProjectsTranslations,
ContactTranslations,
@@ -30,23 +28,6 @@ function getNormalizedLocale(locale: string): 'en' | 'de' {
return locale.startsWith('de') ? 'de' : 'en';
}
export function HeroClient({ locale }: { locale: string; translations: HeroTranslations }) {
const normalLocale = getNormalizedLocale(locale);
const baseMessages = messageMap[normalLocale];
const messages = {
home: {
hero: baseMessages.home.hero
}
};
return (
<NextIntlClientProvider locale={locale} messages={messages}>
<Hero />
</NextIntlClientProvider>
);
}
export function AboutClient({ locale }: { locale: string; translations: AboutTranslations }) {
const normalLocale = getNormalizedLocale(locale);
const baseMessages = messageMap[normalLocale];

View File

@@ -1,27 +1,17 @@
"use client";
import { useLocale, useTranslations } from "next-intl";
import { getTranslations } from "next-intl/server";
import Image from "next/image";
import { useEffect, useState } from "react";
import { getMessages } from "@/lib/directus";
const Hero = () => {
const locale = useLocale();
const t = useTranslations("home.hero");
const [cmsMessages, setCmsMessages] = useState<Record<string, string>>({});
interface HeroProps {
locale: string;
}
useEffect(() => {
(async () => {
try {
const res = await fetch(`/api/messages?locale=${locale}`);
if (res.ok) {
const data = await res.json();
setCmsMessages(data.messages || {});
}
} catch {}
})();
}, [locale]);
export default async function Hero({ locale }: HeroProps) {
const [t, cmsMessages] = await Promise.all([
getTranslations("home.hero"),
getMessages(locale).catch(() => ({} as Record<string, string>)),
]);
// Helper to get CMS text or fallback
const getLabel = (key: string, fallback: string) => cmsMessages[key] || fallback;
return (
@@ -86,6 +76,4 @@ const Hero = () => {
</div>
</section>
);
};
export default Hero;
}