@prmichaelsen/acp-visualizer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +68 -0
  2. package/agent/commands/acp.clarification-address.md +417 -0
  3. package/agent/commands/acp.clarification-capture.md +386 -0
  4. package/agent/commands/acp.clarification-create.md +437 -0
  5. package/agent/commands/acp.clarifications-research.md +326 -0
  6. package/agent/commands/acp.command-create.md +432 -0
  7. package/agent/commands/acp.design-create.md +286 -0
  8. package/agent/commands/acp.design-reference.md +355 -0
  9. package/agent/commands/acp.handoff.md +270 -0
  10. package/agent/commands/acp.index.md +423 -0
  11. package/agent/commands/acp.init.md +546 -0
  12. package/agent/commands/acp.package-create.md +895 -0
  13. package/agent/commands/acp.package-info.md +212 -0
  14. package/agent/commands/acp.package-install.md +539 -0
  15. package/agent/commands/acp.package-list.md +280 -0
  16. package/agent/commands/acp.package-publish.md +541 -0
  17. package/agent/commands/acp.package-remove.md +293 -0
  18. package/agent/commands/acp.package-search.md +307 -0
  19. package/agent/commands/acp.package-update.md +361 -0
  20. package/agent/commands/acp.package-validate.md +540 -0
  21. package/agent/commands/acp.pattern-create.md +386 -0
  22. package/agent/commands/acp.plan.md +587 -0
  23. package/agent/commands/acp.proceed.md +882 -0
  24. package/agent/commands/acp.project-create.md +675 -0
  25. package/agent/commands/acp.project-info.md +312 -0
  26. package/agent/commands/acp.project-list.md +226 -0
  27. package/agent/commands/acp.project-remove.md +379 -0
  28. package/agent/commands/acp.project-set.md +227 -0
  29. package/agent/commands/acp.project-update.md +307 -0
  30. package/agent/commands/acp.projects-restore.md +228 -0
  31. package/agent/commands/acp.projects-sync.md +347 -0
  32. package/agent/commands/acp.report.md +407 -0
  33. package/agent/commands/acp.resume.md +239 -0
  34. package/agent/commands/acp.sessions.md +301 -0
  35. package/agent/commands/acp.status.md +293 -0
  36. package/agent/commands/acp.sync.md +364 -0
  37. package/agent/commands/acp.task-create.md +500 -0
  38. package/agent/commands/acp.update.md +302 -0
  39. package/agent/commands/acp.validate.md +466 -0
  40. package/agent/commands/acp.version-check-for-updates.md +276 -0
  41. package/agent/commands/acp.version-check.md +191 -0
  42. package/agent/commands/acp.version-update.md +289 -0
  43. package/agent/commands/command.template.md +339 -0
  44. package/agent/commands/git.commit.md +526 -0
  45. package/agent/commands/git.init.md +514 -0
  46. package/agent/commands/tanstack-cloudflare.deploy.md +272 -0
  47. package/agent/commands/tanstack-cloudflare.tail.md +275 -0
  48. package/agent/design/.gitkeep +0 -0
  49. package/agent/design/design.template.md +154 -0
  50. package/agent/design/local.dashboard-layout-routing.md +288 -0
  51. package/agent/design/local.data-model-yaml-parsing.md +310 -0
  52. package/agent/design/local.search-filtering.md +331 -0
  53. package/agent/design/local.server-api-auto-refresh.md +235 -0
  54. package/agent/design/local.table-tree-views.md +299 -0
  55. package/agent/design/local.visualizer-requirements.md +349 -0
  56. package/agent/design/requirements.template.md +387 -0
  57. package/agent/index/.gitkeep +0 -0
  58. package/agent/index/acp.core.yaml +137 -0
  59. package/agent/index/local.main.template.yaml +37 -0
  60. package/agent/manifest.template.yaml +13 -0
  61. package/agent/manifest.yaml +302 -0
  62. package/agent/milestones/.gitkeep +0 -0
  63. package/agent/milestones/milestone-1-project-scaffold-data-pipeline.md +67 -0
  64. package/agent/milestones/milestone-1-{title}.template.md +206 -0
  65. package/agent/milestones/milestone-2-dashboard-views-interaction.md +79 -0
  66. package/agent/package.template.yaml +86 -0
  67. package/agent/patterns/.gitkeep +0 -0
  68. package/agent/patterns/bootstrap.template.md +1237 -0
  69. package/agent/patterns/pattern.template.md +382 -0
  70. package/agent/patterns/tanstack-cloudflare.acl-permissions.md +332 -0
  71. package/agent/patterns/tanstack-cloudflare.action-bar-item.md +416 -0
  72. package/agent/patterns/tanstack-cloudflare.api-route-handlers.md +401 -0
  73. package/agent/patterns/tanstack-cloudflare.auth-session-management.md +387 -0
  74. package/agent/patterns/tanstack-cloudflare.card-and-list.md +271 -0
  75. package/agent/patterns/tanstack-cloudflare.chat-engine.md +353 -0
  76. package/agent/patterns/tanstack-cloudflare.confirmation-tokens.md +346 -0
  77. package/agent/patterns/tanstack-cloudflare.durable-objects-websocket.md +516 -0
  78. package/agent/patterns/tanstack-cloudflare.email-service.md +431 -0
  79. package/agent/patterns/tanstack-cloudflare.expander.md +98 -0
  80. package/agent/patterns/tanstack-cloudflare.fcm-push.md +115 -0
  81. package/agent/patterns/tanstack-cloudflare.firebase-anonymous-sessions.md +441 -0
  82. package/agent/patterns/tanstack-cloudflare.firebase-auth.md +348 -0
  83. package/agent/patterns/tanstack-cloudflare.firebase-firestore.md +550 -0
  84. package/agent/patterns/tanstack-cloudflare.firebase-storage.md +369 -0
  85. package/agent/patterns/tanstack-cloudflare.form-controls.md +145 -0
  86. package/agent/patterns/tanstack-cloudflare.global-search-context.md +93 -0
  87. package/agent/patterns/tanstack-cloudflare.image-carousel.md +126 -0
  88. package/agent/patterns/tanstack-cloudflare.library-services.md +553 -0
  89. package/agent/patterns/tanstack-cloudflare.lightbox.md +169 -0
  90. package/agent/patterns/tanstack-cloudflare.markdown-content.md +115 -0
  91. package/agent/patterns/tanstack-cloudflare.mention-suggestions.md +98 -0
  92. package/agent/patterns/tanstack-cloudflare.modal.md +156 -0
  93. package/agent/patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md +461 -0
  94. package/agent/patterns/tanstack-cloudflare.notifications-engine.md +151 -0
  95. package/agent/patterns/tanstack-cloudflare.oauth-token-refresh.md +90 -0
  96. package/agent/patterns/tanstack-cloudflare.og-metadata.md +296 -0
  97. package/agent/patterns/tanstack-cloudflare.pagination.md +442 -0
  98. package/agent/patterns/tanstack-cloudflare.pill-input.md +220 -0
  99. package/agent/patterns/tanstack-cloudflare.provider-adapter.md +401 -0
  100. package/agent/patterns/tanstack-cloudflare.rate-limiting.md +323 -0
  101. package/agent/patterns/tanstack-cloudflare.scheduled-tasks.md +338 -0
  102. package/agent/patterns/tanstack-cloudflare.searchable-settings.md +375 -0
  103. package/agent/patterns/tanstack-cloudflare.slide-over.md +129 -0
  104. package/agent/patterns/tanstack-cloudflare.ssr-preload.md +571 -0
  105. package/agent/patterns/tanstack-cloudflare.third-party-api-integration.md +508 -0
  106. package/agent/patterns/tanstack-cloudflare.toast-system.md +142 -0
  107. package/agent/patterns/tanstack-cloudflare.unified-header.md +280 -0
  108. package/agent/patterns/tanstack-cloudflare.user-scoped-collections.md +628 -0
  109. package/agent/patterns/tanstack-cloudflare.websocket-manager.md +237 -0
  110. package/agent/patterns/tanstack-cloudflare.wrangler-configuration.md +358 -0
  111. package/agent/patterns/tanstack-cloudflare.zod-schema-validation.md +336 -0
  112. package/agent/progress.template.yaml +161 -0
  113. package/agent/progress.yaml +145 -0
  114. package/agent/schemas/package.schema.yaml +276 -0
  115. package/agent/scripts/acp.common.sh +1781 -0
  116. package/agent/scripts/acp.install.sh +333 -0
  117. package/agent/scripts/acp.package-create.sh +924 -0
  118. package/agent/scripts/acp.package-info.sh +288 -0
  119. package/agent/scripts/acp.package-install.sh +893 -0
  120. package/agent/scripts/acp.package-list.sh +311 -0
  121. package/agent/scripts/acp.package-publish.sh +420 -0
  122. package/agent/scripts/acp.package-remove.sh +348 -0
  123. package/agent/scripts/acp.package-search.sh +156 -0
  124. package/agent/scripts/acp.package-update.sh +517 -0
  125. package/agent/scripts/acp.package-validate.sh +1018 -0
  126. package/agent/scripts/acp.uninstall.sh +85 -0
  127. package/agent/scripts/acp.version-check-for-updates.sh +98 -0
  128. package/agent/scripts/acp.version-check.sh +47 -0
  129. package/agent/scripts/acp.version-update.sh +176 -0
  130. package/agent/scripts/acp.yaml-parser.sh +985 -0
  131. package/agent/scripts/acp.yaml-validate.sh +205 -0
  132. package/agent/tasks/.gitkeep +0 -0
  133. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-1-initialize-tanstack-start-project.md +210 -0
  134. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-2-implement-data-model-yaml-parser.md +294 -0
  135. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-3-build-server-api-data-loading.md +193 -0
  136. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-4-add-auto-refresh-sse.md +262 -0
  137. package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md +156 -0
  138. package/agent/tasks/milestone-2-dashboard-views-interaction/task-5-build-dashboard-layout-routing.md +178 -0
  139. package/agent/tasks/milestone-2-dashboard-views-interaction/task-6-build-overview-page.md +141 -0
  140. package/agent/tasks/milestone-2-dashboard-views-interaction/task-7-implement-milestone-table-view.md +153 -0
  141. package/agent/tasks/milestone-2-dashboard-views-interaction/task-8-implement-milestone-tree-view.md +174 -0
  142. package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md +233 -0
  143. package/agent/tasks/task-1-{title}.template.md +244 -0
  144. package/bin/visualize.mjs +84 -0
  145. package/package.json +48 -0
  146. package/src/components/ExtraFieldsBadge.tsx +15 -0
  147. package/src/components/FilterBar.tsx +33 -0
  148. package/src/components/Header.tsx +23 -0
  149. package/src/components/MilestoneTable.tsx +167 -0
  150. package/src/components/MilestoneTree.tsx +84 -0
  151. package/src/components/ProgressBar.tsx +20 -0
  152. package/src/components/SearchInput.tsx +22 -0
  153. package/src/components/Sidebar.tsx +54 -0
  154. package/src/components/StatusBadge.tsx +23 -0
  155. package/src/components/StatusDot.tsx +12 -0
  156. package/src/components/TaskList.tsx +36 -0
  157. package/src/components/ViewToggle.tsx +31 -0
  158. package/src/lib/config.ts +8 -0
  159. package/src/lib/file-watcher.ts +43 -0
  160. package/src/lib/search.ts +48 -0
  161. package/src/lib/types.ts +73 -0
  162. package/src/lib/useAutoRefresh.ts +31 -0
  163. package/src/lib/useCollapse.ts +31 -0
  164. package/src/lib/useFilteredData.ts +55 -0
  165. package/src/lib/yaml-loader-real.spec.ts +47 -0
  166. package/src/lib/yaml-loader.spec.ts +201 -0
  167. package/src/lib/yaml-loader.ts +265 -0
  168. package/src/routeTree.gen.ts +140 -0
  169. package/src/router.tsx +10 -0
  170. package/src/routes/__root.tsx +75 -0
  171. package/src/routes/api/watch.ts +29 -0
  172. package/src/routes/index.tsx +115 -0
  173. package/src/routes/milestones.tsx +50 -0
  174. package/src/routes/search.tsx +84 -0
  175. package/src/routes/tasks.tsx +63 -0
  176. package/src/services/progress-database.service.ts +46 -0
  177. package/src/styles.css +25 -0
  178. package/tsconfig.json +24 -0
  179. package/vite.config.ts +16 -0
  180. package/vitest.config.ts +27 -0
