@measured/puck 0.12.0-canary.afd6a9d → 0.12.0-canary.c7daf5e
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.
- package/README.md +20 -486
- package/dist/{Config-60a50493.d.ts → Config-a03de579.d.ts} +1 -0
- package/dist/index.css +287 -150
- package/dist/index.d.ts +4 -4
- package/dist/index.js +407 -517
- package/dist/rsc.d.ts +1 -1
- package/package.json +22 -1
package/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# Puck
|
2
2
|
|
3
3
|
The self-hosted, drag and drop editor for React.
|
4
4
|
|
@@ -17,25 +17,30 @@ The self-hosted, drag and drop editor for React.
|
|
17
17
|
</a>
|
18
18
|
</p>
|
19
19
|
|
20
|
-
##
|
20
|
+
## Demo
|
21
21
|
|
22
|
-
|
23
|
-
- 🌐 **Integrations**: Load your content from a 3rd party headless CMS
|
24
|
-
- ✍️ **Inline editing**: Author content directly via puck for convenience
|
25
|
-
- ⭐️ **No vendor lock-in**: Self-host or integrate with your existing application
|
22
|
+
Visit https://demo.puckeditor.com/edit to try the demo.
|
26
23
|
|
27
|
-
|
24
|
+
## Documentation
|
28
25
|
|
29
|
-
|
26
|
+
Visit https://puckeditor.com to view the full documentation.
|
27
|
+
|
28
|
+
## Quick start
|
29
|
+
|
30
|
+
Install the package:
|
31
|
+
|
32
|
+
```sh
|
33
|
+
npm i @measured/puck --save # or npx create-puck-app my-app
|
34
|
+
```
|
30
35
|
|
31
36
|
Render the editor:
|
32
37
|
|
33
38
|
```jsx
|
34
39
|
// Editor.jsx
|
35
40
|
import { Puck } from "@measured/puck";
|
36
|
-
import "@measured/puck/
|
41
|
+
import "@measured/puck/puck.css";
|
37
42
|
|
38
|
-
// Create
|
43
|
+
// Create Puck component config
|
39
44
|
const config = {
|
40
45
|
components: {
|
41
46
|
HeadingBlock: {
|
@@ -71,495 +76,24 @@ Render the page:
|
|
71
76
|
```jsx
|
72
77
|
// Page.jsx
|
73
78
|
import { Render } from "@measured/puck";
|
74
|
-
import "@measured/puck/
|
79
|
+
import "@measured/puck/puck.css";
|
75
80
|
|
76
81
|
export function Page() {
|
77
82
|
return <Render config={config} data={data} />;
|
78
83
|
}
|
79
84
|
```
|
80
85
|
|
81
|
-
##
|
82
|
-
|
83
|
-
Install the package
|
84
|
-
|
85
|
-
```
|
86
|
-
npm i @measured/puck --save
|
87
|
-
```
|
86
|
+
## Recipes
|
88
87
|
|
89
|
-
|
88
|
+
Use `create-puck-app` to quickly spin up a a pre-configured app based on our provided [recipes](https://github.com/measuredco/puck/tree/main/recipes):
|
90
89
|
|
91
90
|
```sh
|
92
91
|
npx create-puck-app my-app
|
93
92
|
```
|
94
93
|
|
95
|
-
|
96
|
-
|
97
|
-
Puck is a React component that can be easily integrated into your existing application. We also provide helpful recipes for common use cases:
|
98
|
-
|
99
|
-
- [**next**](https://github.com/measuredco/puck/tree/main/recipes/next): Next.js app example
|
100
|
-
|
101
|
-
## Plugins
|
102
|
-
|
103
|
-
Puck can be configured to work with plugins. Plugins can extend the functionality to support novel functionality.
|
104
|
-
|
105
|
-
### Official plugins
|
106
|
-
|
107
|
-
- [`heading-analyzer`](https://github.com/measuredco/puck/tree/main/packages/plugin-heading-analyzer): Analyze the heading outline of your page and be warned when you're not respecting WCAG 2 accessibility standards.
|
108
|
-
|
109
|
-
### Developing a plugin
|
110
|
-
|
111
|
-
The plugin API follows a React paradigm. Each plugin passed to the Puck editor can provide three functions:
|
112
|
-
|
113
|
-
- `renderRoot` (`Component`): Render the root node of the preview content
|
114
|
-
- `renderRootFields` (`Component`): Render the root fields
|
115
|
-
- `renderFields` (`Component`): Render the fields for the currently selected component
|
116
|
-
- `renderComponentList` (`Component`): Render the component list
|
117
|
-
|
118
|
-
Each render function receives three props:
|
119
|
-
|
120
|
-
- **children** (`ReactNode`): The normal contents of the root or field. You must render this if provided.
|
121
|
-
- **state** (`AppState`): The current application state, including data and UI state
|
122
|
-
- **dispatch** (`(action: PuckAction) => void`): The Puck dispatcher, used for making data changes or updating the UI. See the [action definitions](https://github.com/measuredco/puck/blob/main/packages/core/reducer/actions.tsx) for a full reference of available mutations.
|
123
|
-
|
124
|
-
#### Example
|
125
|
-
|
126
|
-
Here's an example plugin that creates a button to toggle the left side-bar:
|
127
|
-
|
128
|
-
```jsx
|
129
|
-
const myPlugin = {
|
130
|
-
renderRootFields: ({ children, dispatch, state }) => (
|
131
|
-
<div>
|
132
|
-
{children}
|
133
|
-
|
134
|
-
<button
|
135
|
-
onClick={() => {
|
136
|
-
dispatch({
|
137
|
-
type: "setUi",
|
138
|
-
ui: { leftSideBarVisible: !state.ui.leftSideBarVisible },
|
139
|
-
});
|
140
|
-
}}
|
141
|
-
>
|
142
|
-
Toggle side-bar
|
143
|
-
</button>
|
144
|
-
</div>
|
145
|
-
),
|
146
|
-
};
|
147
|
-
```
|
148
|
-
|
149
|
-
## Custom fields
|
150
|
-
|
151
|
-
Puck supports custom fields using the `custom` field type and `render` method.
|
152
|
-
|
153
|
-
In this example, we optionally add the `<FieldLabel>` component to add a label:
|
154
|
-
|
155
|
-
```tsx
|
156
|
-
import { FieldLabel } from "@measured/puck";
|
157
|
-
|
158
|
-
export const MyComponent: ComponentConfig = {
|
159
|
-
fields: {
|
160
|
-
myField: {
|
161
|
-
type: "custom",
|
162
|
-
render: ({ field, name, onChange, value }) => {
|
163
|
-
return (
|
164
|
-
<FieldLabel label={field.label || name}>
|
165
|
-
<input
|
166
|
-
placeholder="Enter text..."
|
167
|
-
type="text"
|
168
|
-
name={name}
|
169
|
-
defaultValue={value}
|
170
|
-
onChange={(e) => onChange(e.currentTarget.value)}
|
171
|
-
></input>
|
172
|
-
</FieldLabel>
|
173
|
-
);
|
174
|
-
},
|
175
|
-
},
|
176
|
-
},
|
177
|
-
};
|
178
|
-
```
|
179
|
-
|
180
|
-
## DropZones
|
181
|
-
|
182
|
-
Puck supports creating complex layouts (like multi-column layouts) using the `<DropZone>` component.
|
183
|
-
|
184
|
-
### Example
|
185
|
-
|
186
|
-
In this example, we use the `<DropZone>` component to render two nested DropZones within another component:
|
187
|
-
|
188
|
-
```tsx
|
189
|
-
import { DropZone } from "@measured/puck";
|
190
|
-
|
191
|
-
export const MyComponent: ComponentConfig = {
|
192
|
-
render: () => {
|
193
|
-
return (
|
194
|
-
<div>
|
195
|
-
<DropZone zone="first-drop-zone">
|
196
|
-
<DropZone zone="second-drop-zone">
|
197
|
-
</div>
|
198
|
-
)
|
199
|
-
}
|
200
|
-
};
|
201
|
-
```
|
202
|
-
|
203
|
-
### Custom root entry points
|
204
|
-
|
205
|
-
You can also do this at the root of your component. This is useful if you have a fixed layout and only want to make certain parts of your page customisable:
|
206
|
-
|
207
|
-
```tsx
|
208
|
-
import { DropZone, Config } from "@measured/puck";
|
209
|
-
|
210
|
-
export const config: Config = {
|
211
|
-
root: {
|
212
|
-
render: ({ children }) => {
|
213
|
-
return (
|
214
|
-
<div>
|
215
|
-
{/* children renders the default zone. This can be omitted if necessary. */}
|
216
|
-
{children}
|
217
|
-
|
218
|
-
<div>
|
219
|
-
<DropZone zone="other-drop-zone">
|
220
|
-
</div>
|
221
|
-
</div>
|
222
|
-
)
|
223
|
-
}
|
224
|
-
}
|
225
|
-
};
|
226
|
-
```
|
227
|
-
|
228
|
-
### The Rules of DropZones
|
229
|
-
|
230
|
-
The current DropZone implementation has certain rules and limitations:
|
231
|
-
|
232
|
-
1. You can drag from the component list on the LHS into any DropZone
|
233
|
-
2. You can drag components between DropZones, so long as those DropZones share a parent (also known as _area_)
|
234
|
-
3. You can't drag between DropZones that don't share a parent (or _area_)
|
235
|
-
4. Your mouse must be directly over a DropZone for a collision to be detected
|
236
|
-
|
237
|
-
## External fields
|
238
|
-
|
239
|
-
External fields can be used to import data from a third-party API, such as a headless CMS.
|
240
|
-
|
241
|
-
### Example
|
242
|
-
|
243
|
-
The `external` field type enables us to query data from a third party API:
|
244
|
-
|
245
|
-
```tsx
|
246
|
-
const config = {
|
247
|
-
components: {
|
248
|
-
HeadingBlock: {
|
249
|
-
fields: {
|
250
|
-
myData: {
|
251
|
-
type: "external",
|
252
|
-
fetchList: async () => {
|
253
|
-
const response = await fetch("https://www.example.com/api");
|
254
|
-
|
255
|
-
return {
|
256
|
-
text: response.json().text,
|
257
|
-
};
|
258
|
-
},
|
259
|
-
},
|
260
|
-
},
|
261
|
-
render: ({ myData }) => {
|
262
|
-
return <h1>{myData.text}</h1>;
|
263
|
-
},
|
264
|
-
},
|
265
|
-
},
|
266
|
-
};
|
267
|
-
```
|
268
|
-
|
269
|
-
When the user interacts with this external field, they'll be presented with a list of items to choose from. Once they select an item, the value will be mapped onto the prop. In this case, `myData`.
|
270
|
-
|
271
|
-
## Dynamic prop resolution
|
272
|
-
|
273
|
-
Dynamic prop resolution allows developers to change the props for a component after the props have been changed by the user. This is useful for making third-party API calls, such as requesting the latest content from a headless CMS.
|
274
|
-
|
275
|
-
### resolveData()
|
276
|
-
|
277
|
-
`resolveData` is defined in the component config, and allows the developer to make asynchronous calls to change the [ComponentData](#componentdata) after they've been set by Puck. Receives [ComponentData](#componentdata) and returns [ComponentData](#componentdata).
|
278
|
-
|
279
|
-
#### Examples
|
280
|
-
|
281
|
-
##### Basic example
|
282
|
-
|
283
|
-
In this example, we remap the `text` prop to the `title` prop and mark the `title` field as read-only.
|
284
|
-
|
285
|
-
```tsx
|
286
|
-
const config = {
|
287
|
-
components: {
|
288
|
-
HeadingBlock: {
|
289
|
-
fields: {
|
290
|
-
text: {
|
291
|
-
type: "text",
|
292
|
-
},
|
293
|
-
title: {
|
294
|
-
type: "text",
|
295
|
-
},
|
296
|
-
},
|
297
|
-
resolveData: async (props) => {
|
298
|
-
return {
|
299
|
-
props: {
|
300
|
-
title: props.text,
|
301
|
-
},
|
302
|
-
readOnly: {
|
303
|
-
title: true,
|
304
|
-
},
|
305
|
-
};
|
306
|
-
},
|
307
|
-
render: ({ title }) => {
|
308
|
-
return <h1>{title}</h1>;
|
309
|
-
},
|
310
|
-
},
|
311
|
-
},
|
312
|
-
};
|
313
|
-
```
|
314
|
-
|
315
|
-
##### Combining with external fields
|
316
|
-
|
317
|
-
A more advanced pattern is to combine the `resolveData` method with `external` fields to dynamically fetch data when rendering the component.
|
318
|
-
|
319
|
-
```tsx
|
320
|
-
const config = {
|
321
|
-
components: {
|
322
|
-
HeadingBlock: {
|
323
|
-
fields: {
|
324
|
-
myData: {
|
325
|
-
type: "external",
|
326
|
-
placeholder: "Select from example.com",
|
327
|
-
fetchList: async () => {
|
328
|
-
const response = await fetch("https://www.example.com/api");
|
329
|
-
|
330
|
-
return {
|
331
|
-
id: response.json().id,
|
332
|
-
};
|
333
|
-
},
|
334
|
-
},
|
335
|
-
title: {
|
336
|
-
type: "text",
|
337
|
-
},
|
338
|
-
},
|
339
|
-
resolveData: async (props) => {
|
340
|
-
if (!myData.id) {
|
341
|
-
return { props, readOnly: { title: false } };
|
342
|
-
}
|
343
|
-
|
344
|
-
const latestData = await fetch(
|
345
|
-
`https://www.example.com/api/${myData.id}`
|
346
|
-
);
|
347
|
-
|
348
|
-
return {
|
349
|
-
props: {
|
350
|
-
title: latestData.json().text,
|
351
|
-
},
|
352
|
-
readOnly: {
|
353
|
-
title: true,
|
354
|
-
},
|
355
|
-
};
|
356
|
-
},
|
357
|
-
render: ({ title }) => {
|
358
|
-
return <h1>{title}</h1>;
|
359
|
-
},
|
360
|
-
},
|
361
|
-
},
|
362
|
-
};
|
363
|
-
```
|
364
|
-
|
365
|
-
### resolveAllData()
|
366
|
-
|
367
|
-
`resolveAllData` is a utility function exported by Puck to enable the developer to run all their `resolveData` methods before rendering the component with `<Render>`.
|
368
|
-
|
369
|
-
If your `resolveData` methods rely on any external APIs, you should run this before rendering your page.
|
370
|
-
|
371
|
-
```tsx
|
372
|
-
import { resolveAllData } from "@measured/puck";
|
373
|
-
|
374
|
-
const resolvedData = resolveAllData(data, config);
|
375
|
-
```
|
376
|
-
|
377
|
-
### React server components
|
378
|
-
|
379
|
-
If you want to use React server components, use ` <Render>` from the `@measured/puck/rsc` bundle instead of the main bundle.
|
380
|
-
|
381
|
-
```tsx
|
382
|
-
import { Render } from "@measured/puck/rsc";
|
383
|
-
import "@measured/puck/dist/index.css";
|
384
|
-
|
385
|
-
export function Page() {
|
386
|
-
return <Render config={config} data={data} />;
|
387
|
-
}
|
388
|
-
```
|
389
|
-
|
390
|
-
If you're using DropZones with React server components, use the `puck.renderDropZone` prop provided to your render function instead of the `<DropZone>` component.
|
391
|
-
|
392
|
-
```tsx
|
393
|
-
export const MyComponent: ComponentConfig = {
|
394
|
-
render: ({ puck: { renderDropZone } }) => {
|
395
|
-
return (
|
396
|
-
<div>
|
397
|
-
{renderDropZone({ zone: "first-drop-zone" })}
|
398
|
-
{renderDropZone({ zone: "second-drop-zone" })}
|
399
|
-
</div>
|
400
|
-
);
|
401
|
-
},
|
402
|
-
};
|
403
|
-
```
|
404
|
-
|
405
|
-
In future, we may deprecate DropZone in favour of renderDropZone.
|
406
|
-
|
407
|
-
## Reference
|
408
|
-
|
409
|
-
### `<Puck>`
|
410
|
-
|
411
|
-
The `<Puck>` component renders the Puck editor.
|
412
|
-
|
413
|
-
- **config** (`Config`): Puck component configuration
|
414
|
-
- **data** (`Data`): Initial data to render
|
415
|
-
- **onChange** (`(Data) => void` [optional]): Callback that triggers when the user makes a change
|
416
|
-
- **onPublish** (`(Data) => void` [optional]): Callback that triggers when the user hits the "Publish" button
|
417
|
-
- **renderComponentList** (`Component` [optional]): Render function for wrapping the component list
|
418
|
-
- **renderHeader** (`Component` [optional]): Render function for overriding the Puck header component
|
419
|
-
- **renderHeaderActions** (`Component` [optional]): Render function for overriding the Puck header actions. Use a fragment.
|
420
|
-
- **headerTitle** (`string` [optional]): Set the title shown in the header title
|
421
|
-
- **headerPath** (`string` [optional]): Set a path to show after the header title
|
422
|
-
- **plugins** (`Plugin[]` [optional]): Array of plugins that can be used to enhance Puck
|
423
|
-
|
424
|
-
### `<Render>`
|
425
|
-
|
426
|
-
The `<Render>` component renders user-facing UI using Puck data.
|
427
|
-
|
428
|
-
- **config** (`Config`): Puck component configuration
|
429
|
-
- **data** (`Data`): Data to render
|
430
|
-
|
431
|
-
### `<DropZone>`
|
432
|
-
|
433
|
-
The `<DropZone>` component allows you to create advanced layouts, like multi-columns.
|
434
|
-
|
435
|
-
- **zone** (`string`): Identifier for the zone of your component, unique to the parent component
|
436
|
-
- **style** (`CSSProperties`): Custom inline styles
|
437
|
-
|
438
|
-
### `Config`
|
439
|
-
|
440
|
-
The `Config` object describes which components Puck should render, how they should render and which inputs are available to them.
|
441
|
-
|
442
|
-
- **root** (`object`)
|
443
|
-
- **fields** (`object`):
|
444
|
-
- **title** (`Field`): Title of the content, typically used for the page title.
|
445
|
-
- **[fieldName]** (`Field`): User defined fields, used to describe the input data stored in the `root` key.
|
446
|
-
- **render** (`Component`): Render a React component at the root of your component tree. Useful for defining context providers.
|
447
|
-
- **resolveData** (`async (data: ComponentData) => ComponentData` [optional]): Function to dynamically change props before rendering the root.
|
448
|
-
- **components** (`object`): Definitions for each of the components you want to show in the visual editor
|
449
|
-
- **[componentName]** (`object`)
|
450
|
-
- **fields** (`Field`): The Field objects describing the input data stored against this component.
|
451
|
-
- **render** (`Component`): Render function for your React component. Receives props as defined in fields.
|
452
|
-
- **defaultProps** (`object` [optional]): Default props to pass to your component. Will show in fields.
|
453
|
-
- **resolveData** (`async (data: ComponentData) => ComponentData` [optional]): Function to dynamically change props before rendering the component.
|
454
|
-
- **categories** (`object`): Component categories for rendering in the side bar or restricting in DropZones
|
455
|
-
- **[categoryName]** (`object`)
|
456
|
-
- **components** (`sting[]`, [optional]): Array containing the names of components in this category
|
457
|
-
- **title** (`sting`, [optional]): Title of the category
|
458
|
-
- **visible** (`boolean`, [optional]): Whether or not the category should be visible in the side bar
|
459
|
-
- **defaultExpanded** (`boolean`, [optional]): Whether or not the category should be expanded in the side bar by default
|
460
|
-
|
461
|
-
### `Field`
|
462
|
-
|
463
|
-
A `Field` represents a user input field shown in the Puck interface.
|
464
|
-
|
465
|
-
### All Fields
|
466
|
-
|
467
|
-
- **label** (`text` [optional]): A label for the input. Will use the key if not provided.
|
468
|
-
|
469
|
-
### Text Fields
|
470
|
-
|
471
|
-
- **type** (`"text"`)
|
472
|
-
|
473
|
-
### Textarea Fields
|
474
|
-
|
475
|
-
- **type** (`"textarea"`)
|
476
|
-
|
477
|
-
### Number Fields
|
478
|
-
|
479
|
-
- **type** (`"number"`)
|
480
|
-
|
481
|
-
### Select Fields
|
482
|
-
|
483
|
-
- **type** (`"select"`)
|
484
|
-
- **options** (`object[]`): array of items to render
|
485
|
-
- **label** (`string`)
|
486
|
-
- **value** (`string` | `number` | `boolean`)
|
487
|
-
|
488
|
-
### Radio Fields
|
489
|
-
|
490
|
-
- **type** (`"radio"`)
|
491
|
-
- **options** (`object[]`): array of items to render
|
492
|
-
- **label** (`string`)
|
493
|
-
- **value** (`string` | `number` | `boolean`)
|
494
|
-
|
495
|
-
### Array Fields
|
496
|
-
|
497
|
-
- **type** (`"array"`)
|
498
|
-
- **arrayFields** (`object`): Object describing sub-fields for each item
|
499
|
-
- **[fieldName]** (`Field`): The Field objects describing the input data for each item
|
500
|
-
- **getItemSummary** (`(object, number) => string` [optional]): Function to get the label of each item
|
501
|
-
- **defaultItemProps** (`object` [optional]): Default props to pass to each new item added, when using a `array` field type
|
502
|
-
|
503
|
-
### External Fields
|
504
|
-
|
505
|
-
External fields can be used to load content from an external content repository, like Strapi.js.
|
506
|
-
|
507
|
-
- **type** (`"external"`)
|
508
|
-
- **placeholder** (`string`): A placeholder for the external field button
|
509
|
-
- **fetchList** (`() => object`): Fetch content from a third-party API and return an array
|
510
|
-
- **mapProp** (`(selectedItem: object) => object`): Map the selected item into another shape
|
511
|
-
|
512
|
-
### Custom Fields
|
513
|
-
|
514
|
-
- **type** (`"custom"`)
|
515
|
-
- **render** (`Component`): Render a custom field. Receives the props:
|
516
|
-
- **field** (`Field`): Field configuration
|
517
|
-
- **name** (`string`): Name of the field
|
518
|
-
- **value** (`any`): Value for the field
|
519
|
-
- **onChange** (`(value: any) => void`): Callback to change the value
|
520
|
-
- **readOnly** (`boolean` | `undefined`): Whether or not the field should be in readOnly mode
|
521
|
-
|
522
|
-
### `AppState`
|
523
|
-
|
524
|
-
The `AppState` object stores the puck application state.
|
525
|
-
|
526
|
-
- **data** (`Data`): The page data currently being rendered
|
527
|
-
- **ui** (`object`):
|
528
|
-
- **leftSideBarVisible** (boolean): Whether or not the left side bar is visible
|
529
|
-
- **itemSelector** (object): An object describing which item is selected
|
530
|
-
- **arrayState** (object): An object describing the internal state of array items
|
531
|
-
- **componentList** (object): An object describing the component list. Similar shape to `Config.categories`.
|
532
|
-
- **components** (`sting[]`, [optional]): Array containing the names of components in this category
|
533
|
-
- **title** (`sting`, [optional]): Title of the category
|
534
|
-
- **visible** (`boolean`, [optional]): Whether or not the category is visible in the side bar
|
535
|
-
- **expanded** (`boolean`, [optional]): Whether or not the category is expanded in the side bar
|
536
|
-
|
537
|
-
### `Data`
|
538
|
-
|
539
|
-
The `Data` object stores the puck page data.
|
540
|
-
|
541
|
-
- **root** (`ComponentData`): The component data for the root of your configuration.
|
542
|
-
- **props** (object): Extends `ComponentData.props`, with some additional props
|
543
|
-
- **title** (`string`, [optional]): Title of the content, typically used for the page title
|
544
|
-
- **content** (`ComponentData[]`): Component data for the main content
|
545
|
-
- **zones** (`object`, [optional]): Component data for all DropZones
|
546
|
-
**[zoneCompound]** (`ComponentData[]`): Component data for a specific DropZone `zone` within a component instance
|
547
|
-
|
548
|
-
### `ComponentData`
|
549
|
-
|
550
|
-
- **type** (`string`): Component name
|
551
|
-
- **props** (`object`):
|
552
|
-
- **[prop]** (`any`): User defined data from component fields
|
553
|
-
- **readOnly** (`object`): Object describing which fields on the component are currently read-only. Can use dot-notation for arrays, like `array[1].text` or `array[*].text`.
|
554
|
-
- **[prop]** (`boolean`): boolean describing whether or not the prop field is read-only
|
555
|
-
|
556
|
-
### `Plugin`
|
557
|
-
|
558
|
-
Plugins that can be used to enhance Puck.
|
94
|
+
Available recipes include:
|
559
95
|
|
560
|
-
- **
|
561
|
-
- **renderRootFields** (`Component`): Render the root fields
|
562
|
-
- **renderFields** (`Component`): Render the fields for the currently selected component
|
96
|
+
- [**next**](https://github.com/measuredco/puck/tree/main/recipes/next): Next.js 13 app example, using App Router and static page generation
|
563
97
|
|
564
98
|
## Hire the Puck team
|
565
99
|
|