Skip to content

Commit 00dc633

Browse files
committed
feat: make public pages static with server-side rendering
Replace client-side reactivity with server-side data loading to minimize JavaScript on public pages. Changes: - Add +page.server.ts for all data-loading pages - Replace $page reactive usage with server-loaded props - Add prerender=true to static content pages - Remove $effect pattern from search page (moved to server load) - Convert all list/detail pages to use PageData props This reduces client-side JavaScript significantly while maintaining functionality. Only the search modal requires client JS.
1 parent e2f3467 commit 00dc633

File tree

14 files changed

+147
-160
lines changed

14 files changed

+147
-160
lines changed

src/routes/(site)/+page.server.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { getHomeArticles, getHomeProjects } from "$lib/data/public/index.remote";
2+
import { getStats } from "$lib/data/public/stats.remote";
3+
import type { PageServerLoad } from "./$types";
4+
5+
export const load: PageServerLoad = async () => {
6+
const stats = await getStats();
7+
const articles = await getHomeArticles(3);
8+
const allProjects = await getHomeProjects(5);
9+
10+
return {
11+
stats,
12+
articles,
13+
allProjects,
14+
};
15+
};
16+
17+
export const prerender = true;

src/routes/(site)/+page.svelte

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,12 @@
77
import ProjectsSection from "$lib/components/home/ProjectsSection.svelte";
88
import SponsorsSection from "$lib/components/home/SponsorsSection.svelte";
99
import StatsSection from "$lib/components/home/StatsSection.svelte";
10-
import { getHomeArticles, getHomeProjects } from "$lib/data/public/index.remote";
11-
import { getStats } from "$lib/data/public/stats.remote";
10+
import type { PageData } from "./$types";
1211
13-
const stats = await getStats();
14-
const articles = await getHomeArticles(3);
15-
const allProjects = await getHomeProjects(5);
12+
const { data }: { data: PageData } = $props();
1613
17-
const featuredProject = $derived(allProjects.find((p) => p.category === "active"));
18-
const projects = $derived(allProjects.filter((p) => p.id !== featuredProject?.id).slice(0, 4));
14+
const featuredProject = $derived(data.allProjects.find((p) => p.category === "active"));
15+
const projects = $derived(data.allProjects.filter((p) => p.id !== featuredProject?.id).slice(0, 4));
1916
2017
const currentYear = new Date().getFullYear();
2118
const years = currentYear - 2019;
@@ -28,16 +25,16 @@
2825

2926
<HeroSection />
3027

31-
<StatsSection members={stats.members} projects={stats.projects} articles={stats.articles} {years} />
28+
<StatsSection members={data.stats.members} projects={data.stats.projects} articles={data.stats.articles} {years} />
3229

3330
<ActivitiesSection />
3431

3532
<ProjectsSection {featuredProject} {projects} />
3633

37-
<ArticlesSection {articles} />
34+
<ArticlesSection articles={data.articles} />
3835

3936
<JoinCTA />
4037

4138
<SponsorsSection />
4239

43-
<AboutSection members={stats.members} />
40+
<AboutSection members={data.stats.members} />

src/routes/(site)/about/+page.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const prerender = true;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const prerender = true;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const prerender = true;

src/routes/(site)/articles/+page.svelte

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
11
<script lang="ts">
2-
import { SvelteURLSearchParams } from "svelte/reactivity";
3-
import { page } from "$app/state";
4-
import { getPublicArticles } from "$lib/data/public/index.remote";
2+
import type { PageData } from "./$types";
53
6-
const articles = await getPublicArticles();
4+
const { data }: { data: PageData } = $props();
75
const itemsPerPage = 12;
86
9-
const currentPage = $derived(Number(page.url.searchParams.get("page")) || 1);
10-
const totalPages = $derived(Math.ceil(articles.length / itemsPerPage));
7+
const totalPages = $derived(Math.ceil(data.articles.length / itemsPerPage));
118
const paginatedArticles = $derived(
12-
articles.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage),
9+
data.articles.slice((data.currentPage - 1) * itemsPerPage, data.currentPage * itemsPerPage),
1310
);
1411
1512
function pageUrl(pageNum: number): string {
16-
const params = new SvelteURLSearchParams(page.url.searchParams);
17-
if (pageNum === 1) {
18-
params.delete("page");
19-
} else {
20-
params.set("page", String(pageNum));
21-
}
22-
const query = params.toString();
23-
return query ? `?${query}` : page.url.pathname;
13+
return pageNum === 1 ? "/articles" : `/articles?page=${pageNum}`;
2414
}
2515
</script>
2616

