Skip to content

Commit b3807da

Browse files
committed
Refactor item detail dialog and analytics, clean up search actions
- Modularize item detail dialog into smaller components - Improve accessibility for item cards - Refactor search keyword actions to use a single action file - Remove unused and duplicate UI components - Add server-side PostHog analytics for error tracking - Update dependencies and environment variables for PostHog - Improve debounce logic in search bar - Add static metadata to home and about pages - Minor UI and type improvements throughout
1 parent b9c6777 commit b3807da

65 files changed

Lines changed: 1017 additions & 1068 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.example.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ AWS_USER=your-db-user
1010
AWS_PASSWORD=your-db-password
1111
AWS_DB_NAME=your-db-name
1212
AWS_PORT=5432
13+
NEXT_PUBLIC_POSTHOG_KEY=your_posthog_project_api_key
14+
NEXT_PUBLIC_POSTHOG_HOST=https://us.posthog.com
15+
POSTHOG_API_KEY=your_posthog_server_api_key
16+
POSTHOG_HOST=https://us.posthog.com

PERF_CHANGE_EXPLANATION.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Performance Change Explanation
2+
3+
This document explains what changed, why it changed, and how it affected speed, rendering mode, and perceived bundle/runtime cost.
4+
5+
## Goals of This Refactor
6+
7+
- Make first paint faster.
8+
- Avoid root-level blocking work.
9+
- Reduce hydration flicker.
10+
- Keep cache behavior simple and predictable.
11+
- Keep only required `Suspense` boundaries.
12+
13+
## What Was Changed
14+
15+
### 1) Root auth/session composition was simplified
16+
17+
- Removed the dedicated `SessionProvider` wrapper file.
18+
- Root layout now uses `Providers` directly.
19+
- Session is resolved client-side in `ContextProvider` with `authClient.getSession()` after hydration.
20+
21+
Why:
22+
23+
- Server-side session calls at root can force request-time rendering and delay initial HTML.
24+
- Client-side session resolution preserves a fast static shell.
25+
26+
### 2) Auth UI now waits for hydration
27+
28+
- Added `isUserHydrated` state in `ContextProvider`.
29+
- Navbar hides Sign In/Sign Out until hydration completes.
30+
- Shows a pulsing placeholder instead of flashing between auth states.
31+
32+
Why:
33+
34+
- Prevents hydration flicker and auth-button state jumps.
35+
36+
### 3) Suspense boundaries were minimized but kept where required
37+
38+
Kept:
39+
40+
- `Suspense` around `PostHogPageView` in layout.
41+
- `Suspense` around `ItemDisplayList` on home.
42+
- `Suspense` around async settings content in `/settings`.
43+
44+
Removed:
45+
46+
- Extra page-level `Suspense` around `LazyMap` section (not needed, `dynamic(..., { ssr: false, loading })` already handles client loading).
47+
48+
Why:
49+
50+
- With `cacheComponents`, Next.js requires some dynamic reads to be under `Suspense`.
51+
- Removing all Suspense around `ItemDisplayList` caused a build error (`blocking-route`) because it uses navigation/search param APIs.
52+
53+
### 4) Cache strategy was reduced to minimal behavior
54+
55+
- `getAllItems()` keeps `"use cache"` only.
56+
- Removed explicit `cacheLife(...)`.
57+
- Removed `cacheTag(...)`.
58+
- Item mutations use `revalidatePath("/")` (create/edit/delete/update).
59+
60+
Why:
61+
62+
- This is the smallest setup that still keeps homepage fresh after writes.
63+
- Matches the request to avoid extra cache complexity.
64+
65+
### 5) CSS animation stability tweaks
66+
67+
- Added animation stabilization hints in global CSS:
68+
- `animation-fill-mode: both`
69+
- `backface-visibility: hidden`
70+
- GPU/compositor hints for animated shell elements
71+
- Reduced over-broad transition usage on homepage wrappers.
72+
73+
Why:
74+
75+
- Lowers repaint artifacts and dithering during hydration.
76+
77+
## Why `/` Is Static Instead of PPR
78+
79+
Homepage (`/`) is static because:
80+
81+
- It is cacheable (`"use cache"`).
82+
- It does not perform request-time server APIs like `headers()` at root render.
83+
- Dynamic client concerns are isolated under required `Suspense`.
84+
85+
Build output reflects this as:
86+
87+
- `/` => `○ Static`
88+
89+
## Why `/settings` Is PPR
90+
91+
`/settings` is partial prerender because:
92+
93+
- It loads user-specific async data (`findPhoneNumber`).
94+
- That async section is wrapped in `Suspense`, so static shell can stream dynamic content.
95+
96+
Build output reflects this as:
97+
98+
- `/settings` => `◐ Partial Prerender`
99+
100+
## Why It Feels Faster and "Smaller"
101+
102+
Even without a formal bundle analyzer comparison, runtime cost dropped because:
103+
104+
- Root server session blocking was removed.
105+
- Redundant provider layer was removed.
106+
- Auth-guess/cookie machinery was removed.
107+
- Unnecessary Suspense wrappers were removed.
108+
- Hydration state transitions were simplified.
109+
110+
This reduces:
111+
112+
- work before first meaningful HTML,
113+
- hydration churn,
114+
- unnecessary UI state corrections.
115+
116+
## Important Build Constraints Learned
117+
118+
1. With `cacheComponents`, dynamic reads can require `Suspense` to avoid build-time blocking-route errors.
119+
2. `revalidate` route segment config conflicts with `cacheComponents`.
120+
3. If using `revalidateTag` in this Next version, it requires a second argument/profile.
121+
4. Simpler invalidation (`revalidatePath("/")`) is often enough if your freshness scope is homepage-only.
122+
123+
## Net Result
124+
125+
- Build passes.
126+
- `/` is static and faster to first paint.
127+
- `/settings` remains PPR for personalized data.
128+
- Auth button flicker is controlled by hydration gating.
129+
- Cache behavior is simple and minimal.

