@tanstack/create 0.61.5 → 0.62.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 (147) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/config-file.js +5 -2
  3. package/dist/custom-add-ons/starter.js +45 -28
  4. package/dist/file-helpers.js +1 -0
  5. package/dist/frameworks/react/add-ons/shadcn/assets/src/styles.css +224 -15
  6. package/dist/frameworks/react/add-ons/store/assets/src/lib/demo-store.ts +5 -6
  7. package/dist/frameworks/react/add-ons/store/assets/src/routes/demo/store.tsx.ejs +1 -1
  8. package/dist/frameworks/react/add-ons/store/package.json +2 -2
  9. package/dist/frameworks/react/add-ons/strapi/README.md +158 -8
  10. package/dist/frameworks/react/add-ons/strapi/assets/_dot_env.local.append +1 -1
  11. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/block-renderer.tsx +55 -0
  12. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/index.ts +14 -0
  13. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/media.tsx +27 -0
  14. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/quote.tsx +19 -0
  15. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/rich-text.tsx +11 -0
  16. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/slider.tsx +28 -0
  17. package/dist/frameworks/react/add-ons/strapi/assets/src/components/markdown-content.tsx +74 -0
  18. package/dist/frameworks/react/add-ons/strapi/assets/src/components/pagination.tsx +120 -0
  19. package/dist/frameworks/react/add-ons/strapi/assets/src/components/search.tsx +35 -0
  20. package/dist/frameworks/react/add-ons/strapi/assets/src/components/strapi-image.tsx +47 -0
  21. package/dist/frameworks/react/add-ons/strapi/assets/src/data/loaders/articles.ts +106 -0
  22. package/dist/frameworks/react/add-ons/strapi/assets/src/data/loaders/index.ts +28 -0
  23. package/dist/frameworks/react/add-ons/strapi/assets/src/data/strapi-sdk.ts +9 -0
  24. package/dist/frameworks/react/add-ons/strapi/assets/src/lib/strapi-utils.ts +25 -0
  25. package/dist/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi.$articleId.tsx +170 -0
  26. package/dist/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi.tsx +269 -43
  27. package/dist/frameworks/react/add-ons/strapi/assets/src/types/strapi.ts +90 -0
  28. package/dist/frameworks/react/add-ons/strapi/info.json +3 -3
  29. package/dist/frameworks/react/add-ons/strapi/package.json +5 -2
  30. package/dist/frameworks/react/index.js +2 -2
  31. package/dist/frameworks/react/project/base/content/blog/fifth-post.mdx.ejs +54 -0
  32. package/dist/frameworks/react/project/base/content/blog/first-post.md.ejs +47 -0
  33. package/dist/frameworks/react/project/base/content/blog/fourth-post.md.ejs +42 -0
  34. package/dist/frameworks/react/project/base/content/blog/second-post.mdx.ejs +46 -0
  35. package/dist/frameworks/react/project/base/content/blog/third-post.md.ejs +49 -0
  36. package/dist/frameworks/react/project/base/content-collections.ts.ejs +37 -0
  37. package/dist/frameworks/react/project/base/package.json +8 -1
  38. package/dist/frameworks/react/project/base/public/images/lagoon-1.svg +13 -0
  39. package/dist/frameworks/react/project/base/public/images/lagoon-2.svg +12 -0
  40. package/dist/frameworks/react/project/base/public/images/lagoon-3.svg +12 -0
  41. package/dist/frameworks/react/project/base/public/images/lagoon-4.svg +12 -0
  42. package/dist/frameworks/react/project/base/public/images/lagoon-5.svg +12 -0
  43. package/dist/frameworks/react/project/base/public/images/lagoon-about.svg +14 -0
  44. package/dist/frameworks/react/project/base/src/components/Footer.tsx.ejs +42 -0
  45. package/dist/frameworks/react/project/base/src/components/Header.tsx.ejs +92 -138
  46. package/dist/frameworks/react/project/base/src/components/MdxCallout.tsx.ejs +16 -0
  47. package/dist/frameworks/react/project/base/src/components/MdxMetrics.tsx.ejs +23 -0
  48. package/dist/frameworks/react/project/base/src/components/ThemeToggle.tsx.ejs +81 -0
  49. package/dist/frameworks/react/project/base/src/lib/site.ts.ejs +4 -0
  50. package/dist/frameworks/react/project/base/src/main.tsx.ejs +0 -1
  51. package/dist/frameworks/react/project/base/src/routes/__root.tsx.ejs +10 -6
  52. package/dist/frameworks/react/project/base/src/routes/about.tsx.ejs +27 -0
  53. package/dist/frameworks/react/project/base/src/routes/blog.$slug.tsx.ejs +71 -0
  54. package/dist/frameworks/react/project/base/src/routes/blog.index.tsx.ejs +93 -0
  55. package/dist/frameworks/react/project/base/src/routes/index.tsx.ejs +58 -91
  56. package/dist/frameworks/react/project/base/src/routes/rss[.]xml.ts.ejs +35 -0
  57. package/dist/frameworks/react/project/base/src/styles.css.ejs +268 -6
  58. package/dist/frameworks/react/project/base/tsconfig.json.ejs +2 -0
  59. package/dist/frameworks/react/project/base/vite.config.ts.ejs +2 -0
  60. package/dist/frameworks/solid/add-ons/store/assets/src/lib/demo-store.ts +5 -6
  61. package/dist/frameworks/solid/add-ons/store/assets/src/routes/demo.store.tsx.ejs +2 -2
  62. package/dist/frameworks/solid/examples/tanchat/assets/src/lib/demo-store.ts +5 -6
  63. package/dist/frameworks/solid/project/base/src/components/Header.tsx.ejs +8 -6
  64. package/dist/frameworks/solid/project/base/src/routes/__root.tsx.ejs +1 -1
  65. package/dist/frameworks/solid/project/base/src/routes/index.tsx.ejs +1 -1
  66. package/dist/frameworks.js +3 -0
  67. package/dist/package-json.js +1 -1
  68. package/dist/registry.js +21 -4
  69. package/dist/types/registry.d.ts +38 -0
  70. package/package.json +1 -1
  71. package/src/config-file.ts +6 -2
  72. package/src/custom-add-ons/starter.ts +30 -10
  73. package/src/file-helpers.ts +1 -0
  74. package/src/frameworks/react/add-ons/shadcn/assets/src/styles.css +224 -15
  75. package/src/frameworks/react/add-ons/store/assets/src/lib/demo-store.ts +5 -6
  76. package/src/frameworks/react/add-ons/store/assets/src/routes/demo/store.tsx.ejs +1 -1
  77. package/src/frameworks/react/add-ons/store/package.json +2 -2
  78. package/src/frameworks/react/add-ons/strapi/README.md +158 -8
  79. package/src/frameworks/react/add-ons/strapi/assets/_dot_env.local.append +1 -1
  80. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/block-renderer.tsx +55 -0
  81. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/index.ts +14 -0
  82. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/media.tsx +27 -0
  83. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/quote.tsx +19 -0
  84. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/rich-text.tsx +11 -0
  85. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/slider.tsx +28 -0
  86. package/src/frameworks/react/add-ons/strapi/assets/src/components/markdown-content.tsx +74 -0
  87. package/src/frameworks/react/add-ons/strapi/assets/src/components/pagination.tsx +120 -0
  88. package/src/frameworks/react/add-ons/strapi/assets/src/components/search.tsx +35 -0
  89. package/src/frameworks/react/add-ons/strapi/assets/src/components/strapi-image.tsx +47 -0
  90. package/src/frameworks/react/add-ons/strapi/assets/src/data/loaders/articles.ts +106 -0
  91. package/src/frameworks/react/add-ons/strapi/assets/src/data/loaders/index.ts +28 -0
  92. package/src/frameworks/react/add-ons/strapi/assets/src/data/strapi-sdk.ts +9 -0
  93. package/src/frameworks/react/add-ons/strapi/assets/src/lib/strapi-utils.ts +25 -0
  94. package/src/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi.$articleId.tsx +170 -0
  95. package/src/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi.tsx +269 -43
  96. package/src/frameworks/react/add-ons/strapi/assets/src/types/strapi.ts +90 -0
  97. package/src/frameworks/react/add-ons/strapi/info.json +3 -3
  98. package/src/frameworks/react/add-ons/strapi/package.json +5 -2
  99. package/src/frameworks/react/index.ts +2 -2
  100. package/src/frameworks/react/project/base/content/blog/fifth-post.mdx.ejs +54 -0
  101. package/src/frameworks/react/project/base/content/blog/first-post.md.ejs +47 -0
  102. package/src/frameworks/react/project/base/content/blog/fourth-post.md.ejs +42 -0
  103. package/src/frameworks/react/project/base/content/blog/second-post.mdx.ejs +46 -0
  104. package/src/frameworks/react/project/base/content/blog/third-post.md.ejs +49 -0
  105. package/src/frameworks/react/project/base/content-collections.ts.ejs +37 -0
  106. package/src/frameworks/react/project/base/package.json +8 -1
  107. package/src/frameworks/react/project/base/public/images/lagoon-1.svg +13 -0
  108. package/src/frameworks/react/project/base/public/images/lagoon-2.svg +12 -0
  109. package/src/frameworks/react/project/base/public/images/lagoon-3.svg +12 -0
  110. package/src/frameworks/react/project/base/public/images/lagoon-4.svg +12 -0
  111. package/src/frameworks/react/project/base/public/images/lagoon-5.svg +12 -0
  112. package/src/frameworks/react/project/base/public/images/lagoon-about.svg +14 -0
  113. package/src/frameworks/react/project/base/src/components/Footer.tsx.ejs +42 -0
  114. package/src/frameworks/react/project/base/src/components/Header.tsx.ejs +92 -138
  115. package/src/frameworks/react/project/base/src/components/MdxCallout.tsx.ejs +16 -0
  116. package/src/frameworks/react/project/base/src/components/MdxMetrics.tsx.ejs +23 -0
  117. package/src/frameworks/react/project/base/src/components/ThemeToggle.tsx.ejs +81 -0
  118. package/src/frameworks/react/project/base/src/lib/site.ts.ejs +4 -0
  119. package/src/frameworks/react/project/base/src/main.tsx.ejs +0 -1
  120. package/src/frameworks/react/project/base/src/routes/__root.tsx.ejs +10 -6
  121. package/src/frameworks/react/project/base/src/routes/about.tsx.ejs +27 -0
  122. package/src/frameworks/react/project/base/src/routes/blog.$slug.tsx.ejs +71 -0
  123. package/src/frameworks/react/project/base/src/routes/blog.index.tsx.ejs +93 -0
  124. package/src/frameworks/react/project/base/src/routes/index.tsx.ejs +58 -91
  125. package/src/frameworks/react/project/base/src/routes/rss[.]xml.ts.ejs +35 -0
  126. package/src/frameworks/react/project/base/src/styles.css.ejs +268 -6
  127. package/src/frameworks/react/project/base/tsconfig.json.ejs +2 -0
  128. package/src/frameworks/react/project/base/vite.config.ts.ejs +2 -0
  129. package/src/frameworks/solid/add-ons/store/assets/src/lib/demo-store.ts +5 -6
  130. package/src/frameworks/solid/add-ons/store/assets/src/routes/demo.store.tsx.ejs +2 -2
  131. package/src/frameworks/solid/examples/tanchat/assets/src/lib/demo-store.ts +5 -6
  132. package/src/frameworks/solid/project/base/src/components/Header.tsx.ejs +8 -6
  133. package/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs +1 -1
  134. package/src/frameworks/solid/project/base/src/routes/index.tsx.ejs +1 -1
  135. package/src/frameworks.ts +4 -0
  136. package/src/package-json.ts +1 -1
  137. package/src/registry.ts +28 -4
  138. package/tests/add-ons.test.ts +4 -4
  139. package/tests/config-file.test.ts +3 -3
  140. package/tests/custom-add-ons/starter.test.ts +34 -2
  141. package/tests/frameworks.test.ts +24 -0
  142. package/tests/options.test.ts +4 -4
  143. package/tests/utils.test.ts +2 -2
  144. package/dist/frameworks/react/add-ons/strapi/assets/src/lib/strapiClient.ts +0 -7
  145. package/dist/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi_.$articleId.tsx +0 -78
  146. package/src/frameworks/react/add-ons/strapi/assets/src/lib/strapiClient.ts +0 -7
  147. package/src/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi_.$articleId.tsx +0 -78