@@ -43,7 +33,7 @@
4333
</section>
4434

4535
<div class="mx-auto max-w-6xl px-6 py-12">
46-
{#if articles.length === 0}
36+
{#if data.articles.length === 0}
4737
<p class="text-zinc-500">まだ記事がありません。</p>
4838
{:else}
4939
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
@@ -89,11 +79,11 @@
8979
{/each}
9080
</div>
9181

92-
{#if articles.length > itemsPerPage}
82+
{#if data.articles.length > itemsPerPage}
9383
<div class="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row sm:gap-4">
94-
{#if currentPage > 1}
84+
{#if data.currentPage > 1}
9585
<a
96-
href={pageUrl(currentPage - 1)}
86+
href={pageUrl(data.currentPage - 1)}
9787
class="w-full rounded-lg border border-zinc-200 bg-white px-4 py-2 text-center text-sm font-medium transition-colors hover:bg-primary/5 hover:border-primary/30 hover:text-primary focus:ring-2 focus:ring-primary/50 focus:ring-offset-2 sm:w-auto"
9888
>
9989
前へ
@@ -108,25 +98,25 @@
10898

10999
<div class="flex flex-1 flex-wrap items-center justify-center gap-1 sm:flex-initial">
110100
{#each Array.from({ length: totalPages }, (_, i) => i + 1) as pageNum (pageNum)}
111-
{#if totalPages <= 7 || pageNum === 1 || pageNum === totalPages || Math.abs(pageNum - currentPage) <= 1}
101+
{#if totalPages <= 7 || pageNum === 1 || pageNum === totalPages || Math.abs(pageNum - data.currentPage) <= 1}
112102
<a
113103
href={pageUrl(pageNum)}
114-
class="min-w-[2.5rem] rounded-lg border px-3 py-2 text-center text-sm font-medium transition-colors focus:ring-2 focus:ring-primary/50 focus:ring-offset-2 {currentPage ===
104+
class="min-w-[2.5rem] rounded-lg border px-3 py-2 text-center text-sm font-medium transition-colors focus:ring-2 focus:ring-primary/50 focus:ring-offset-2 {data.currentPage ===
115105
pageNum
116106
? 'border-primary bg-primary text-white'
117107
: 'border-zinc-200 bg-white hover:bg-primary/5 hover:border-primary/30 hover:text-primary'}"
118108
>
119109
{pageNum}
120110
</a>
121-
{:else if pageNum === currentPage - 2 || pageNum === currentPage + 2}
111+
{:else if pageNum === data.currentPage - 2 || pageNum === data.currentPage + 2}
122112
<span class="px-1 text-zinc-500">...</span>
123113
{/if}
124114
{/each}
125115
</div>
126116

127-
{#if currentPage < totalPages}
117+
{#if data.currentPage < totalPages}
128118
<a
129-
href={pageUrl(currentPage + 1)}
119+
href={pageUrl(data.currentPage + 1)}
130120
class="w-full rounded-lg border border-zinc-200 bg-white px-4 py-2 text-center text-sm font-medium transition-colors hover:bg-primary/5 hover:border-primary/30 hover:text-primary focus:ring-2 focus:ring-primary/50 focus:ring-offset-2 sm:w-auto"
131121
>
132122
次へ

src/routes/(site)/articles/[slug]/+page.svelte

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,25 @@
33
import type { PageData } from "./$types";
44
55
const { data }: { data: PageData } = $props();
6-
const { article, relatedArticles } = data;
6+
77
</script>
88

99
<svelte:head>
10-
{#if article}
11-
<title>{article.title} | ut.code();</title>
12-
<meta property="og:title" content={article.title} />
13-
{#if article.excerpt}
14-
<meta name="description" content={article.excerpt} />
15-
<meta property="og:description" content={article.excerpt} />
10+
{#if data.article}
11+
<title>{data.article.title} | ut.code();</title>
12+
<meta property="og:title" content={data.article.title} />
13+
{#if data.article.excerpt}
14+
<meta name="description" content={data.article.excerpt} />
15+
<meta property="og:description" content={data.article.excerpt} />
1616
{/if}
17-
{#if article.coverUrl}
18-
<meta property="og:image" content={article.coverUrl} />
17+
{#if data.article.coverUrl}
18+
<meta property="og:image" content={data.article.coverUrl} />
1919
{/if}
2020
<meta property="og:type" content="article" />
2121
{/if}
2222
</svelte:head>
2323

24-
{#if article}
24+
{#if data.article}
2525
<article class="mx-auto max-w-3xl px-6 py-16">
2626
<a
2727
href="/articles"
@@ -30,50 +30,50 @@
3030
← 記事一覧
3131
</a>
3232

33-
{#if article.coverUrl}
33+
{#if data.article.coverUrl}
3434
<img
35-
src={article.coverUrl}
36-
alt={article.title}
35+
src={data.article.coverUrl}
36+
alt={data.article.title}
3737
class="mb-6 aspect-[5/3] w-full rounded-xl object-cover sm:mb-8"
3838
loading="lazy"
3939
/>
4040
{/if}
4141

42-
<h1 class="mb-3 text-2xl font-bold sm:mb-4 sm:text-3xl md:text-4xl">{article.title}</h1>
42+
<h1 class="mb-3 text-2xl font-bold sm:mb-4 sm:text-3xl md:text-4xl">{data.article.title}</h1>
4343

4444
<div class="mb-6 flex flex-wrap items-center gap-3 text-sm text-zinc-500 sm:mb-8 sm:gap-4">
45-
{#if article.author}
45+
{#if data.article.author}
4646
<a
47-
href="/members/{article.author.slug}"
47+
href="/members/{data.article.author.slug}"
4848
class="flex items-center gap-2 hover:text-zinc-900"
4949
>
50-
{#if article.author.imageUrl}
50+
{#if data.article.author.imageUrl}
5151
<img
52-
src={article.author.imageUrl}
53-
alt={article.author.name}
52+
src={data.article.author.imageUrl}
53+
alt={data.article.author.name}
5454
class="aspect-square h-6 w-6 rounded-full object-cover"
5555
loading="lazy"
5656
/>
5757
{/if}
58-
{article.author.name}
58+
{data.article.author.name}
5959
</a>
6060
{/if}
61-
{#if article.publishedAt}
62-
<time datetime={article.publishedAt.toISOString()}>
63-
{article.publishedAt.toLocaleDateString("ja-JP")}
61+
{#if data.article.publishedAt}
62+
<time datetime={data.article.publishedAt.toISOString()}>
63+
{data.article.publishedAt.toLocaleDateString("ja-JP")}
6464
</time>
6565
{/if}
6666
</div>
6767

68-
<Markdown content={article.content} />
68+
<Markdown content={data.article.content} />
6969
</article>
7070

71-
{#if relatedArticles.length > 0}
71+
{#if data.relatedArticles.length > 0}
7272
<section class="mx-auto max-w-3xl px-6 pb-16">
7373
<div class="border-t border-zinc-200 pt-8 sm:pt-12">
7474
<h2 class="mb-4 text-xl font-bold sm:mb-6 sm:text-2xl">関連記事</h2>
7575
<div class="grid gap-4 sm:gap-6 md:grid-cols-2 lg:grid-cols-3">
76-
{#each relatedArticles as relatedArticle (relatedArticle.id)}
76+
{#each data.relatedArticles as relatedArticle (relatedArticle.id)}
7777
<a
7878
href="/articles/{relatedArticle.slug}"
7979
class="group rounded-xl border border-zinc-200/50 bg-white/80 backdrop-blur-md p-4 transition-all hover:bg-primary/5 hover:border-primary/30 hover:shadow-md"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const prerender = true;

src/routes/(site)/join/+page.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const prerender = true;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const prerender = true;

0 commit comments

Comments
 (0)