@@ -0,0 +1,571 @@
1
+ # SSR Data Preloading Pattern
2
+
3
+ **Category**: Architecture
4
+ **Applicable To**: TanStack Start + Cloudflare Workers applications requiring server-side rendering
5
+ **Status**: Stable
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ This pattern demonstrates how to preload data on the server using TanStack Router's `beforeLoad` hook, pass it through route context, and hydrate components with SSR data to eliminate loading flashes. By fetching data server-side before the page renders, you provide instant content to users and search engines while maintaining the ability to add real-time updates via WebSockets.
12
+
13
+ The pattern ensures that users never see loading spinners on initial page load, improving perceived performance and providing better SEO since search engines can index the fully-rendered content.
14
+
15
+ ---
16
+
17
+ ## When to Use This Pattern
18
+
19
+ ✅ **Use this pattern when:**
20
+ - Building TanStack Start applications with Cloudflare Workers
21
+ - Any data can be fetched server-side (Firestore, database, API)
22
+ - Working with user-specific data (we use server-side auth, never client-side)
23
+ - Need to eliminate loading spinners on initial page load
24
+ - Want better SEO (search engines can index the content)
25
+ - Improving perceived performance is important
26
+ - Working with real-time data (SSR provides initial state, WebSocket adds updates)
27
+
28
+ ❌ **Don't use this pattern when:**
29
+ - Data is too large (>500KB) and would slow down SSR
30
+ - Data fetch is extremely slow (>3 seconds) and would block page load
31
+ - Data is purely client-side (localStorage, IndexedDB)
32
+ - Building static pages with no dynamic data
33
+
34
+ **Note**: We ALWAYS use server-side auth (`getAuthSession`), never client-side. Real-time listeners are attached AFTER SSR hydration.
35
+
36
+ ---
37
+
38
+ ## Core Principles
39
+
40
+ 1. **Server-First Data Loading**: Fetch data on the server before rendering, not after
41
+ 2. **beforeLoad Over loader**: Use `beforeLoad` (not `loader`) for SSR data preloading in TanStack Start
42
+ 3. **Route Context for Data**: Pass preloaded data through route context, not loader data
43
+ 4. **Graceful Degradation**: Handle errors gracefully - don't fail page load if data fetch fails
44
+ 5. **Skip Client Fetch**: Components check for SSR data and skip client-side fetch if present
45
+ 6. **WebSocket After Hydration**: Real-time listeners attach after SSR hydration completes
46
+
47
+ ---
48
+
49
+ ## Implementation
50
+
51
+ ### Structure
52
+
53
+ ```
54
+ src/
55
+ ├── routes/
56
+ │ └── your-route.tsx # Route with beforeLoad
57
+ ├── components/
58
+ │ └── YourComponent.tsx # Component using initialData
59
+ └── services/
60
+ └── your-database.service.ts # Database service for SSR
61
+ ```
62
+
63
+ ### Code Example
64
+
65
+ #### Step 1: Route Configuration with beforeLoad
66
+
67
+ ```typescript
68
+ // src/routes/your-route.tsx
69
+ import { createFileRoute, redirect } from '@tanstack/react-router'
70
+ import { getAuthSession } from '@/lib/auth/server-fn'
71
+ import { YourDatabaseService } from '@/services/your-database.service'
72
+ import type { YourDataType } from '@/types/your-types'
73
+
74
+ export const Route = createFileRoute('/your-route')({
75
+ beforeLoad: async () => {
76
+ // 1. Check authentication (if needed)
77
+ const user = await getAuthSession()
78
+
79
+ if (!user) {
80
+ throw redirect({
81
+ to: '/auth',
82
+ search: { redirect_url: '/your-route' },
83
+ })
84
+ }
85
+
86
+ // 2. Preload data with proper typing
87
+ let initialData: YourDataType[] = []
88
+
89
+ try {
90
+ initialData = await YourDatabaseService.getData(user.uid, 50)
91
+ } catch (error) {
92
+ console.error('Failed to preload data:', error)
93
+ // Continue with empty data - not fatal
94
+ }
95
+
96
+ // 3. Return data through context
97
+ return {
98
+ user,
99
+ initialData
100
+ }
101
+ },
102
+ component: YourComponent,
103
+ })
104
+ ```
105
+
106
+ #### Step 2: Component Data Access
107
+
108
+ ```typescript
109
+ // src/routes/your-route.tsx (continued)
110
+ function YourComponent() {
111
+ // Get data from route context (NOT useLoaderData)
112
+ const context = Route.useRouteContext()
113
+ const { user, initialData } = context
114
+
115
+ return (
116
+ <div>
117
+ <YourChildComponent initialData={initialData} />
118
+ </div>
119
+ )
120
+ }
121
+ ```
122
+
123
+ #### Step 3: Child Component Integration
124
+
125
+ ```typescript
126
+ // src/components/YourChildComponent.tsx
127
+ interface YourChildComponentProps {
128
+ initialData?: YourDataType[] // SSR data
129
+ className?: string
130
+ }
131
+
132
+ export function YourChildComponent({
133
+ initialData = [], // Default to empty array
134
+ className
135
+ }: YourChildComponentProps) {
136
+ const [data, setData] = useState<YourDataType[]>(initialData) // Initialize with SSR data
137
+
138
+ useEffect(() => {
139
+ // Skip loading if we have SSR data
140
+ if (initialData.length > 0) {
141
+ console.log('Using SSR data, skipping client fetch')
142
+ return
143
+ }
144
+
145
+ // Only load if no SSR data
146
+ loadData()
147
+ }, [initialData.length])
148
+
149
+ // Component renders immediately with SSR data!
150
+ return (
151
+ <div className={className}>
152
+ {data.map(item => (
153
+ <div key={item.id}>{item.name}</div>
154
+ ))}
155
+ </div>
156
+ )
157
+ }
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Examples
163
+
164
+ ### Example 1: Chat Messages with SSR
165
+
166
+ ```typescript
167
+ // src/routes/chat.tsx
168
+ import { createFileRoute, redirect } from '@tanstack/react-router'
169
+ import { getAuthSession } from '@/lib/auth/server-fn'
170
+ import { ConversationDatabaseService } from '@/services/conversation-database.service'
171
+ import type { Message } from '@/types/chat'
172
+
173
+ export const Route = createFileRoute('/chat')({
174
+ beforeLoad: async () => {
175
+ const user = await getAuthSession()
176
+ if (!user) throw redirect({ to: '/auth' })
177
+
178
+ const conversationId = 'main'
179
+ let initialMessages: Message[] = []
180
+
181
+ try {
182
+ initialMessages = await ConversationDatabaseService.getMessages(
183
+ user.uid,
184
+ conversationId,
185
+ 50
186
+ )
187
+ } catch (error) {
188
+ console.error('Failed to preload messages:', error)
189
+ }
190
+
191
+ return { user, conversationId, initialMessages }
192
+ },
193
+ component: Chat,
194
+ })
195
+
196
+ function Chat() {
197
+ const { user, conversationId, initialMessages } = Route.useRouteContext()
198
+
199
+ return (
200
+ <ChatInterface
201
+ conversationId={conversationId}
202
+ initialMessages={initialMessages}
203
+ />
204
+ )
205
+ }
206
+ ```
207
+
208
+ ### Example 2: WebSocket Integration with SSR
209
+
210
+ ```typescript
211
+ // src/components/chat/ChatInterface.tsx
212
+ interface ChatInterfaceProps {
213
+ conversationId?: string
214
+ initialMessages?: Message[]
215
+ }
216
+
217
+ export function ChatInterface({
218
+ conversationId,
219
+ initialMessages = [],
220
+ }: ChatInterfaceProps) {
221
+ const [messages, setMessages] = useState<Message[]>(initialMessages)
222
+
223
+ useEffect(() => {
224
+ const wsClient = new ChatWebSocket({
225
+ onConnectionChange: (isConnected) => {
226
+ // Skip loading if we have SSR data
227
+ if (initialMessages.length > 0) {
228
+ console.log('Skipping WebSocket load - using SSR data')
229
+ return
230
+ }
231
+
232
+ // Load data via WebSocket if no SSR data
233
+ if (isConnected) {
234
+ wsClient.loadMessages(conversationId)
235
+ }
236
+ },
237
+ onMessageReceived: (newMessage) => {
238
+ // Add new messages from WebSocket
239
+ setMessages(prev => [...prev, newMessage])
240
+ }
241
+ })
242
+
243
+ return () => wsClient.disconnect()
244
+ }, [initialMessages.length])
245
+
246
+ // Component renders immediately with SSR data!
247
+ return (
248
+ <div>
249
+ {messages.map(msg => (
250
+ <div key={msg.id}>{msg.content}</div>
251
+ ))}
252
+ </div>
253
+ )
254
+ }
255
+ ```
256
+
257
+ ### Example 3: User Profile with SSR
258
+
259
+ ```typescript
260
+ // src/routes/profile.tsx
261
+ export const Route = createFileRoute('/profile')({
262
+ beforeLoad: async () => {
263
+ const user = await getAuthSession()
264
+ if (!user) throw redirect({ to: '/auth' })
265
+
266
+ let profile = null
267
+
268
+ try {
269
+ profile = await UserDatabaseService.getProfile(user.uid)
270
+ } catch (error) {
271
+ console.error('Failed to load profile:', error)
272
+ }
273
+
274
+ return { user, profile }
275
+ },
276
+ component: Profile,
277
+ })
278
+
279
+ function Profile() {
280
+ const { user, profile } = Route.useRouteContext()
281
+
282
+ return (
283
+ <ProfileView initialProfile={profile} />
284
+ )
285
+ }
286
+ ```
287
+
288
+ ---
289
+
290
+ ## Benefits
291
+
292
+ ### 1. Instant Content Display
293
+
294
+ Users see content immediately without loading spinners. The page is fully rendered on the server with real data.
295
+
296
+ **Before SSR**: Page loads → Show spinner → Fetch data → Render data (2-3 seconds)
297
+ **After SSR**: Page loads → Data already rendered (<1 second)
298
+
299
+ ### 2. Better SEO
300
+
301
+ Search engines can index the fully-rendered content since data is present in the initial HTML.
302
+
303
+ ### 3. Improved Perceived Performance
304
+
305
+ Users perceive the application as faster because they see content immediately, even if real-time updates take a moment to connect.
306
+
307
+ ### 4. Reduced Client-Side Complexity
308
+
309
+ Components don't need complex loading states for initial data - they start with data already present.
310
+
311
+ ---
312
+
313
+ ## Trade-offs
314
+
315
+ ### 1. Server-Side Execution Time
316
+
317
+ **Downside**: Data fetching happens on the server, which can slow down initial page load if queries are slow.
318
+
319
+ **Mitigation**:
320
+ - Set timeouts on data fetches (fail gracefully)
321
+ - Only preload essential data
322
+ - Use caching where appropriate
323
+ - Consider skipping SSR for very slow queries
324
+
325
+ ### 2. Increased Server Load
326
+
327
+ **Downside**: Every page request executes server-side data fetching, increasing server resource usage.
328
+
329
+ **Mitigation**:
330
+ - Use Cloudflare Workers' edge caching
331
+ - Implement query result caching
332
+ - Monitor server performance
333
+ - Scale horizontally if needed
334
+
335
+ ---
336
+
337
+ ## Anti-Patterns
338
+
339
+ ### ❌ Anti-Pattern 1: Using loader Instead of beforeLoad
340
+
341
+ **Description**: Using TanStack Router's `loader` instead of `beforeLoad` for SSR data preloading.
342
+
343
+ **Why it's bad**: May not work correctly in TanStack Start setup, inconsistent with project patterns.
344
+
345
+ **Instead, do this**: Use `beforeLoad` for SSR data preloading.
346
+
347
+ ```typescript
348
+ // ❌ Bad: Using loader
349
+ export const Route = createFileRoute('/route')({
350
+ loader: async () => {
351
+ return { data: await fetchData() }
352
+ }
353
+ })
354
+
355
+ // ✅ Good: Using beforeLoad
356
+ export const Route = createFileRoute('/route')({
357
+ beforeLoad: async () => {
358
+ return { data: await fetchData() }
359
+ }
360
+ })
361
+ ```
362
+
363
+ ### ❌ Anti-Pattern 2: Forgetting Type Annotation
364
+
365
+ **Description**: Not providing explicit type annotation for initialData variable.
366
+
367
+ **Why it's bad**: TypeScript can't infer the type, leading to `any[]` and loss of type safety.
368
+
369
+ **Instead, do this**: Always provide explicit type annotation.
370
+
371
+ ```typescript
372
+ // ❌ Bad: Implicit any[]
373
+ let initialData = []
374
+
375
+ // ✅ Good: Explicit type
376
+ let initialData: Message[] = []
377
+ ```
378
+
379
+ ### ❌ Anti-Pattern 3: Always Fetching on Client
380
+
381
+ **Description**: Component always fetches data on mount, ignoring SSR data.
382
+
383
+ **Why it's bad**: Wastes the SSR data, causes unnecessary re-renders, shows loading state unnecessarily.
384
+
385
+ **Instead, do this**: Check for SSR data first.
386
+
387
+ ```typescript
388
+ // ❌ Bad: Always fetching
389
+ useEffect(() => {
390
+ loadData() // Always runs, ignores SSR data
391
+ }, [])
392
+
393
+ // ✅ Good: Check for SSR data
394
+ useEffect(() => {
395
+ if (initialData.length > 0) return // Skip if SSR data exists
396
+ loadData()
397
+ }, [initialData.length])
398
+ ```
399
+
400
+ ### ❌ Anti-Pattern 4: Failing Page Load on Data Error
401
+
402
+ **Description**: Throwing errors in `beforeLoad` when data fetch fails.
403
+
404
+ **Why it's bad**: Prevents page from loading at all, poor user experience.
405
+
406
+ **Instead, do this**: Handle errors gracefully and continue with empty data.
407
+
408
+ ```typescript
409
+ // ❌ Bad: Throwing on error
410
+ beforeLoad: async () => {
411
+ const data = await fetchData() // Throws if fails
412
+ return { data }
413
+ }
414
+
415
+ // ✅ Good: Graceful error handling
416
+ beforeLoad: async () => {
417
+ let data = []
418
+ try {
419
+ data = await fetchData()
420
+ } catch (error) {
421
+ console.error('Failed to preload:', error)
422
+ // Continue with empty data
423
+ }
424
+ return { data }
425
+ }
426
+ ```
427
+
428
+ ---
429
+
430
+ ## Testing Strategy
431
+
432
+ ### Unit Testing Components with SSR Data
433
+
434
+ ```typescript
435
+ import { render, screen } from '@testing-library/react'
436
+ import { YourComponent } from './YourComponent'
437
+
438
+ describe('YourComponent with SSR', () => {
439
+ it('should render with SSR data', () => {
440
+ const initialData = [
441
+ { id: '1', name: 'Item 1' },
442
+ { id: '2', name: 'Item 2' },
443
+ ]
444
+
445
+ render(<YourComponent initialData={initialData} />)
446
+
447
+ expect(screen.getByText('Item 1')).toBeInTheDocument()
448
+ expect(screen.getByText('Item 2')).toBeInTheDocument()
449
+ })
450
+
451
+ it('should not fetch data when SSR data provided', () => {
452
+ const loadDataSpy = jest.fn()
453
+ const initialData = [{ id: '1', name: 'Item 1' }]
454
+
455
+ render(<YourComponent initialData={initialData} />)
456
+
457
+ expect(loadDataSpy).not.toHaveBeenCalled()
458
+ })
459
+ })
460
+ ```
461
+
462
+ ### Testing SSR with JavaScript Disabled
463
+
464
+ 1. Disable JavaScript in browser
465
+ 2. Navigate to route
466
+ 3. Data should still render (proves SSR works)
467
+
468
+ ### Testing Hydration
469
+
470
+ 1. Enable JavaScript
471
+ 2. Check console for "Using SSR data" log
472
+ 3. Verify no loading spinner appears
473
+ 4. Verify no duplicate data fetches
474
+
475
+ ---
476
+
477
+ ## Related Patterns
478
+
479
+ - **[Library Services Pattern](./tanstack-cloudflare.library-services.md)**: Database services are used in `beforeLoad` for server-side data fetching
480
+ - **[User-Scoped Collections](./tanstack-cloudflare.user-scoped-collections.md)**: SSR preloading works with user-scoped Firestore collections
481
+
482
+ ---
483
+
484
+ ## Migration Guide
485
+
486
+ ### Step 1: Identify Client-Side Data Fetching
487
+
488
+ Find components that fetch data in `useEffect`:
489
+
490
+ ```typescript
491
+ // Current pattern
492
+ useEffect(() => {
493
+ fetchData().then(setData)
494
+ }, [])
495
+ ```
496
+
497
+ ### Step 2: Add beforeLoad to Route
498
+
499
+ ```typescript
500
+ // Add to route file
501
+ export const Route = createFileRoute('/route')({
502
+ beforeLoad: async () => {
503
+ let initialData = []
504
+ try {
505
+ initialData = await YourDatabaseService.getData()
506
+ } catch (error) {
507
+ console.error('Preload failed:', error)
508
+ }
509
+ return { initialData }
510
+ },
511
+ component: YourComponent,
512
+ })
513
+ ```
514
+
515
+ ### Step 3: Update Component to Accept initialData
516
+
517
+ ```typescript
518
+ // Update component props
519
+ interface Props {
520
+ initialData?: DataType[]
521
+ }
522
+
523
+ export function YourComponent({ initialData = [] }: Props) {
524
+ const [data, setData] = useState(initialData)
525
+
526
+ useEffect(() => {
527
+ if (initialData.length > 0) return
528
+ fetchData().then(setData)
529
+ }, [initialData.length])
530
+ }
531
+ ```
532
+
533
+ ### Step 4: Pass Data from Route
534
+
535
+ ```typescript
536
+ function YourComponent() {
537
+ const { initialData } = Route.useRouteContext()
538
+ return <YourChildComponent initialData={initialData} />
539
+ }
540
+ ```
541
+
542
+ ---
543
+
544
+ ## References
545
+
546
+ - [TanStack Router Documentation](https://tanstack.com/router/latest)
547
+ - [TanStack Start Documentation](https://tanstack.com/start/latest)
548
+ - [Cloudflare Workers](https://developers.cloudflare.com/workers/)
549
+ - [Server-Side Rendering Best Practices](https://web.dev/rendering-on-the-web/)
550
+
551
+ ---
552
+
553
+ ## Checklist for Implementation
554
+
555
+ - [ ] Use `beforeLoad` (not `loader`)
556
+ - [ ] Add proper TypeScript types for initialData
557
+ - [ ] Handle errors gracefully (don't fail page load)
558
+ - [ ] Pass data through route context
559
+ - [ ] Access with `Route.useRouteContext()`
560
+ - [ ] Initialize component state with SSR data
561
+ - [ ] Skip client-side fetch if SSR data exists
562
+ - [ ] Test with JavaScript disabled
563
+ - [ ] Test hydration (no flash/re-render)
564
+ - [ ] Test real-time updates still work
565
+
566
+ ---
567
+
568
+ **Status**: Stable - Proven pattern for TanStack Start + Cloudflare Workers
569
+ **Recommendation**: Use for all routes that display user-specific data
570
+ **Last Updated**: 2026-02-21
571
+ **Contributors**: Patrick Michaelsen