@@ -1,24 +1,32 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap');
1
2
  @import 'tailwindcss';
3
+ @plugin '@tailwindcss/typography';
2
4
 
3
5
  @import 'tw-animate-css';
4
6
 
5
7
  @custom-variant dark (&:is(.dark *));
6
8
 
7
- body {
8
- @apply m-0;
9
- font-family:
10
- -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
11
- 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
12
- -webkit-font-smoothing: antialiased;
13
- -moz-osx-font-smoothing: grayscale;
14
- }
15
-
16
- code {
17
- font-family:
18
- source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
19
- }
20
-
21
9
  :root {
10
+ --sea-ink: #173a40;
11
+ --sea-ink-soft: #416166;
12
+ --lagoon: #4fb8b2;
13
+ --lagoon-deep: #328f97;
14
+ --palm: #2f6a4a;
15
+ --sand: #e7f0e8;
16
+ --foam: #f3faf5;
17
+ --surface: rgba(255, 255, 255, 0.74);
18
+ --surface-strong: rgba(255, 255, 255, 0.9);
19
+ --line: rgba(23, 58, 64, 0.14);
20
+ --inset-glint: rgba(255, 255, 255, 0.82);
21
+ --kicker: rgba(47, 106, 74, 0.9);
22
+ --bg-base: #e7f3ec;
23
+ --header-bg: rgba(251, 255, 248, 0.84);
24
+ --chip-bg: rgba(255, 255, 255, 0.8);
25
+ --chip-line: rgba(47, 106, 74, 0.18);
26
+ --link-bg-hover: rgba(255, 255, 255, 0.9);
27
+ --hero-a: rgba(79, 184, 178, 0.36);
28
+ --hero-b: rgba(47, 106, 74, 0.2);
29
+
22
30
  --background: oklch(1 0 0);
23
31
  --foreground: oklch(0.141 0.005 285.823);
24
32
  --card: oklch(1 0 0);
@@ -55,6 +63,26 @@ code {
55
63
  }
56
64
 
57
65
  .dark {
66
+ --sea-ink: #d7ece8;
67
+ --sea-ink-soft: #afcdc8;
68
+ --lagoon: #60d7cf;
69
+ --lagoon-deep: #8de5db;
70
+ --palm: #6ec89a;
71
+ --sand: #0f1a1e;
72
+ --foam: #101d22;
73
+ --surface: rgba(16, 30, 34, 0.8);
74
+ --surface-strong: rgba(15, 27, 31, 0.92);
75
+ --line: rgba(141, 229, 219, 0.18);
76
+ --inset-glint: rgba(194, 247, 238, 0.14);
77
+ --kicker: #b8efe5;
78
+ --bg-base: #0a1418;
79
+ --header-bg: rgba(10, 20, 24, 0.8);
80
+ --chip-bg: rgba(13, 28, 32, 0.9);
81
+ --chip-line: rgba(141, 229, 219, 0.24);
82
+ --link-bg-hover: rgba(24, 44, 49, 0.8);
83
+ --hero-a: rgba(96, 215, 207, 0.18);
84
+ --hero-b: rgba(110, 200, 154, 0.12);
85
+
58
86
  --background: oklch(0.141 0.005 285.823);
59
87
  --foreground: oklch(0.985 0 0);
60
88
  --card: oklch(0.141 0.005 285.823);
@@ -90,6 +118,7 @@ code {
90
118
  }
91
119
 
92
120
  @theme inline {
121
+ --font-sans: 'Manrope', ui-sans-serif, system-ui, sans-serif;
93
122
  --color-background: var(--background);
94
123
  --color-foreground: var(--foreground);
95
124
  --color-card: var(--card);
@@ -128,11 +157,191 @@ code {
128
157
  --color-sidebar-ring: var(--sidebar-ring);
129
158
  }
130
159
 
160
+ html,
161
+ body,
162
+ #app {
163
+ min-height: 100%;
164
+ }
165
+
166
+ body {
167
+ margin: 0;
168
+ color: var(--sea-ink);
169
+ font-family: var(--font-sans);
170
+ background-color: var(--bg-base);
171
+ background:
172
+ radial-gradient(1100px 620px at -8% -10%, var(--hero-a), transparent 58%),
173
+ radial-gradient(1050px 620px at 112% -12%, var(--hero-b), transparent 62%),
174
+ radial-gradient(720px 380px at 50% 115%, rgba(79, 184, 178, 0.1), transparent 68%),
175
+ linear-gradient(180deg, color-mix(in oklab, var(--sand) 68%, white) 0%, var(--foam) 44%, var(--bg-base) 100%);
176
+ overflow-x: hidden;
177
+ -webkit-font-smoothing: antialiased;
178
+ -moz-osx-font-smoothing: grayscale;
179
+ }
180
+
181
+ body::before {
182
+ content: '';
183
+ position: fixed;
184
+ inset: 0;
185
+ pointer-events: none;
186
+ z-index: -1;
187
+ opacity: 0.28;
188
+ background:
189
+ radial-gradient(circle at 20% 15%, rgba(255, 255, 255, 0.8), transparent 34%),
190
+ radial-gradient(circle at 78% 26%, rgba(79, 184, 178, 0.2), transparent 42%),
191
+ radial-gradient(circle at 42% 82%, rgba(47, 106, 74, 0.14), transparent 36%);
192
+ }
193
+
194
+ body::after {
195
+ content: '';
196
+ position: fixed;
197
+ inset: 0;
198
+ pointer-events: none;
199
+ z-index: -1;
200
+ opacity: 0.14;
201
+ background-image:
202
+ linear-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px),
203
+ linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px);
204
+ background-size: 28px 28px;
205
+ mask-image: radial-gradient(circle at 50% 30%, black, transparent 78%);
206
+ }
207
+
208
+ a {
209
+ color: var(--lagoon-deep);
210
+ text-decoration-color: rgba(50, 143, 151, 0.4);
211
+ text-decoration-thickness: 1px;
212
+ text-underline-offset: 2px;
213
+ }
214
+
215
+ a:hover {
216
+ color: #246f76;
217
+ }
218
+
219
+ code {
220
+ font-size: 0.9em;
221
+ border: 1px solid var(--line);
222
+ background: color-mix(in oklab, var(--surface-strong) 82%, white 18%);
223
+ border-radius: 7px;
224
+ padding: 2px 7px;
225
+ }
226
+
227
+ pre code {
228
+ border: 0;
229
+ background: transparent;
230
+ padding: 0;
231
+ border-radius: 0;
232
+ font-size: inherit;
233
+ color: inherit;
234
+ }
235
+
236
+ .prose pre {
237
+ border: 1px solid var(--line);
238
+ border-radius: 12px;
239
+ background: #1d2e45;
240
+ color: #e8efff;
241
+ }
242
+
243
+ .page-wrap {
244
+ width: min(1080px, calc(100% - 2rem));
245
+ margin-inline: auto;
246
+ }
247
+
248
+ .display-title {
249
+ font-family: 'Fraunces', Georgia, serif;
250
+ }
251
+
252
+ .island-shell {
253
+ border: 1px solid var(--line);
254
+ background: linear-gradient(165deg, var(--surface-strong), var(--surface));
255
+ box-shadow:
256
+ 0 1px 0 var(--inset-glint) inset,
257
+ 0 22px 44px rgba(30, 90, 72, 0.1),
258
+ 0 6px 18px rgba(23, 58, 64, 0.08);
259
+ backdrop-filter: blur(4px);
260
+ }
261
+
262
+ .feature-card {
263
+ background: linear-gradient(165deg, color-mix(in oklab, var(--surface-strong) 93%, white 7%), var(--surface));
264
+ box-shadow:
265
+ 0 1px 0 var(--inset-glint) inset,
266
+ 0 18px 34px rgba(30, 90, 72, 0.1),
267
+ 0 4px 14px rgba(23, 58, 64, 0.06);
268
+ }
269
+
270
+ .feature-card:hover {
271
+ transform: translateY(-2px);
272
+ border-color: color-mix(in oklab, var(--lagoon-deep) 35%, var(--line));
273
+ }
274
+
275
+ button,
276
+ .island-shell,
277
+ a {
278
+ transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease,
279
+ transform 180ms ease;
280
+ }
281
+
282
+ .island-kicker {
283
+ letter-spacing: 0.16em;
284
+ text-transform: uppercase;
285
+ font-weight: 700;
286
+ font-size: 0.69rem;
287
+ color: var(--kicker);
288
+ }
289
+
290
+ .nav-link {
291
+ position: relative;
292
+ text-decoration: none;
293
+ color: var(--sea-ink-soft);
294
+ }
295
+
296
+ .nav-link::after {
297
+ content: '';
298
+ position: absolute;
299
+ left: 0;
300
+ bottom: -8px;
301
+ width: 100%;
302
+ height: 2px;
303
+ transform: scaleX(0);
304
+ transform-origin: left;
305
+ background: linear-gradient(90deg, var(--lagoon), #7ed3bf);
306
+ transition: transform 170ms ease;
307
+ }
308
+
309
+ .nav-link:hover,
310
+ .nav-link.is-active {
311
+ color: var(--sea-ink);
312
+ }
313
+
314
+ .nav-link:hover::after,
315
+ .nav-link.is-active::after {
316
+ transform: scaleX(1);
317
+ }
318
+
319
+ .rise-in {
320
+ animation: rise-in 700ms cubic-bezier(0.16, 1, 0.3, 1) both;
321
+ }
322
+
323
+ @keyframes rise-in {
324
+ from {
325
+ opacity: 0;
326
+ transform: translateY(12px);
327
+ }
328
+ to {
329
+ opacity: 1;
330
+ transform: translateY(0);
331
+ }
332
+ }
333
+
334
+ .site-footer {
335
+ border-top: 1px solid var(--line);
336
+ background: color-mix(in oklab, var(--header-bg) 84%, transparent 16%);
337
+ }
338
+
131
339
  @layer base {
132
340
  * {
133
341
  @apply border-border outline-ring/50;
134
342
  }
135
343
  body {
136
- @apply bg-background text-foreground;
344
+ background-color: var(--background);
345
+ color: var(--foreground);
137
346
  }
138
347
  }
@@ -1,13 +1,12 @@
1
- import { Derived, Store } from '@tanstack/store'
1
+ import { Store } from '@tanstack/store'
2
2
 
3
3
  export const store = new Store({
4
4
  firstName: 'Jane',
5
5
  lastName: 'Smith',
6
6
  })
7
7
 
8
- export const fullName = new Derived({
9
- fn: () => `${store.state.firstName} ${store.state.lastName}`,
10
- deps: [store],
11
- })
8
+ export const fullName = new Store(`${store.state.firstName} ${store.state.lastName}`)
12
9
 
13
- fullName.mount()
10
+ store.subscribe(() => {
11
+ fullName.setState(() => `${store.state.firstName} ${store.state.lastName}`)
12
+ })
@@ -39,7 +39,7 @@ function LastName() {
39
39
  }
40
40
 
41
41
  function FullName() {
42
- const fName = useStore(fullName)
42
+ const fName = useStore(fullName, (state) => state)
43
43
  return (
44
44
  <div className="bg-white/10 rounded-lg px-4 py-2 outline-none ">
45
45
  {fName}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
- "@tanstack/store": "^0.8.0",
4
- "@tanstack/react-store": "^0.8.0"
3
+ "@tanstack/store": "^0.9.1",
4
+ "@tanstack/react-store": "^0.9.1"
5
5
  },
6
6
  "devDependencies": {
7
7
  "@tanstack/devtools-event-client": "^0.4.0"
@@ -1,14 +1,164 @@
1
- ## Setting up Strapi
1
+ ## Strapi CMS Integration
2
2
 
3
- The current setup shows an example of how to use Strapi with an articles collection which is part of the example structure & data.
3
+ This add-on integrates Strapi CMS with your TanStack Start application using the official Strapi Client SDK.
4
4
 
5
- - Create a local running copy of the strapi admin
5
+ ### Features
6
6
 
7
+ - Article listing with search and pagination
8
+ - Article detail pages with dynamic block rendering
9
+ - Rich text, quotes, media, and image slider blocks
10
+ - Markdown content rendering with GitHub Flavored Markdown
11
+ - Responsive image handling with error fallbacks
12
+ - URL-based search and pagination (shareable/bookmarkable)
13
+ - Graceful error handling with helpful setup instructions
14
+
15
+ ### Project Structure
16
+
17
+ ```
18
+ parent/
19
+ ├── client/ # TanStack Start frontend (your project name)
20
+ │ ├── src/
21
+ │ │ ├── components/
22
+ │ │ │ ├── blocks/ # Block rendering components
23
+ │ │ │ ├── markdown-content.tsx
24
+ │ │ │ ├── pagination.tsx
25
+ │ │ │ ├── search.tsx
26
+ │ │ │ └── strapi-image.tsx
27
+ │ │ ├── data/
28
+ │ │ │ ├── loaders/ # Server functions
29
+ │ │ │ └── strapi-sdk.ts
30
+ │ │ ├── lib/
31
+ │ │ │ └── strapi-utils.ts
32
+ │ │ ├── routes/demo/
33
+ │ │ │ ├── strapi.tsx # Articles list
34
+ │ │ │ └── strapi.$articleId.tsx # Article detail
35
+ │ │ └── types/
36
+ │ │ └── strapi.ts
37
+ │ ├── .env.local
38
+ │ └── package.json
39
+ └── server/ # Strapi CMS backend (create manually or use hosted Strapi)
40
+ ├── src/api/ # Content types
41
+ ├── config/ # Strapi configuration
42
+ └── package.json
43
+ ```
44
+
45
+ ### Quick Start
46
+
47
+ Create your Strapi project separately (or use an existing hosted Strapi instance), then point this app to it with `VITE_STRAPI_URL`.
48
+
49
+ **1. Set up Strapi:**
50
+
51
+ Follow the Strapi quick-start guide to create a local project, or use your existing Strapi deployment:
52
+
53
+ - https://docs.strapi.io/dev-docs/quick-start
54
+
55
+ If you created a local Strapi project in a sibling `server` directory, continue with:
56
+
57
+ ```bash
58
+ cd ../server
59
+ npm install # or pnpm install / yarn install
60
+ ```
61
+
62
+ **2. Start the Strapi server:**
63
+
64
+ ```bash
65
+ npm run develop # Starts at http://localhost:1337
66
+ ```
67
+
68
+ **3. Create an admin account:**
69
+
70
+ Open http://localhost:1337/admin and create your first admin user.
71
+
72
+ **4. Create content:**
73
+
74
+ In the Strapi admin panel, go to Content Manager > Article and create some articles.
75
+
76
+ **5. Start your TanStack app (in another terminal):**
77
+
78
+ ```bash
79
+ cd ../client # or your project name
80
+ npm run dev # Starts at http://localhost:3000
81
+ ```
82
+
83
+ **6. View the demo:**
84
+
85
+ Navigate to http://localhost:3000/demo/strapi to see your articles.
86
+
87
+ ### Environment Variables
88
+
89
+ The following environment variable is pre-configured in `.env.local`:
90
+
91
+ ```bash
92
+ VITE_STRAPI_URL="http://localhost:1337"
93
+ ```
94
+
95
+ For production, update this to your deployed Strapi URL.
96
+
97
+ ### Demo Pages
98
+
99
+ | URL | Description |
100
+ |-----|-------------|
101
+ | `/demo/strapi` | Articles list with search and pagination |
102
+ | `/demo/strapi/:articleId` | Article detail with block rendering |
103
+
104
+ ### Search and Pagination
105
+
106
+ - **Search**: Type in the search box to filter articles by title or description
107
+ - **Pagination**: Navigate between pages using the pagination controls
108
+ - **URL State**: Search and page are stored in the URL (`?query=term&page=2`)
109
+
110
+ ### Block Types Supported
111
+
112
+ | Block | Component | Description |
113
+ |-------|-----------|-------------|
114
+ | `shared.rich-text` | RichText | Markdown content |
115
+ | `shared.quote` | Quote | Blockquote with author |
116
+ | `shared.media` | Media | Single image/video |
117
+ | `shared.slider` | Slider | Image gallery grid |
118
+
119
+ ### Dependencies
120
+
121
+ | Package | Purpose |
122
+ |---------|---------|
123
+ | `@strapi/client` | Official Strapi SDK |
124
+ | `react-markdown` | Markdown rendering |
125
+ | `remark-gfm` | GitHub Flavored Markdown |
126
+ | `use-debounce` | Debounced search input |
127
+
128
+ ### Running Both Servers
129
+
130
+ Open two terminal windows from the parent directory:
131
+
132
+ **Terminal 1 - Strapi:**
7
133
  ```bash
8
- pnpm dlx create-strapi@latest my-strapi-project
9
- cd my-strapi-project
10
- pnpm dev
134
+ cd server && npm run develop
11
135
  ```
12
136
 
13
- - Login and publish the example articles to see them on the strapi demo page.
14
- - Set the `VITE_STRAPI_URL` environment variable in your `.env.local`. (For local it should be http://localhost:1337/api)
137
+ **Terminal 2 - TanStack Start:**
138
+ ```bash
139
+ cd client && npm run dev # or your project name
140
+ ```
141
+
142
+ ### Customization
143
+
144
+ **Change page size:**
145
+ Edit `src/data/loaders/articles.ts` and modify `PAGE_SIZE`.
146
+
147
+ **Add new block types:**
148
+ 1. Create component in `src/components/blocks/`
149
+ 2. Export from `src/components/blocks/index.ts`
150
+ 3. Add case to `block-renderer.tsx` switch statement
151
+ 4. Update populate in articles loader
152
+
153
+ **Add new content types:**
154
+ 1. Add types to `src/types/strapi.ts`
155
+ 2. Create loader in `src/data/loaders/`
156
+ 3. Create route in `src/routes/demo/`
157
+
158
+ ### Learn More
159
+
160
+ - [Strapi Documentation](https://docs.strapi.io/)
161
+ - [Strapi Client SDK](https://www.npmjs.com/package/@strapi/client)
162
+ - [Strapi Cloud Template Blog](https://github.com/strapi/strapi-cloud-template-blog)
163
+ - [TanStack Start Documentation](https://tanstack.com/start/latest)
164
+ - [TanStack Router Search Params](https://tanstack.com/router/latest/docs/framework/react/guide/search-params)
@@ -1,2 +1,2 @@
1
1
  # Strapi configuration
2
- VITE_STRAPI_URL="http://localhost:1337/api"
2
+ VITE_STRAPI_URL="http://localhost:1337"
@@ -0,0 +1,55 @@
1
+ import { RichText } from "./rich-text";
2
+ import { Quote } from "./quote";
3
+ import { Media } from "./media";
4
+ import { Slider } from "./slider";
5
+
6
+ import type { IRichText } from "./rich-text";
7
+ import type { IQuote } from "./quote";
8
+ import type { IMedia } from "./media";
9
+ import type { ISlider } from "./slider";
10
+
11
+ // Union type of all block types
12
+ export type Block = IRichText | IQuote | IMedia | ISlider;
13
+
14
+ interface BlockRendererProps {
15
+ blocks: Array<Block>;
16
+ }
17
+
18
+ /**
19
+ * BlockRenderer - Renders dynamic content blocks from Strapi
20
+ *
21
+ * Usage:
22
+ * ```tsx
23
+ * <BlockRenderer blocks={article.blocks} />
24
+ * ```
25
+ */
26
+ export function BlockRenderer({ blocks }: Readonly<BlockRendererProps>) {
27
+ if (!blocks || blocks.length === 0) return null;
28
+
29
+ const renderBlock = (block: Block) => {
30
+ switch (block.__component) {
31
+ case "shared.rich-text":
32
+ return <RichText {...block} />;
33
+ case "shared.quote":
34
+ return <Quote {...block} />;
35
+ case "shared.media":
36
+ return <Media {...block} />;
37
+ case "shared.slider":
38
+ return <Slider {...block} />;
39
+ default:
40
+ // Log unknown block types in development
41
+ console.warn("Unknown block type:", (block as any).__component);
42
+ return null;
43
+ }
44
+ };
45
+
46
+ return (
47
+ <div className="space-y-6">
48
+ {blocks.map((block, index) => (
49
+ <div key={`${block.__component}-${block.id}-${index}`}>
50
+ {renderBlock(block)}
51
+ </div>
52
+ ))}
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,14 @@
1
+ export { BlockRenderer } from "./block-renderer";
2
+ export type { Block } from "./block-renderer";
3
+
4
+ export { RichText } from "./rich-text";
5
+ export type { IRichText } from "./rich-text";
6
+
7
+ export { Quote } from "./quote";
8
+ export type { IQuote } from "./quote";
9
+
10
+ export { Media } from "./media";
11
+ export type { IMedia } from "./media";
12
+
13
+ export { Slider } from "./slider";
14
+ export type { ISlider } from "./slider";
@@ -0,0 +1,27 @@
1
+ import { StrapiImage } from "@/components/strapi-image";
2
+ import type { TImage } from "@/types/strapi";
3
+
4
+ export interface IMedia {
5
+ __component: "shared.media";
6
+ id: number;
7
+ file?: TImage;
8
+ }
9
+
10
+ export function Media({ file }: Readonly<IMedia>) {
11
+ if (!file) return null;
12
+
13
+ return (
14
+ <figure className="my-8">
15
+ <StrapiImage
16
+ src={file.url}
17
+ alt={file.alternativeText || ""}
18
+ className="rounded-lg w-full"
19
+ />
20
+ {file.alternativeText && (
21
+ <figcaption className="mt-2 text-center text-sm text-gray-500">
22
+ {file.alternativeText}
23
+ </figcaption>
24
+ )}
25
+ </figure>
26
+ );
27
+ }
@@ -0,0 +1,19 @@
1
+ export interface IQuote {
2
+ __component: "shared.quote";
3
+ id: number;
4
+ body: string;
5
+ title?: string;
6
+ }
7
+
8
+ export function Quote({ body, title }: Readonly<IQuote>) {
9
+ return (
10
+ <blockquote className="border-l-4 border-cyan-400 pl-6 py-4 my-6 bg-slate-800/30 rounded-r-lg">
11
+ <p className="text-xl italic text-gray-300 leading-relaxed">{body}</p>
12
+ {title && (
13
+ <cite className="block mt-4 text-cyan-400 not-italic font-medium">
14
+ — {title}
15
+ </cite>
16
+ )}
17
+ </blockquote>
18
+ );
19
+ }
@@ -0,0 +1,11 @@
1
+ import { MarkdownContent } from "@/components/markdown-content";
2
+
3
+ export interface IRichText {
4
+ __component: "shared.rich-text";
5
+ id: number;
6
+ body: string;
7
+ }
8
+
9
+ export function RichText({ body }: Readonly<IRichText>) {
10
+ return <MarkdownContent content={body} />;
11
+ }
@@ -0,0 +1,28 @@
1
+ import { StrapiImage } from "@/components/strapi-image";
2
+ import type { TImage } from "@/types/strapi";
3
+
4
+ export interface ISlider {
5
+ __component: "shared.slider";
6
+ id: number;
7
+ files?: Array<TImage>;
8
+ }
9
+
10
+ export function Slider({ files }: Readonly<ISlider>) {
11
+ if (!files || files.length === 0) return null;
12
+
13
+ return (
14
+ <div className="my-8">
15
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
16
+ {files.map((file, index) => (
17
+ <figure key={file.id || index}>
18
+ <StrapiImage
19
+ src={file.url}
20
+ alt={file.alternativeText || ""}
21
+ className="rounded-lg w-full h-48 object-cover"
22
+ />
23
+ </figure>
24
+ ))}
25
+ </div>
26
+ </div>
27
+ );
28
+ }