@agility/create-next-app 1.0.0-beta.2

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 (213) hide show
  1. package/.claude/settings.json +7 -0
  2. package/.claude/settings.local.json +24 -0
  3. package/FEATURE_ROADMAP.md +343 -0
  4. package/README.md +205 -0
  5. package/TESTING.md +131 -0
  6. package/bin/create-agility-app.js +48 -0
  7. package/dist/agility/api-keys/generateApiKeys.d.ts +9 -0
  8. package/dist/agility/api-keys/generateApiKeys.d.ts.map +1 -0
  9. package/dist/agility/api-keys/generateApiKeys.js +99 -0
  10. package/dist/agility/api-keys/generateApiKeys.js.map +1 -0
  11. package/dist/agility/api-keys/getApiKeys.d.ts +9 -0
  12. package/dist/agility/api-keys/getApiKeys.d.ts.map +1 -0
  13. package/dist/agility/api-keys/getApiKeys.js +14 -0
  14. package/dist/agility/api-keys/getApiKeys.js.map +1 -0
  15. package/dist/agility/index.d.ts +3 -0
  16. package/dist/agility/index.d.ts.map +1 -0
  17. package/dist/agility/index.js +8 -0
  18. package/dist/agility/index.js.map +1 -0
  19. package/dist/agility/instance/createNewInstance.d.ts +8 -0
  20. package/dist/agility/instance/createNewInstance.d.ts.map +1 -0
  21. package/dist/agility/instance/createNewInstance.js +65 -0
  22. package/dist/agility/instance/createNewInstance.js.map +1 -0
  23. package/dist/agility/instance/getAvailableInstances.d.ts +8 -0
  24. package/dist/agility/instance/getAvailableInstances.d.ts.map +1 -0
  25. package/dist/agility/instance/getAvailableInstances.js +43 -0
  26. package/dist/agility/instance/getAvailableInstances.js.map +1 -0
  27. package/dist/agility/instance/manageInstance.d.ts +9 -0
  28. package/dist/agility/instance/manageInstance.d.ts.map +1 -0
  29. package/dist/agility/instance/manageInstance.js +82 -0
  30. package/dist/agility/instance/manageInstance.js.map +1 -0
  31. package/dist/agility/utils/getMgmtAPIUrl.d.ts +20 -0
  32. package/dist/agility/utils/getMgmtAPIUrl.d.ts.map +1 -0
  33. package/dist/agility/utils/getMgmtAPIUrl.js +61 -0
  34. package/dist/agility/utils/getMgmtAPIUrl.js.map +1 -0
  35. package/dist/auth/api-key/authenticateWithApiKey.d.ts +6 -0
  36. package/dist/auth/api-key/authenticateWithApiKey.d.ts.map +1 -0
  37. package/dist/auth/api-key/authenticateWithApiKey.js +28 -0
  38. package/dist/auth/api-key/authenticateWithApiKey.js.map +1 -0
  39. package/dist/auth/index.d.ts +3 -0
  40. package/dist/auth/index.d.ts.map +1 -0
  41. package/dist/auth/index.js +8 -0
  42. package/dist/auth/index.js.map +1 -0
  43. package/dist/auth/oauth/authenticate.d.ts +6 -0
  44. package/dist/auth/oauth/authenticate.d.ts.map +1 -0
  45. package/dist/auth/oauth/authenticate.js +162 -0
  46. package/dist/auth/oauth/authenticate.js.map +1 -0
  47. package/dist/auth/oauth/constants.d.ts +5 -0
  48. package/dist/auth/oauth/constants.d.ts.map +1 -0
  49. package/dist/auth/oauth/constants.js +9 -0
  50. package/dist/auth/oauth/constants.js.map +1 -0
  51. package/dist/auth/oauth/exchangeCodeForToken.d.ts +7 -0
  52. package/dist/auth/oauth/exchangeCodeForToken.d.ts.map +1 -0
  53. package/dist/auth/oauth/exchangeCodeForToken.js +39 -0
  54. package/dist/auth/oauth/exchangeCodeForToken.js.map +1 -0
  55. package/dist/cli/index.d.ts +3 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +290 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/promptForMissingOptions.d.ts +8 -0
  60. package/dist/cli/promptForMissingOptions.d.ts.map +1 -0
  61. package/dist/cli/promptForMissingOptions.js +92 -0
  62. package/dist/cli/promptForMissingOptions.js.map +1 -0
  63. package/dist/config/env/createEnvFile.d.ts +6 -0
  64. package/dist/config/env/createEnvFile.d.ts.map +1 -0
  65. package/dist/config/env/createEnvFile.js +31 -0
  66. package/dist/config/env/createEnvFile.js.map +1 -0
  67. package/dist/config/index.d.ts +2 -0
  68. package/dist/config/index.d.ts.map +1 -0
  69. package/dist/config/index.js +6 -0
  70. package/dist/config/index.js.map +1 -0
  71. package/dist/config/mcp/createMcpConfig.d.ts +5 -0
  72. package/dist/config/mcp/createMcpConfig.d.ts.map +1 -0
  73. package/dist/config/mcp/createMcpConfig.js +32 -0
  74. package/dist/config/mcp/createMcpConfig.js.map +1 -0
  75. package/dist/config/packages/installAgilityPackages.d.ts +6 -0
  76. package/dist/config/packages/installAgilityPackages.d.ts.map +1 -0
  77. package/dist/config/packages/installAgilityPackages.js +61 -0
  78. package/dist/config/packages/installAgilityPackages.js.map +1 -0
  79. package/dist/config/setupProject.d.ts +8 -0
  80. package/dist/config/setupProject.d.ts.map +1 -0
  81. package/dist/config/setupProject.js +32 -0
  82. package/dist/config/setupProject.js.map +1 -0
  83. package/dist/create-next-app/createNextApp.d.ts +9 -0
  84. package/dist/create-next-app/createNextApp.d.ts.map +1 -0
  85. package/dist/create-next-app/createNextApp.js +83 -0
  86. package/dist/create-next-app/createNextApp.js.map +1 -0
  87. package/dist/create-next-app/index.d.ts +3 -0
  88. package/dist/create-next-app/index.d.ts.map +1 -0
  89. package/dist/create-next-app/index.js +8 -0
  90. package/dist/create-next-app/index.js.map +1 -0
  91. package/dist/scaffold/components/createPageComponents.d.ts +6 -0
  92. package/dist/scaffold/components/createPageComponents.d.ts.map +1 -0
  93. package/dist/scaffold/components/createPageComponents.js +62 -0
  94. package/dist/scaffold/components/createPageComponents.js.map +1 -0
  95. package/dist/scaffold/containers/createContainers.d.ts +6 -0
  96. package/dist/scaffold/containers/createContainers.d.ts.map +1 -0
  97. package/dist/scaffold/containers/createContainers.js +48 -0
  98. package/dist/scaffold/containers/createContainers.js.map +1 -0
  99. package/dist/scaffold/index.d.ts +2 -0
  100. package/dist/scaffold/index.d.ts.map +1 -0
  101. package/dist/scaffold/index.js +6 -0
  102. package/dist/scaffold/index.js.map +1 -0
  103. package/dist/scaffold/instance/createBlankInstance.d.ts +8 -0
  104. package/dist/scaffold/instance/createBlankInstance.d.ts.map +1 -0
  105. package/dist/scaffold/instance/createBlankInstance.js +51 -0
  106. package/dist/scaffold/instance/createBlankInstance.js.map +1 -0
  107. package/dist/scaffold/models/createContentModels.d.ts +6 -0
  108. package/dist/scaffold/models/createContentModels.d.ts.map +1 -0
  109. package/dist/scaffold/models/createContentModels.js +70 -0
  110. package/dist/scaffold/models/createContentModels.js.map +1 -0
  111. package/dist/templates/copyDirectory.d.ts +5 -0
  112. package/dist/templates/copyDirectory.d.ts.map +1 -0
  113. package/dist/templates/copyDirectory.js +28 -0
  114. package/dist/templates/copyDirectory.js.map +1 -0
  115. package/dist/templates/copyTemplates.d.ts +8 -0
  116. package/dist/templates/copyTemplates.d.ts.map +1 -0
  117. package/dist/templates/copyTemplates.js +58 -0
  118. package/dist/templates/copyTemplates.js.map +1 -0
  119. package/dist/templates/index.d.ts +2 -0
  120. package/dist/templates/index.d.ts.map +1 -0
  121. package/dist/templates/index.js +6 -0
  122. package/dist/templates/index.js.map +1 -0
  123. package/dist/types/index.d.ts +50 -0
  124. package/dist/types/index.d.ts.map +1 -0
  125. package/dist/types/index.js +3 -0
  126. package/dist/types/index.js.map +1 -0
  127. package/dist/utils/git.d.ts +9 -0
  128. package/dist/utils/git.d.ts.map +1 -0
  129. package/dist/utils/git.js +71 -0
  130. package/dist/utils/git.js.map +1 -0
  131. package/dist/utils/validation.d.ts +45 -0
  132. package/dist/utils/validation.d.ts.map +1 -0
  133. package/dist/utils/validation.js +180 -0
  134. package/dist/utils/validation.js.map +1 -0
  135. package/package.json +45 -0
  136. package/src/agility/api-keys/generateApiKeys.ts +100 -0
  137. package/src/agility/api-keys/getApiKeys.ts +13 -0
  138. package/src/agility/index.ts +3 -0
  139. package/src/agility/instance/createNewInstance.ts +67 -0
  140. package/src/agility/instance/getAvailableInstances.ts +49 -0
  141. package/src/agility/instance/manageInstance.ts +90 -0
  142. package/src/agility/utils/getMgmtAPIUrl.ts +68 -0
  143. package/src/auth/api-key/authenticateWithApiKey.ts +24 -0
  144. package/src/auth/index.ts +3 -0
  145. package/src/auth/oauth/authenticate.ts +165 -0
  146. package/src/auth/oauth/constants.ts +6 -0
  147. package/src/auth/oauth/exchangeCodeForToken.ts +43 -0
  148. package/src/cli/index.ts +281 -0
  149. package/src/cli/promptForMissingOptions.ts +104 -0
  150. package/src/config/env/createEnvFile.ts +30 -0
  151. package/src/config/index.ts +2 -0
  152. package/src/config/mcp/createMcpConfig.ts +30 -0
  153. package/src/config/packages/installAgilityPackages.ts +63 -0
  154. package/src/config/setupProject.ts +31 -0
  155. package/src/create-next-app/createNextApp.ts +75 -0
  156. package/src/create-next-app/index.ts +3 -0
  157. package/src/scaffold/components/createPageComponents.ts +74 -0
  158. package/src/scaffold/containers/createContainers.ts +55 -0
  159. package/src/scaffold/index.ts +2 -0
  160. package/src/scaffold/instance/createBlankInstance.ts +55 -0
  161. package/src/scaffold/models/createContentModels.ts +83 -0
  162. package/src/templates/copyDirectory.ts +24 -0
  163. package/src/templates/copyTemplates.ts +57 -0
  164. package/src/templates/index.ts +2 -0
  165. package/src/types/index.ts +55 -0
  166. package/src/utils/git.ts +74 -0
  167. package/src/utils/validation.ts +184 -0
  168. package/templates/.claude/QUICK-START.md +230 -0
  169. package/templates/.claude/README.md +32 -0
  170. package/templates/.claude/settings.json +8 -0
  171. package/templates/BLANK-INSTANCE-SETUP.md +375 -0
  172. package/templates/DEVELOPMENT.md +160 -0
  173. package/templates/EXAMPLE-PROMPTS.md +643 -0
  174. package/templates/PROMPTS.md +410 -0
  175. package/templates/README.md +281 -0
  176. package/templates/agents.md +429 -0
  177. package/templates/app/[locale]/[...slug]/error.tsx +17 -0
  178. package/templates/app/[locale]/[...slug]/not-found.tsx +9 -0
  179. package/templates/app/[locale]/[...slug]/page.tsx +102 -0
  180. package/templates/app/[locale]/layout.tsx +22 -0
  181. package/templates/app/[locale]/page.tsx +12 -0
  182. package/templates/app/api/dynamic-redirect/route.ts +24 -0
  183. package/templates/app/api/preview/exit/route.ts +34 -0
  184. package/templates/app/api/preview/route.ts +63 -0
  185. package/templates/app/api/revalidate/route.ts +118 -0
  186. package/templates/components/agility-components/RichTextArea.tsx +66 -0
  187. package/templates/components/agility-components/index.ts +30 -0
  188. package/templates/components/agility-pages/MainTemplate.tsx +36 -0
  189. package/templates/components/agility-pages/index.ts +11 -0
  190. package/templates/docs/01-agility-cms-overview.md +139 -0
  191. package/templates/docs/02-page-routing.md +251 -0
  192. package/templates/docs/03-creating-components.md +462 -0
  193. package/templates/docs/04-data-fetching.md +484 -0
  194. package/templates/docs/05-containers-and-lists.md +596 -0
  195. package/templates/docs/06-localization.md +561 -0
  196. package/templates/docs/07-caching-strategies.md +410 -0
  197. package/templates/docs/08-common-components.md +756 -0
  198. package/templates/docs/09-whats-included.md +279 -0
  199. package/templates/docs/10-mcp-server-setup.md +153 -0
  200. package/templates/docs/11-linked-nested-content.md +611 -0
  201. package/templates/docs/README.md +164 -0
  202. package/templates/lib/cms/getAgilityContext.ts +28 -0
  203. package/templates/lib/cms/getAgilityPage.ts +51 -0
  204. package/templates/lib/cms/getAgilitySDK.ts +22 -0
  205. package/templates/lib/cms/getContentItem.ts +20 -0
  206. package/templates/lib/cms/getContentList.ts +19 -0
  207. package/templates/lib/cms/getRedirections.ts +85 -0
  208. package/templates/lib/cms/getSitemapFlat.ts +19 -0
  209. package/templates/lib/cms/getSitemapNested.ts +19 -0
  210. package/templates/lib/env.ts +99 -0
  211. package/templates/lib/i18n/config.ts +28 -0
  212. package/templates/proxy.ts +101 -0
  213. package/tsconfig.json +21 -0
