kaddidlehopper 0.5.0 → 0.6.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 (296) hide show
  1. package/dist/cli.js +104 -208
  2. package/dist/index.js +1 -31
  3. package/dist/types/cli.d.ts +1 -8
  4. package/dist/types/types.d.ts +7 -12
  5. package/package.json +1 -2
  6. package/src/cli.ts +129 -271
  7. package/src/index.ts +1 -47
  8. package/src/types.ts +7 -13
  9. package/CONTEXT.md +0 -139
  10. package/add-ons/ai/README.md +0 -34
  11. package/add-ons/ai/assets/AGENTS.md.append +0 -24
  12. package/add-ons/ai/assets/_dot_env.local.append +0 -13
  13. package/add-ons/ai/assets/src/components/AIAssistant.tsx +0 -149
  14. package/add-ons/ai/assets/src/lib/ai-hook.ts +0 -21
  15. package/add-ons/ai/assets/src/lib/weather-tools.ts +0 -30
  16. package/add-ons/ai/assets/src/routes/api.chat.ts +0 -94
  17. package/add-ons/ai/assets/src/routes/chat.css +0 -175
  18. package/add-ons/ai/assets/src/routes/chat.tsx +0 -141
  19. package/add-ons/ai/info.json +0 -22
  20. package/add-ons/ai/package.json +0 -17
  21. package/add-ons/ai/small-logo.svg +0 -8
  22. package/add-ons/db/assets/AGENTS.md.append +0 -22
  23. package/add-ons/db/assets/DB-SETUP.md +0 -65
  24. package/add-ons/db/assets/_dot_env.local.append +0 -2
  25. package/add-ons/db/assets/drizzle.config.ts +0 -10
  26. package/add-ons/db/assets/src/db/index.ts +0 -8
  27. package/add-ons/db/assets/src/db/schema.ts +0 -8
  28. package/add-ons/db/assets/src/routes/db-example.tsx +0 -118
  29. package/add-ons/db/assets/src/server/guestbook.functions.ts +0 -23
  30. package/add-ons/db/info.json +0 -22
  31. package/add-ons/db/package.json +0 -10
  32. package/add-ons/forms/assets/AGENTS.md.append +0 -13
  33. package/add-ons/forms/assets/public/form-example.html +0 -14
  34. package/add-ons/forms/assets/src/routes/form-example.tsx +0 -76
  35. package/add-ons/forms/info.json +0 -18
  36. package/add-ons/forms/package.json +0 -3
  37. package/examples/ai-chat/README.md +0 -36
  38. package/examples/ai-chat/assets/AGENTS.md.append +0 -24
  39. package/examples/ai-chat/assets/src/lib/ai-hook.ts +0 -21
  40. package/examples/ai-chat/assets/src/lib/weather-tools.ts +0 -30
  41. package/examples/ai-chat/assets/src/routes/__root.tsx +0 -57
  42. package/examples/ai-chat/assets/src/routes/api.chat.ts +0 -94
  43. package/examples/ai-chat/assets/src/routes/index.tsx +0 -141
  44. package/examples/ai-chat/assets/src/styles.css +0 -165
  45. package/examples/ai-chat/info.json +0 -25
  46. package/examples/ai-chat/package.json +0 -14
  47. package/examples/blog/README.md +0 -60
  48. package/examples/blog/assets/AGENTS.md.append +0 -18
  49. package/examples/blog/assets/content/posts/beach.md +0 -12
  50. package/examples/blog/assets/content/posts/jungle.md.ejs +0 -12
  51. package/examples/blog/assets/content/posts/mountains.md.ejs +0 -12
  52. package/examples/blog/assets/content/posts/snorkeling.md.ejs +0 -12
  53. package/examples/blog/assets/content/posts/waterfall.md.ejs +0 -12
  54. package/examples/blog/assets/content-collections.ts +0 -30
  55. package/examples/blog/assets/public/beach.jpg +0 -0
  56. package/examples/blog/assets/public/jungle.jpg +0 -0
  57. package/examples/blog/assets/public/mountains.jpg +0 -0
  58. package/examples/blog/assets/public/snorkeling.jpg +0 -0
  59. package/examples/blog/assets/public/waterfall.jpg +0 -0
  60. package/examples/blog/assets/src/components/Header.tsx +0 -52
  61. package/examples/blog/assets/src/components/VacayAssistant.tsx +0 -205
  62. package/examples/blog/assets/src/components/blog-posts.tsx +0 -78
  63. package/examples/blog/assets/src/components/ui/card.tsx +0 -92
  64. package/examples/blog/assets/src/lib/blog-ai-hook.ts +0 -25
  65. package/examples/blog/assets/src/lib/blog-tools.ts +0 -111
  66. package/examples/blog/assets/src/lib/utils.ts +0 -6
  67. package/examples/blog/assets/src/routes/__root.tsx +0 -57
  68. package/examples/blog/assets/src/routes/api.blog-chat.ts +0 -117
  69. package/examples/blog/assets/src/routes/category.$category.tsx +0 -19
  70. package/examples/blog/assets/src/routes/index.tsx +0 -19
  71. package/examples/blog/assets/src/routes/posts.$slug.tsx +0 -63
  72. package/examples/blog/assets/src/styles.css +0 -138
  73. package/examples/blog/info.json +0 -40
  74. package/examples/blog/package.json +0 -23
  75. package/examples/calculator/README.md +0 -16
  76. package/examples/calculator/assets/AGENTS.md.append +0 -9
  77. package/examples/calculator/assets/src/components/Calculator.tsx +0 -238
  78. package/examples/calculator/assets/src/components/Header.tsx +0 -17
  79. package/examples/calculator/assets/src/routes/__root.tsx +0 -57
  80. package/examples/calculator/assets/src/routes/index.tsx +0 -14
  81. package/examples/calculator/info.json +0 -20
  82. package/examples/calculator/package.json +0 -1
  83. package/examples/dashboard/README.md +0 -17
  84. package/examples/dashboard/assets/AGENTS.md.append +0 -12
  85. package/examples/dashboard/assets/src/components/Header.tsx +0 -17
  86. package/examples/dashboard/assets/src/routes/__root.tsx +0 -57
  87. package/examples/dashboard/assets/src/routes/index.tsx +0 -158
  88. package/examples/dashboard/info.json +0 -19
  89. package/examples/dashboard/package.json +0 -6
  90. package/examples/ecommerce/README.md +0 -48
  91. package/examples/ecommerce/assets/AGENTS.md.append +0 -22
  92. package/examples/ecommerce/assets/public/logo.png +0 -0
  93. package/examples/ecommerce/assets/public/motorcycle-scooter.jpg +0 -0
  94. package/examples/ecommerce/assets/src/components/BuyButton.tsx +0 -35
  95. package/examples/ecommerce/assets/src/components/Header.tsx +0 -36
  96. package/examples/ecommerce/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  97. package/examples/ecommerce/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  98. package/examples/ecommerce/assets/src/data/motorcycles.ts +0 -27
  99. package/examples/ecommerce/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  100. package/examples/ecommerce/assets/src/lib/motorcycle-tools.ts +0 -42
  101. package/examples/ecommerce/assets/src/lib/stripe.server.ts +0 -39
  102. package/examples/ecommerce/assets/src/routes/__root.tsx +0 -57
  103. package/examples/ecommerce/assets/src/routes/api.motorcycle-chat.ts +0 -78
  104. package/examples/ecommerce/assets/src/routes/checkout/cancel.tsx +0 -25
  105. package/examples/ecommerce/assets/src/routes/checkout/success.tsx +0 -25
  106. package/examples/ecommerce/assets/src/routes/index.tsx +0 -76
  107. package/examples/ecommerce/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -55
  108. package/examples/ecommerce/assets/src/store/motorcycle-assistant.ts +0 -3
  109. package/examples/ecommerce/assets/src/styles.css +0 -212
  110. package/examples/ecommerce/info.json +0 -38
  111. package/examples/ecommerce/package.json +0 -13
  112. package/examples/events/README.md +0 -110
  113. package/examples/events/assets/AGENTS.md.append +0 -21
  114. package/examples/events/assets/content/speakers/andre-costa.md +0 -22
  115. package/examples/events/assets/content/speakers/hans-mueller.md.ejs +0 -22
  116. package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +0 -22
  117. package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +0 -22
  118. package/examples/events/assets/content/speakers/marie-dubois.md.ejs +0 -20
  119. package/examples/events/assets/content/speakers/priya-sharma.md.ejs +0 -22
  120. package/examples/events/assets/content/talks/croissant-lamination-secrets.md +0 -39
  121. package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +0 -39
  122. package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +0 -39
  123. package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +0 -39
  124. package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +0 -36
  125. package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +0 -32
  126. package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +0 -39
  127. package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +0 -39
  128. package/examples/events/assets/content-collections.ts +0 -56
  129. package/examples/events/assets/public/background-1.jpg +0 -0
  130. package/examples/events/assets/public/background-2.jpg +0 -0
  131. package/examples/events/assets/public/background-3.jpg +0 -0
  132. package/examples/events/assets/public/background-4.jpg +0 -0
  133. package/examples/events/assets/public/conference-logo.png +0 -0
  134. package/examples/events/assets/public/favicon.ico +0 -0
  135. package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
  136. package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
  137. package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
  138. package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
  139. package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
  140. package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
  141. package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
  142. package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
  143. package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
  144. package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
  145. package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
  146. package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
  147. package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
  148. package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
  149. package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
  150. package/examples/events/assets/public/tanstack-word-logo-white.svg +0 -1
  151. package/examples/events/assets/src/components/Header.tsx +0 -59
  152. package/examples/events/assets/src/components/HeaderNav.tsx +0 -67
  153. package/examples/events/assets/src/components/HeroCarousel.tsx +0 -61
  154. package/examples/events/assets/src/components/RemyAssistant.tsx +0 -207
  155. package/examples/events/assets/src/components/SpeakerCard.tsx +0 -67
  156. package/examples/events/assets/src/components/TalkCard.tsx +0 -77
  157. package/examples/events/assets/src/components/ui/card.tsx +0 -92
  158. package/examples/events/assets/src/lib/conference-ai-hook.ts +0 -26
  159. package/examples/events/assets/src/lib/conference-tools.ts +0 -210
  160. package/examples/events/assets/src/lib/model-selection.ts +0 -1
  161. package/examples/events/assets/src/lib/utils.ts +0 -6
  162. package/examples/events/assets/src/routes/__root.tsx +0 -70
  163. package/examples/events/assets/src/routes/api.remy-chat.ts +0 -119
  164. package/examples/events/assets/src/routes/index.tsx +0 -192
  165. package/examples/events/assets/src/routes/schedule.index.tsx +0 -274
  166. package/examples/events/assets/src/routes/speakers.$slug.tsx +0 -122
  167. package/examples/events/assets/src/routes/speakers.index.tsx +0 -40
  168. package/examples/events/assets/src/routes/talks.$slug.tsx +0 -116
  169. package/examples/events/assets/src/routes/talks.index.tsx +0 -40
  170. package/examples/events/assets/src/styles.css +0 -182
  171. package/examples/events/info.json +0 -61
  172. package/examples/events/package.json +0 -23
  173. package/examples/marketing/README.md +0 -60
  174. package/examples/marketing/assets/AGENTS.md.append +0 -15
  175. package/examples/marketing/assets/public/logo.png +0 -0
  176. package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
  177. package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
  178. package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
  179. package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
  180. package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
  181. package/examples/marketing/assets/src/components/Header.tsx +0 -36
  182. package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  183. package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  184. package/examples/marketing/assets/src/data/motorcycles.ts.ejs +0 -77
  185. package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  186. package/examples/marketing/assets/src/lib/motorcycle-tools.ts +0 -42
  187. package/examples/marketing/assets/src/routes/__root.tsx +0 -57
  188. package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +0 -78
  189. package/examples/marketing/assets/src/routes/index.tsx +0 -72
  190. package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -56
  191. package/examples/marketing/assets/src/store/motorcycle-assistant.ts +0 -3
  192. package/examples/marketing/assets/src/styles.css +0 -212
  193. package/examples/marketing/info.json +0 -33
  194. package/examples/marketing/package.json +0 -14
  195. package/examples/portfolio/README.md +0 -49
  196. package/examples/portfolio/assets/AGENTS.md.append +0 -21
  197. package/examples/portfolio/assets/content/blog/getting-started-with-tanstack.md +0 -53
  198. package/examples/portfolio/assets/content/blog/react-19-features.md +0 -78
  199. package/examples/portfolio/assets/content/blog/tailwind-css-v4-guide.md +0 -60
  200. package/examples/portfolio/assets/content/education/code-school.md +0 -17
  201. package/examples/portfolio/assets/content/jobs/initech-junior.md +0 -20
  202. package/examples/portfolio/assets/content/projects/portfolio-site.md +0 -15
  203. package/examples/portfolio/assets/content/projects/task-manager.md +0 -15
  204. package/examples/portfolio/assets/content-collections.ts +0 -65
  205. package/examples/portfolio/assets/public/contact.html +0 -6
  206. package/examples/portfolio/assets/public/headshot-on-white.jpg +0 -0
  207. package/examples/portfolio/assets/src/components/Header.tsx +0 -33
  208. package/examples/portfolio/assets/src/components/ResumeAssistant.tsx +0 -193
  209. package/examples/portfolio/assets/src/components/ui/badge.tsx +0 -46
  210. package/examples/portfolio/assets/src/components/ui/card.tsx +0 -92
  211. package/examples/portfolio/assets/src/components/ui/checkbox.tsx +0 -30
  212. package/examples/portfolio/assets/src/components/ui/hover-card.tsx +0 -44
  213. package/examples/portfolio/assets/src/components/ui/separator.tsx +0 -26
  214. package/examples/portfolio/assets/src/lib/resume-ai-hook.ts +0 -21
  215. package/examples/portfolio/assets/src/lib/resume-tools.ts +0 -165
  216. package/examples/portfolio/assets/src/lib/utils.ts +0 -6
  217. package/examples/portfolio/assets/src/routes/__root.tsx +0 -57
  218. package/examples/portfolio/assets/src/routes/api.resume-chat.ts +0 -116
  219. package/examples/portfolio/assets/src/routes/blog/$slug.tsx +0 -73
  220. package/examples/portfolio/assets/src/routes/contact.tsx +0 -121
  221. package/examples/portfolio/assets/src/routes/index.tsx +0 -55
  222. package/examples/portfolio/assets/src/routes/projects.tsx +0 -62
  223. package/examples/portfolio/assets/src/routes/resume.tsx +0 -220
  224. package/examples/portfolio/assets/src/styles.css +0 -138
  225. package/examples/portfolio/info.json +0 -50
  226. package/examples/portfolio/package.json +0 -26
  227. package/examples/resume/README.md +0 -82
  228. package/examples/resume/assets/AGENTS.md.append +0 -19
  229. package/examples/resume/assets/content/education/code-school.md +0 -17
  230. package/examples/resume/assets/content/jobs/freelance.md.ejs +0 -13
  231. package/examples/resume/assets/content/jobs/initech-junior.md +0 -20
  232. package/examples/resume/assets/content/jobs/initech-lead.md.ejs +0 -29
  233. package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +0 -28
  234. package/examples/resume/assets/content-collections.ts +0 -36
  235. package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
  236. package/examples/resume/assets/src/components/Header.tsx +0 -33
  237. package/examples/resume/assets/src/components/ResumeAssistant.tsx +0 -193
  238. package/examples/resume/assets/src/components/ui/badge.tsx +0 -46
  239. package/examples/resume/assets/src/components/ui/card.tsx +0 -92
  240. package/examples/resume/assets/src/components/ui/checkbox.tsx +0 -30
  241. package/examples/resume/assets/src/components/ui/hover-card.tsx +0 -44
  242. package/examples/resume/assets/src/components/ui/separator.tsx +0 -26
  243. package/examples/resume/assets/src/lib/resume-ai-hook.ts +0 -21
  244. package/examples/resume/assets/src/lib/resume-tools.ts +0 -165
  245. package/examples/resume/assets/src/lib/utils.ts +0 -6
  246. package/examples/resume/assets/src/routes/api.resume-chat.ts +0 -110
  247. package/examples/resume/assets/src/routes/index.tsx +0 -220
  248. package/examples/resume/assets/src/styles.css +0 -138
  249. package/examples/resume/info.json +0 -35
  250. package/examples/resume/package.json +0 -26
  251. package/examples/saas/README.md +0 -16
  252. package/examples/saas/assets/AGENTS.md.append +0 -9
  253. package/examples/saas/assets/src/components/Header.tsx +0 -17
  254. package/examples/saas/assets/src/routes/__root.tsx +0 -57
  255. package/examples/saas/assets/src/routes/faq.tsx +0 -94
  256. package/examples/saas/assets/src/routes/index.tsx +0 -197
  257. package/examples/saas/info.json +0 -23
  258. package/examples/saas/package.json +0 -1
  259. package/examples/survey/README.md +0 -20
  260. package/examples/survey/assets/AGENTS.md.append +0 -9
  261. package/examples/survey/assets/public/form-survey.html +0 -15
  262. package/examples/survey/assets/src/components/SurveyForm.tsx +0 -128
  263. package/examples/survey/assets/src/routes/index.tsx +0 -14
  264. package/examples/survey/info.json +0 -19
  265. package/examples/survey/package.json +0 -1
  266. package/project/base/AGENTS.md +0 -86
  267. package/project/base/_dot_claude/skills/content-collections/SKILL.md +0 -505
  268. package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +0 -410
  269. package/project/base/_dot_claude/skills/netlify-db/SKILL.md +0 -424
  270. package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +0 -419
  271. package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +0 -243
  272. package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +0 -372
  273. package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +0 -421
  274. package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +0 -426
  275. package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +0 -493
  276. package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +0 -430
  277. package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +0 -445
  278. package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +0 -494
  279. package/project/base/_dot_gitignore +0 -8
  280. package/project/base/netlify.toml +0 -7
  281. package/project/base/package.json +0 -33
  282. package/project/base/public/favicon.ico +0 -0
  283. package/project/base/public/tanstack-circle-logo.png +0 -0
  284. package/project/base/public/tanstack-word-logo-white.svg +0 -1
  285. package/project/base/src/components/Header.tsx +0 -17
  286. package/project/base/src/components/HeaderNav.tsx.ejs +0 -179
  287. package/project/base/src/router.tsx +0 -15
  288. package/project/base/src/routes/__root.tsx +0 -57
  289. package/project/base/src/routes/index.tsx +0 -48
  290. package/project/base/src/styles.css +0 -15
  291. package/project/base/tsconfig.json +0 -28
  292. package/project/base/vite.config.ts.ejs +0 -25
  293. package/project/info.json +0 -17
  294. package/project/packages.json +0 -22
  295. package/scripts/check-outdated-packages.js +0 -421
  296. package/src/agents-md.ts +0 -139
