fix: prevent hero image flash by aligning server/client random seed

Generate heroIndex on the server and pass it to the client so SSR and
hydration pick the same hero recipe, eliminating the image swap on
first interaction.
This commit is contained in:
2026-02-16 14:43:15 +01:00
parent c21dcaa7ef
commit 37d7f28a72
3 changed files with 5 additions and 3 deletions
@@ -19,6 +19,7 @@ export const load: PageServerLoad = async ({ fetch, locals, params }) => {
return { return {
season: addFavoriteStatusToRecipes(item_season, userFavorites), season: addFavoriteStatusToRecipes(item_season, userFavorites),
all_brief: addFavoriteStatusToRecipes(item_all_brief, userFavorites), all_brief: addFavoriteStatusToRecipes(item_all_brief, userFavorites),
session session,
heroIndex: Math.random()
}; };
}; };
@@ -26,8 +26,8 @@
// Only recipes with hashed images (e.g. myrecipe.a1b2c3d4.webp) // Only recipes with hashed images (e.g. myrecipe.a1b2c3d4.webp)
const hasHashedImage = (r) => r.images?.length > 0 && /\.\w+\.\w+$/.test(r.images[0].mediapath); const hasHashedImage = (r) => r.images?.length > 0 && /\.\w+\.\w+$/.test(r.images[0].mediapath);
// Pick once on mount — not reactive, so image and link always match // Server-generated random index ensures SSR and client pick the same hero
const heroIndex = Math.random(); const heroIndex = data.heroIndex;
const heroRecipe = $derived.by(() => { const heroRecipe = $derived.by(() => {
const seasonPool = data.season.filter(hasHashedImage); const seasonPool = data.season.filter(hasHashedImage);
const pool = seasonPool.length > 0 ? seasonPool : data.all_brief.filter(hasHashedImage); const pool = seasonPool.length > 0 ? seasonPool : data.all_brief.filter(hasHashedImage);
@@ -34,6 +34,7 @@ export async function load({ data }) {
...data, ...data,
all_brief: rand_array(allBrief), all_brief: rand_array(allBrief),
season: rand_array(seasonRecipes), season: rand_array(seasonRecipes),
heroIndex: Math.random(),
isOffline: true isOffline: true
}; };
} }