From 1182cfd23924b76fb2da59203b005dcaf1ec4ace Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Wed, 31 Dec 2025 14:40:03 +0100 Subject: [PATCH] implement category-based lazy loading to improve initial page load Add Intersection Observer-based lazy loading for recipe categories to dramatically reduce initial render time. Categories render progressively as users scroll, reducing initial DOM from 240 cards to ~30-50 cards. Performance improvements: - First 2 categories render eagerly (~30-50 cards) for fast perceived load - Remaining categories lazy-load 600px before entering viewport - Categories render immediately during active search for instant results - "In Season" section always renders first as hero content Implementation: - Add LazyCategory component with IntersectionObserver for vertical lazy loading - Wrap MediaScroller categories with progressive loading logic - Maintain scroll position with placeholder heights (300px per category) - Keep search functionality fully intact with all 240 recipes searchable - Horizontal lazy loading not implemented (IntersectionObserver doesn't work well with overflow-x scroll containers) --- src/lib/components/LazyCategory.svelte | 65 +++++++++++++++++++ .../[recipeLang=recipeLang]/+page.svelte | 36 ++++++---- 2 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 src/lib/components/LazyCategory.svelte diff --git a/src/lib/components/LazyCategory.svelte b/src/lib/components/LazyCategory.svelte new file mode 100644 index 0000000..507ce77 --- /dev/null +++ b/src/lib/components/LazyCategory.svelte @@ -0,0 +1,65 @@ + + +{#if isVisible} + +
+ {@render children()} +
+{:else} + +
+ +
+{/if} diff --git a/src/routes/[recipeLang=recipeLang]/+page.svelte b/src/routes/[recipeLang=recipeLang]/+page.svelte index 182886d..b907155 100644 --- a/src/routes/[recipeLang=recipeLang]/+page.svelte +++ b/src/routes/[recipeLang=recipeLang]/+page.svelte @@ -4,6 +4,7 @@ import AddButton from '$lib/components/AddButton.svelte'; import Card from '$lib/components/Card.svelte'; import Search from '$lib/components/Search.svelte'; + import LazyCategory from '$lib/components/LazyCategory.svelte'; let { data }: { data: PageData } = $props(); let current_month = new Date().getMonth() + 1; @@ -68,25 +69,38 @@ h1{ !hasActiveSearch || matchedRecipeIds.has(recipe._id) )} {#if seasonRecipes.length > 0} - - {#each seasonRecipes as recipe} - - {/each} - + + {#snippet children()} + + {#each seasonRecipes as recipe} + + {/each} + + {/snippet} + {/if} {/if} -{#each categories as category} +{#each categories as category, index} {@const categoryRecipes = data.all_brief.filter(recipe => recipe.category === category && (!hasActiveSearch || matchedRecipeIds.has(recipe._id)) )} {#if categoryRecipes.length > 0} - - {#each categoryRecipes as recipe} - - {/each} - + + {#snippet children()} + + {#each categoryRecipes as recipe} + + {/each} + + {/snippet} + {/if} {/each} {#if !isEnglish}