@@ -1,426 +0,0 @@
1
- ---
2
- name: tanstack-start-loaders
3
- description: Load data for TanStack Start routes using beforeLoad and loader functions. Use when fetching data for pages, implementing route guards, or setting up route context. IMPORTANT - Loaders should call server functions for data access.
4
- license: Apache-2.0
5
- metadata:
6
- author: tanstack
7
- version: "1.0"
8
- ---
9
-
10
- # TanStack Start Loaders
11
-
12
- TanStack Start provides `beforeLoad` and `loader` functions for route data loading. Both are **isomorphic** - they run on the server during SSR and on the client during navigation.
13
-
14
- ## Critical Rule
15
-
16
- **Loaders should call server functions when accessing databases, APIs with secrets, or any server-only resources.** Loaders are isomorphic (run on both server and client), so they cannot directly access server-only code.
17
-
18
- ```tsx
19
- // ❌ WRONG - Direct database access in loader
20
- export const Route = createFileRoute('/posts')({
21
- loader: async () => {
22
- // This will FAIL on client-side navigation!
23
- const posts = await db.query('SELECT * FROM posts');
24
- return { posts };
25
- },
26
- });
27
-
28
- // ✅ CORRECT - Call server function from loader
29
- import { getPosts } from '../server/posts.functions';
30
-
31
- export const Route = createFileRoute('/posts')({
32
- loader: async () => {
33
- const posts = await getPosts();
34
- return { posts };
35
- },
36
- });
37
- ```
38
-
39
- ## When to Use
40
-
41
- - **beforeLoad**: Route guards, authentication, setting context
42
- - **loader**: Fetching data for the route component
43
-
44
- ## beforeLoad vs loader
45
-
46
- | Feature | beforeLoad | loader |
47
- |---------|------------|--------|
48
- | Execution | Sequential (parent → child) | Parallel across routes |
49
- | Return | Merges into context | Route-specific data |
50
- | Use case | Guards, auth, context setup | Data fetching |
51
-
52
- ## beforeLoad Function
53
-
54
- Runs sequentially from parent to child. Use for guards and context:
55
-
56
- ```tsx
57
- // src/routes/_protected.tsx
58
- import { createFileRoute, redirect } from '@tanstack/react-router';
59
- import { getUser } from '../server/auth.functions';
60
-
61
- export const Route = createFileRoute('/_protected')({
62
- beforeLoad: async ({ context }) => {
63
- // Call server function - NOT direct database access
64
- const user = await getUser();
65
-
66
- if (!user) {
67
- throw redirect({
68
- to: '/login',
69
- search: { redirect: location.href },
70
- });
71
- }
72
-
73
- // Return value merges into context for child routes
74
- return { user };
75
- },
76
- component: ProtectedLayout,
77
- });
78
- ```
79
-
80
- Child routes can access parent's beforeLoad data:
81
-
82
- ```tsx
83
- // src/routes/_protected.dashboard.tsx
84
- import { createFileRoute } from '@tanstack/react-router';
85
-
86
- export const Route = createFileRoute('/_protected/dashboard')({
87
- beforeLoad: ({ context }) => {
88
- // Access user from parent's beforeLoad
89
- console.log('User:', context.user);
90
- },
91
- component: DashboardComponent,
92
- });
93
- ```
94
-
95
- ## loader Function
96
-
97
- Fetches route-specific data. Runs in parallel across matched routes:
98
-
99
- ```tsx
100
- // src/routes/posts.tsx
101
- import { createFileRoute } from '@tanstack/react-router';
102
- import { getPosts } from '../server/posts.functions';
103
-
104
- export const Route = createFileRoute('/posts')({
105
- loader: async () => {
106
- // Call server function for data
107
- const posts = await getPosts();
108
- return { posts };
109
- },
110
- component: PostsComponent,
111
- });
112
-
113
- function PostsComponent() {
114
- // Type-safe access to loader data
115
- const { posts } = Route.useLoaderData();
116
-
117
- return (
118
- <ul>
119
- {posts.map((post) => (
120
- <li key={post.id}>{post.title}</li>
121
- ))}
122
- </ul>
123
- );
124
- }
125
- ```
126
-
127
- ## Loader with Parameters
128
-
129
- ```tsx
130
- // src/routes/posts.$postId.tsx
131
- import { createFileRoute } from '@tanstack/react-router';
132
- import { getPost } from '../server/posts.functions';
133
-
134
- export const Route = createFileRoute('/posts/$postId')({
135
- loader: async ({ params }) => {
136
- const post = await getPost({ data: { id: params.postId } });
137
-
138
- if (!post) {
139
- throw new Error('Post not found');
140
- }
141
-
142
- return { post };
143
- },
144
- component: PostComponent,
145
- });
146
-
147
- function PostComponent() {
148
- const { post } = Route.useLoaderData();
149
-
150
- return (
151
- <article>
152
- <h1>{post.title}</h1>
153
- <p>{post.content}</p>
154
- </article>
155
- );
156
- }
157
- ```
158
-
159
- ## Loader Dependencies (loaderDeps)
160
-
161
- Re-run loader when search params change:
162
-
163
- ```tsx
164
- // src/routes/posts.tsx
165
- import { createFileRoute } from '@tanstack/react-router';
166
- import { searchPosts } from '../server/posts.functions';
167
-
168
- export const Route = createFileRoute('/posts')({
169
- validateSearch: (search) => ({
170
- page: Number(search.page) || 1,
171
- filter: search.filter as string | undefined,
172
- }),
173
-
174
- // Define which values trigger loader re-runs
175
- loaderDeps: ({ search }) => ({
176
- page: search.page,
177
- filter: search.filter,
178
- }),
179
-
180
- loader: async ({ deps }) => {
181
- // deps contains values from loaderDeps
182
- const { posts, total } = await searchPosts({
183
- data: { page: deps.page, filter: deps.filter }
184
- });
185
- return { posts, total, page: deps.page };
186
- },
187
-
188
- component: PostsComponent,
189
- });
190
-
191
- function PostsComponent() {
192
- const { posts, total, page } = Route.useLoaderData();
193
- const navigate = useNavigate();
194
-
195
- return (
196
- <div>
197
- <ul>
198
- {posts.map((post) => (
199
- <li key={post.id}>{post.title}</li>
200
- ))}
201
- </ul>
202
-
203
- <button
204
- onClick={() => navigate({ search: { page: page + 1 } })}
205
- >
206
- Next Page
207
- </button>
208
- </div>
209
- );
210
- }
211
- ```
212
-
213
- ## Deferred Data Loading
214
-
215
- Load critical data first, stream non-critical data:
216
-
217
- ```tsx
218
- // src/routes/posts.$postId.tsx
219
- import { createFileRoute, Await } from '@tanstack/react-router';
220
- import { getPost, getComments, getRelatedPosts } from '../server/posts.functions';
221
-
222
- export const Route = createFileRoute('/posts/$postId')({
223
- loader: async ({ params }) => {
224
- // Critical data - await it
225
- const post = await getPost({ data: { id: params.postId } });
226
-
227
- // Non-critical data - don't await, stream later
228
- const commentsPromise = getComments({ data: { postId: params.postId } });
229
- const relatedPromise = getRelatedPosts({ data: { postId: params.postId } });
230
-
231
- return {
232
- post,
233
- comments: commentsPromise, // Promise, not resolved
234
- related: relatedPromise, // Promise, not resolved
235
- };
236
- },
237
- component: PostComponent,
238
- });
239
-
240
- function PostComponent() {
241
- const { post, comments, related } = Route.useLoaderData();
242
-
243
- return (
244
- <article>
245
- <h1>{post.title}</h1>
246
- <p>{post.content}</p>
247
-
248
- {/* Comments stream in when ready */}
249
- <Suspense fallback={<p>Loading comments...</p>}>
250
- <Await promise={comments}>
251
- {(resolvedComments) => (
252
- <section>
253
- <h2>Comments</h2>
254
- {resolvedComments.map((c) => (
255
- <div key={c.id}>{c.text}</div>
256
- ))}
257
- </section>
258
- )}
259
- </Await>
260
- </Suspense>
261
-
262
- {/* Related posts stream in when ready */}
263
- <Suspense fallback={<p>Loading related...</p>}>
264
- <Await promise={related}>
265
- {(resolvedRelated) => (
266
- <aside>
267
- <h2>Related Posts</h2>
268
- <ul>
269
- {resolvedRelated.map((p) => (
270
- <li key={p.id}>{p.title}</li>
271
- ))}
272
- </ul>
273
- </aside>
274
- )}
275
- </Await>
276
- </Suspense>
277
- </article>
278
- );
279
- }
280
- ```
281
-
282
- ## Context from Router
283
-
284
- Pass initial context from router creation:
285
-
286
- ```tsx
287
- // src/router.tsx
288
- import { createRouter } from '@tanstack/react-router';
289
- import { routeTree } from './routeTree.gen';
290
-
291
- export function getRouter() {
292
- return createRouter({
293
- routeTree,
294
- context: {
295
- // Initial context available to all routes
296
- queryClient: new QueryClient(),
297
- },
298
- });
299
- }
300
- ```
301
-
302
- Access in loaders:
303
-
304
- ```tsx
305
- export const Route = createFileRoute('/posts')({
306
- loader: async ({ context }) => {
307
- // Access router context
308
- return context.queryClient.ensureQueryData(postsQueryOptions());
309
- },
310
- });
311
- ```
312
-
313
- ## Error Handling
314
-
315
- ```tsx
316
- export const Route = createFileRoute('/posts/$postId')({
317
- loader: async ({ params }) => {
318
- const post = await getPost({ data: { id: params.postId } });
319
-
320
- if (!post) {
321
- throw new Error('Post not found');
322
- }
323
-
324
- return { post };
325
- },
326
-
327
- // Custom error component
328
- errorComponent: ({ error }) => (
329
- <div>
330
- <h1>Error Loading Post</h1>
331
- <p>{error.message}</p>
332
- <Link to="/posts">Back to Posts</Link>
333
- </div>
334
- ),
335
-
336
- component: PostComponent,
337
- });
338
- ```
339
-
340
- ## Pending/Loading States
341
-
342
- ```tsx
343
- export const Route = createFileRoute('/posts')({
344
- loader: async () => {
345
- const posts = await getPosts();
346
- return { posts };
347
- },
348
-
349
- // Show while loading
350
- pendingComponent: () => <div>Loading posts...</div>,
351
-
352
- // Minimum time to show pending (prevents flash)
353
- pendingMinMs: 200,
354
-
355
- // Delay before showing pending
356
- pendingMs: 100,
357
-
358
- component: PostsComponent,
359
- });
360
- ```
361
-
362
- ## Stale Time Configuration
363
-
364
- Control when loaders re-run:
365
-
366
- ```tsx
367
- export const Route = createFileRoute('/posts')({
368
- // Data is fresh for 5 minutes
369
- staleTime: 5 * 60 * 1000,
370
-
371
- // Preload data is fresh for 30 seconds
372
- preloadStaleTime: 30 * 1000,
373
-
374
- // Garbage collect after 10 minutes
375
- gcTime: 10 * 60 * 1000,
376
-
377
- loader: async () => {
378
- return { posts: await getPosts() };
379
- },
380
- });
381
- ```
382
-
383
- ## Using with TanStack Query
384
-
385
- For complex caching needs, use TanStack Query with loaders:
386
-
387
- ```tsx
388
- import { createFileRoute } from '@tanstack/react-router';
389
- import { useSuspenseQuery, queryOptions } from '@tanstack/react-query';
390
- import { getPosts } from '../server/posts.functions';
391
-
392
- const postsQueryOptions = () => queryOptions({
393
- queryKey: ['posts'],
394
- queryFn: () => getPosts(),
395
- });
396
-
397
- export const Route = createFileRoute('/posts')({
398
- loader: async ({ context }) => {
399
- // Ensure data is in cache before rendering
400
- await context.queryClient.ensureQueryData(postsQueryOptions());
401
- },
402
- component: PostsComponent,
403
- });
404
-
405
- function PostsComponent() {
406
- // Use query hook for reactive updates
407
- const { data: posts } = useSuspenseQuery(postsQueryOptions());
408
-
409
- return (
410
- <ul>
411
- {posts.map((post) => (
412
- <li key={post.id}>{post.title}</li>
413
- ))}
414
- </ul>
415
- );
416
- }
417
- ```
418
-
419
- ## Summary
420
-
421
- 1. **Always use server functions** in loaders for server-only operations
422
- 2. **beforeLoad** for guards and context (sequential)
423
- 3. **loader** for data fetching (parallel)
424
- 4. Use **loaderDeps** to re-run on search param changes
425
- 5. **Defer** non-critical data with promises
426
- 6. Configure **staleTime** for caching behavior