@@ -0,0 +1,611 @@
1
+ # Linked and Nested Content in Agility CMS
2
+
3
+ This document explains the different types of linked and nested content in Agility CMS and how to properly fetch and work with each type.
4
+
5
+ ## Understanding Content Relationships
6
+
7
+ Agility CMS supports several types of content relationships. Understanding which type you're working with is **CRITICAL** for correct data fetching.
8
+
9
+ ### The Two Main Types
10
+
11
+ 1. **Auto-Populated Linked Content** (Single Item Links)
12
+ 2. **Reference-Based Nested Content** (Content Lists/Grids)
13
+
14
+ ## Type 1: Auto-Populated Linked Content
15
+
16
+ ### What is it?
17
+
18
+ When you use these Agility field types, the linked content is **automatically populated** by the SDK:
19
+ - **Content Link** (Single item selector)
20
+ - **Dropdown** (Single item from list)
21
+ - **Checkbox** (Multiple items from list)
22
+ - **Search List Box** (Searchable single/multiple item selector)
23
+
24
+ ### Key Characteristic
25
+
26
+ The field contains the **complete content item** with all fields, not just a reference.
27
+
28
+ ### How to Use
29
+
30
+ **NO separate fetch needed!** The data is already there.
31
+
32
+ ```tsx
33
+ import type { ContentItem } from "@agility/nextjs";
34
+
35
+ interface Author {
36
+ name: string;
37
+ bio: string;
38
+ avatar: ImageField;
39
+ }
40
+
41
+ interface BlogPost {
42
+ title: string;
43
+ content: string;
44
+ author: ContentItem<Author>; // ✅ Complete author object
45
+ category: ContentItem<Category>; // ✅ Complete category object
46
+ }
47
+
48
+ export default async function BlogPost({ module, locale }: any) {
49
+ const { fields: { title, content, author, category } } = module;
50
+
51
+ return (
52
+ <article>
53
+ <h1>{title}</h1>
54
+
55
+ {/* ✅ CORRECT: Access linked content directly */}
56
+ {author && (
57
+ <div className="author">
58
+ <img src={author.fields.avatar.url} alt={author.fields.name} />
59
+ <span>{author.fields.name}</span>
60
+ </div>
61
+ )}
62
+
63
+ {/* ✅ CORRECT: No fetch needed */}
64
+ {category && (
65
+ <span className="badge">{category.fields.name}</span>
66
+ )}
67
+
68
+ <div dangerouslySetInnerHTML={{ __html: content }} />
69
+ </article>
70
+ );
71
+ }
72
+ ```
73
+
74
+ ### Common Mistake
75
+
76
+ ```tsx
77
+ // ❌ WRONG: Don't fetch content that's already populated
78
+ const author = await getContentItem({
79
+ contentID: post.fields.author.contentID, // Unnecessary!
80
+ locale,
81
+ });
82
+
83
+ // ✅ CORRECT: Use it directly
84
+ const authorName = post.fields.author.fields.name;
85
+ ```
86
+
87
+ ## Type 2: Reference-Based Nested Content
88
+
89
+ ### What is it?
90
+
91
+ When you use these Agility field types, you get a **reference** that you must fetch separately:
92
+ - **Linked Content** (Grid/list of items)
93
+ - **Content Link (Multi)** (Multiple content items)
94
+
95
+ ### Key Characteristic
96
+
97
+ The field contains a **referencename** property that points to a content list, NOT the actual content.
98
+
99
+ ### Critical Pattern
100
+
101
+ **You MUST fetch the content separately using the `referencename`.**
102
+
103
+ ```tsx
104
+ import { getContentItem } from "@/lib/cms/getContentItem";
105
+ import { getContentList } from "@/lib/cms/getContentList";
106
+
107
+ interface BentoSection {
108
+ heading: string;
109
+ cards: {
110
+ referencename: string; // ⚠️ Just a reference, not the actual content!
111
+ };
112
+ }
113
+
114
+ export default async function BentoSection({ module, locale }: any) {
115
+ // Step 1: Get the module fields
116
+ const { fields: { heading, cards } } = await getContentItem<BentoSection>({
117
+ contentID: module.contentid,
118
+ languageCode: locale,
119
+ });
120
+
121
+ // Step 2: Fetch the nested content using the reference name
122
+ const cardItems = await getContentList({
123
+ referenceName: cards.referencename, // ✅ CRITICAL: Use referencename
124
+ languageCode: locale,
125
+ take: 20,
126
+ });
127
+
128
+ return (
129
+ <section>
130
+ <h2>{heading}</h2>
131
+ <div className="grid grid-cols-3 gap-4">
132
+ {cardItems.map((card) => (
133
+ <div key={card.contentID}>
134
+ <h3>{card.fields.title}</h3>
135
+ <p>{card.fields.description}</p>
136
+ </div>
137
+ ))}
138
+ </div>
139
+ </section>
140
+ );
141
+ }
142
+ ```
143
+
144
+ ### Common Mistakes
145
+
146
+ ```tsx
147
+ // ❌ WRONG: Trying to access fields directly
148
+ <div>{cards.fields.title}</div> // This won't work!
149
+
150
+ // ❌ WRONG: Using contentID instead of referencename
151
+ const items = await getContentList({
152
+ referenceName: cards.contentID, // Wrong property!
153
+ locale,
154
+ });
155
+
156
+ // ✅ CORRECT: Fetch using referencename
157
+ const items = await getContentList({
158
+ referenceName: cards.referencename, // Correct!
159
+ locale,
160
+ });
161
+ ```
162
+
163
+ ## How to Tell Which Type You Have
164
+
165
+ ### Method 1: Check the Field Type in Agility CMS
166
+
167
+ | Field Type | Populated? | How to Access |
168
+ |------------|-----------|---------------|
169
+ | Content Link (Single) | ✅ Yes | `field.fields.xxx` |
170
+ | Dropdown | ✅ Yes | `field.fields.xxx` |
171
+ | Checkbox | ✅ Yes | `field.fields.xxx` |
172
+ | Search List Box | ✅ Yes | `field.fields.xxx` |
173
+ | **Linked Content (Grid)** | ❌ No | `await getContentList({ referenceName: field.referencename })` |
174
+ | **Content Link (Multi)** | ❌ No | `await getContentList({ referenceName: field.referencename })` |
175
+
176
+ ### Method 2: Inspect the Data Structure
177
+
178
+ ```tsx
179
+ // Auto-populated linked content has 'fields'
180
+ if (module.fields.author?.fields) {
181
+ // ✅ Auto-populated - use directly
182
+ console.log(module.fields.author.fields.name);
183
+ }
184
+
185
+ // Reference-based content has 'referencename'
186
+ if (module.fields.cards?.referencename) {
187
+ // ⚠️ Reference-based - must fetch separately
188
+ const items = await getContentList({
189
+ referenceName: module.fields.cards.referencename,
190
+ locale,
191
+ });
192
+ }
193
+ ```
194
+
195
+ ## Real-World Examples
196
+
197
+ ### Example 1: Blog Post with Auto-Populated Links
198
+
199
+ ```tsx
200
+ import { AgilityPic } from "@agility/nextjs";
201
+ import type { ContentItem, ImageField } from "@agility/nextjs";
202
+
203
+ interface Category {
204
+ name: string;
205
+ slug: string;
206
+ color: string;
207
+ }
208
+
209
+ interface Author {
210
+ name: string;
211
+ bio: string;
212
+ avatar: ImageField;
213
+ }
214
+
215
+ interface Tag {
216
+ name: string;
217
+ slug: string;
218
+ }
219
+
220
+ interface BlogPost {
221
+ title: string;
222
+ content: string;
223
+ image: ImageField;
224
+ author: ContentItem<Author>; // Auto-populated
225
+ category: ContentItem<Category>; // Auto-populated
226
+ tags: ContentItem<Tag>[]; // Auto-populated array
227
+ }
228
+
229
+ export default async function BlogPostDetail({ module, locale }: any) {
230
+ const { fields } = module as { fields: BlogPost };
231
+
232
+ return (
233
+ <article>
234
+ <header>
235
+ <h1>{fields.title}</h1>
236
+
237
+ {/* ✅ Category is auto-populated */}
238
+ {fields.category && (
239
+ <span
240
+ className="badge"
241
+ style={{ backgroundColor: fields.category.fields.color }}
242
+ >
243
+ {fields.category.fields.name}
244
+ </span>
245
+ )}
246
+
247
+ {/* ✅ Author is auto-populated */}
248
+ {fields.author && (
249
+ <div className="author-info">
250
+ <AgilityPic
251
+ image={fields.author.fields.avatar}
252
+ fallbackWidth={48}
253
+ className="rounded-full"
254
+ />
255
+ <div>
256
+ <p className="font-bold">{fields.author.fields.name}</p>
257
+ <p className="text-sm">{fields.author.fields.bio}</p>
258
+ </div>
259
+ </div>
260
+ )}
261
+ </header>
262
+
263
+ <AgilityPic
264
+ image={fields.image}
265
+ fallbackWidth={1200}
266
+ className="w-full"
267
+ />
268
+
269
+ <div dangerouslySetInnerHTML={{ __html: fields.content }} />
270
+
271
+ {/* ✅ Tags are auto-populated array */}
272
+ {fields.tags && fields.tags.length > 0 && (
273
+ <footer className="flex gap-2">
274
+ {fields.tags.map((tag) => (
275
+ <a
276
+ key={tag.contentID}
277
+ href={`/tags/${tag.fields.slug}`}
278
+ className="tag"
279
+ >
280
+ {tag.fields.name}
281
+ </a>
282
+ ))}
283
+ </footer>
284
+ )}
285
+ </article>
286
+ );
287
+ }
288
+ ```
289
+
290
+ ### Example 2: Testimonials Grid with Reference-Based Content
291
+
292
+ ```tsx
293
+ import { getContentItem } from "@/lib/cms/getContentItem";
294
+ import { getContentList } from "@/lib/cms/getContentList";
295
+ import { AgilityPic } from "@agility/nextjs";
296
+ import type { ImageField } from "@agility/nextjs";
297
+
298
+ interface TestimonialsModule {
299
+ heading: string;
300
+ subheading: string;
301
+ testimonials: {
302
+ referencename: string; // Reference, not actual content
303
+ };
304
+ }
305
+
306
+ interface Testimonial {
307
+ quote: string;
308
+ name: string;
309
+ company: string;
310
+ avatar: ImageField;
311
+ rating: number;
312
+ }
313
+
314
+ export default async function TestimonialsGrid({ module, locale }: any) {
315
+ // Step 1: Get module configuration
316
+ const { fields } = await getContentItem<TestimonialsModule>({
317
+ contentID: module.contentid,
318
+ languageCode: locale,
319
+ });
320
+
321
+ // Step 2: Fetch the actual testimonials using the reference
322
+ const testimonials = await getContentList<Testimonial>({
323
+ referenceName: fields.testimonials.referencename,
324
+ languageCode: locale,
325
+ take: 12,
326
+ });
327
+
328
+ return (
329
+ <section>
330
+ <div className="text-center mb-12">
331
+ <h2>{fields.heading}</h2>
332
+ <p>{fields.subheading}</p>
333
+ </div>
334
+
335
+ <div className="grid md:grid-cols-3 gap-6">
336
+ {testimonials.map((testimonial) => (
337
+ <div key={testimonial.contentID} className="testimonial-card">
338
+ <div className="flex items-center gap-3 mb-4">
339
+ <AgilityPic
340
+ image={testimonial.fields.avatar}
341
+ fallbackWidth={64}
342
+ className="rounded-full"
343
+ />
344
+ <div>
345
+ <p className="font-bold">{testimonial.fields.name}</p>
346
+ <p className="text-sm">{testimonial.fields.company}</p>
347
+ </div>
348
+ </div>
349
+ <p className="italic">"{testimonial.fields.quote}"</p>
350
+ <div className="stars">
351
+ {"★".repeat(testimonial.fields.rating)}
352
+ </div>
353
+ </div>
354
+ ))}
355
+ </div>
356
+ </section>
357
+ );
358
+ }
359
+ ```
360
+
361
+ ### Example 3: Mixed - Both Types in One Component
362
+
363
+ ```tsx
364
+ import { getContentItem } from "@/lib/cms/getContentItem";
365
+ import { getContentList } from "@/lib/cms/getContentList";
366
+ import type { ContentItem, ImageField } from "@agility/nextjs";
367
+
368
+ interface FeaturedSection {
369
+ title: string;
370
+ featuredPost: ContentItem<BlogPost>; // Auto-populated single item
371
+ relatedPosts: {
372
+ referencename: string; // Reference to list
373
+ };
374
+ }
375
+
376
+ interface BlogPost {
377
+ title: string;
378
+ excerpt: string;
379
+ image: ImageField;
380
+ slug: string;
381
+ }
382
+
383
+ export default async function FeaturedSection({ module, locale }: any) {
384
+ // Get module data
385
+ const { fields } = await getContentItem<FeaturedSection>({
386
+ contentID: module.contentid,
387
+ languageCode: locale,
388
+ });
389
+
390
+ // Fetch the referenced list
391
+ const relatedPosts = await getContentList<BlogPost>({
392
+ referenceName: fields.relatedPosts.referencename,
393
+ languageCode: locale,
394
+ take: 3,
395
+ });
396
+
397
+ return (
398
+ <section>
399
+ <h2>{fields.title}</h2>
400
+
401
+ {/* ✅ Featured post is auto-populated - use directly */}
402
+ {fields.featuredPost && (
403
+ <div className="featured-post">
404
+ <h3>{fields.featuredPost.fields.title}</h3>
405
+ <p>{fields.featuredPost.fields.excerpt}</p>
406
+ <a href={`/blog/${fields.featuredPost.fields.slug}`}>Read more</a>
407
+ </div>
408
+ )}
409
+
410
+ {/* ⚠️ Related posts were fetched separately */}
411
+ <div className="grid grid-cols-3 gap-4">
412
+ {relatedPosts.map((post) => (
413
+ <article key={post.contentID}>
414
+ <h4>{post.fields.title}</h4>
415
+ <p>{post.fields.excerpt}</p>
416
+ </article>
417
+ ))}
418
+ </div>
419
+ </section>
420
+ );
421
+ }
422
+ ```
423
+
424
+ ## TypeScript Typing Best Practices
425
+
426
+ ### For Auto-Populated Content
427
+
428
+ ```tsx
429
+ import type { ContentItem } from "@agility/nextjs";
430
+
431
+ interface Author {
432
+ name: string;
433
+ email: string;
434
+ }
435
+
436
+ interface MyModule {
437
+ title: string;
438
+ author: ContentItem<Author>; // Type the linked content
439
+ }
440
+ ```
441
+
442
+ ### For Reference-Based Content
443
+
444
+ ```tsx
445
+ interface MyModule {
446
+ title: string;
447
+ items: {
448
+ referencename: string; // This is just a string reference
449
+ sortids?: string;
450
+ };
451
+ }
452
+ ```
453
+
454
+ ## Performance Considerations
455
+
456
+ ### Auto-Populated Content
457
+
458
+ **Pros:**
459
+ - Single API call - already included
460
+ - No additional fetch latency
461
+ - Simpler code
462
+
463
+ **Cons:**
464
+ - Included even if you don't need it
465
+ - Can increase payload size
466
+
467
+ ### Reference-Based Content
468
+
469
+ **Pros:**
470
+ - Flexible - fetch only when needed
471
+ - Can control what data is fetched
472
+ - Can add filters, sorts, pagination
473
+
474
+ **Cons:**
475
+ - Additional API call required
476
+ - Slightly more complex code
477
+ - Must remember to fetch
478
+
479
+ ### Parallel Fetching for Multiple References
480
+
481
+ ```tsx
482
+ export default async function Dashboard({ module, locale }: any) {
483
+ const { fields } = await getContentItem({
484
+ contentID: module.contentid,
485
+ languageCode: locale,
486
+ });
487
+
488
+ // Fetch multiple referenced lists in parallel
489
+ const [testimonials, teamMembers, stats] = await Promise.all([
490
+ getContentList({
491
+ referenceName: fields.testimonials.referencename,
492
+ languageCode: locale,
493
+ }),
494
+ getContentList({
495
+ referenceName: fields.team.referencename,
496
+ languageCode: locale,
497
+ }),
498
+ getContentList({
499
+ referenceName: fields.stats.referencename,
500
+ languageCode: locale,
501
+ }),
502
+ ]);
503
+
504
+ return (
505
+ <div>
506
+ {/* Use the fetched data */}
507
+ </div>
508
+ );
509
+ }
510
+ ```
511
+
512
+ ## Decision Tree: Which Approach?
513
+
514
+ ```
515
+ Is the field a Linked Content (Grid) or Content Link (Multi)?
516
+
517
+ ├─ YES → It's reference-based
518
+ │ │
519
+ │ └─ Must fetch using:
520
+ │ await getContentList({
521
+ │ referenceName: field.referencename,
522
+ │ languageCode: locale
523
+ │ })
524
+
525
+ └─ NO → It's auto-populated
526
+
527
+ └─ Access directly:
528
+ field.fields.propertyName
529
+ ```
530
+
531
+ ## Common Patterns Summary
532
+
533
+ ### Pattern 1: Direct Access (Auto-Populated)
534
+
535
+ ```tsx
536
+ // Single linked item
537
+ const authorName = post.fields.author.fields.name;
538
+
539
+ // Multiple linked items (array)
540
+ post.fields.tags.map((tag) => tag.fields.name);
541
+ ```
542
+
543
+ ### Pattern 2: Fetch by Reference
544
+
545
+ ```tsx
546
+ // Get reference name first
547
+ const { fields } = await getContentItem({ contentID, languageCode });
548
+
549
+ // Then fetch the list
550
+ const items = await getContentList({
551
+ referenceName: fields.myList.referencename,
552
+ languageCode,
553
+ });
554
+ ```
555
+
556
+ ### Pattern 3: Conditional Fetching
557
+
558
+ ```tsx
559
+ // Only fetch if reference exists
560
+ const items = fields.optionalList?.referencename
561
+ ? await getContentList({
562
+ referenceName: fields.optionalList.referencename,
563
+ languageCode: locale,
564
+ })
565
+ : [];
566
+ ```
567
+
568
+ ## Debugging Tips
569
+
570
+ ### Check What You Have
571
+
572
+ ```tsx
573
+ console.log("Field keys:", Object.keys(module.fields.myField));
574
+
575
+ // Auto-populated will show: ['contentID', 'fields', ...]
576
+ // Reference-based will show: ['referencename', 'sortids', ...]
577
+ ```
578
+
579
+ ### Verify Data Structure
580
+
581
+ ```tsx
582
+ // Auto-populated
583
+ if (module.fields.author?.fields) {
584
+ console.log("✅ Auto-populated linked content");
585
+ }
586
+
587
+ // Reference-based
588
+ if (module.fields.items?.referencename) {
589
+ console.log("⚠️ Reference-based - need to fetch");
590
+ }
591
+ ```
592
+
593
+ ## Summary
594
+
595
+ | Aspect | Auto-Populated | Reference-Based |
596
+ |--------|---------------|-----------------|
597
+ | **Field Types** | Content Link, Dropdown, Checkbox, Search List Box | Linked Content (Grid), Content Link (Multi) |
598
+ | **Data Structure** | `{ contentID, fields: {...} }` | `{ referencename: "..." }` |
599
+ | **Access Pattern** | Direct: `field.fields.name` | Fetch: `getContentList({ referenceName })` |
600
+ | **API Calls** | 0 (included) | 1 (separate fetch required) |
601
+ | **When to Use** | Single related items | Lists/grids of items |
602
+
603
+ ## Key Takeaways
604
+
605
+ 1. ✅ **Auto-populated links** (dropdowns, checkboxes, etc.) - use directly with `.fields`
606
+ 2. ⚠️ **Reference-based links** (grids, linked content) - fetch using `.referencename`
607
+ 3. 🔍 **Check the field structure** to determine which type you have
608
+ 4. 📦 **Use TypeScript** to document which type each field is
609
+ 5. ⚡ **Use Promise.all()** when fetching multiple referenced lists
610
+
611
+ Following these patterns ensures correct data access and optimal performance!