PERF_LEARNINGS.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## Performance Learnings (Homepage + OpenNext)
2+
3+
### Rendering and PPR
4+
5+
- With `cacheComponents` enabled, any dynamic hooks like `useSearchParams()` must sit under a `<Suspense>` boundary to avoid PPR build failures.
6+
- Route segment config like `dynamic = "force-dynamic"` is incompatible with `cacheComponents`; use `"use cache"` and `cacheLife` instead.
7+
- Keep client-only analytics (like `PostHogPageView`) under a `Suspense` boundary so it does not block PPR.
8+
9+
### Data and Payload
10+
11+
- Large server payloads on the homepage are the biggest TTFB and RSC weight factor.
12+
- A trimmed DTO for homepage list/map reduces RSC payload without losing UI functionality.
13+
- Fetch full item details on-demand for dialogs to keep the first payload light.
14+
15+
### Caching (ISR-like behavior)
16+
17+
- `cacheLife("days")` provides ISR-like stale-while-revalidate semantics with long-lived cache.
18+
- `cacheTag(...)` + `revalidateTag(tag, "seconds")` makes cache update immediately after writes.
19+
- Keep cache long-lived (days/weeks) when updates are slow and invalidate on write.
20+
21+
### Client JS Deferrals
22+
23+
- Defer large map bundles by gating on intersection/idle and keep a skeleton in place.
24+
- Lazy-load modal/dialog flows so they do not inflate the first chunk.
25+
- Lazy-load analytics initialization and event capture to keep the initial bundle small.
26+
27+
### Auth/Session
28+
29+
- Server session checks can block initial HTML if placed at the root; keep auth UI simple until the client session resolves.
30+
- If server session is required for correctness, accept the tradeoff and keep Suspense only where needed (analytics).
31+
32+
### OpenNext/SST
33+
34+
- Warming the server function (`warm: 1`) helps reduce cold-start p95 without changing app semantics.
35+
- PPR build logs confirm partial prerender; keep dynamic reads isolated behind `Suspense`.
36+
37+
### Build-time Gotchas
38+
39+
- `revalidateTag` requires a profile/config argument with `cacheComponents` enabled.
40+
- Avoid mixing route segment configs that conflict with cache components.

0 commit comments

Comments
 (0)