Repository: puckeditor/puck
Branch: main
Commit: edc819204441
Files: 719
Total size: 1.4 MB
Directory structure:
gitextract_c768lzuy/
├── .eslintrc.js
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── ci.yml
│ ├── publish-canary.yml
│ └── publish.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .prettierrc.json
├── .yarnrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── apps/
│ ├── demo/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── [...puckPath]/
│ │ │ │ ├── client.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── custom-ui/
│ │ │ │ └── [...puckPath]/
│ │ │ │ ├── client.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ ├── rsc/
│ │ │ │ └── page.tsx
│ │ │ └── styles.css
│ │ ├── config/
│ │ │ ├── blocks/
│ │ │ │ ├── Blank/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── Button/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Card/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── Flex/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── Grid/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── Heading/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── Hero/
│ │ │ │ │ ├── Hero.tsx
│ │ │ │ │ ├── client.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── quotes.ts
│ │ │ │ │ ├── server.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── Logos/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── RichText/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Space/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── Stats/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── Template/
│ │ │ │ │ ├── Template.tsx
│ │ │ │ │ ├── client.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── server.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ └── Text/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── components/
│ │ │ │ ├── Footer/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Header/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ ├── Layout/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ └── Section/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── index.tsx
│ │ │ ├── initial-data.ts
│ │ │ ├── options.ts
│ │ │ ├── root.tsx
│ │ │ ├── server.tsx
│ │ │ └── types.ts
│ │ ├── lib/
│ │ │ ├── resolve-puck-path.ts
│ │ │ └── use-demo-data.ts
│ │ ├── next-env.d.ts
│ │ ├── next.config.js
│ │ ├── package.json
│ │ ├── tsconfig/
│ │ │ ├── base.json
│ │ │ └── nextjs.json
│ │ └── tsconfig.json
│ └── docs/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── components/
│ │ ├── CtaCard/
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── DiscoveryButton/
│ │ │ └── index.tsx
│ │ ├── FooterActions/
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── Home/
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── Preview/
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── ReleaseSwitcher/
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ └── Viewport/
│ │ ├── index.tsx
│ │ └── styles.module.css
│ ├── middleware.ts
│ ├── next-env.d.ts
│ ├── next-sitemap.config.js
│ ├── next.config.mjs
│ ├── package.json
│ ├── pages/
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── _meta.js
│ │ ├── api/
│ │ │ └── releases.ts
│ │ ├── docs/
│ │ │ ├── _meta.js
│ │ │ ├── api-reference/
│ │ │ │ ├── _meta.js
│ │ │ │ ├── actions.mdx
│ │ │ │ ├── components/
│ │ │ │ │ ├── _meta.js
│ │ │ │ │ ├── action-bar-action.mdx
│ │ │ │ │ ├── action-bar-group.mdx
│ │ │ │ │ ├── action-bar-label.mdx
│ │ │ │ │ ├── action-bar-separator.mdx
│ │ │ │ │ ├── action-bar.mdx
│ │ │ │ │ ├── auto-field.mdx
│ │ │ │ │ ├── drawer-item.mdx
│ │ │ │ │ ├── drawer.mdx
│ │ │ │ │ ├── drop-zone.mdx
│ │ │ │ │ ├── field-label.mdx
│ │ │ │ │ ├── puck-components.mdx
│ │ │ │ │ ├── puck-fields.mdx
│ │ │ │ │ ├── puck-layout.mdx
│ │ │ │ │ ├── puck-outline.mdx
│ │ │ │ │ ├── puck-preview.mdx
│ │ │ │ │ ├── puck.mdx
│ │ │ │ │ ├── render.mdx
│ │ │ │ │ ├── rich-text-menu-control.mdx
│ │ │ │ │ ├── rich-text-menu-group.mdx
│ │ │ │ │ └── rich-text-menu.mdx
│ │ │ │ ├── components.mdx
│ │ │ │ ├── configuration/
│ │ │ │ │ ├── _meta.js
│ │ │ │ │ ├── component-config.mdx
│ │ │ │ │ └── config.mdx
│ │ │ │ ├── configuration.mdx
│ │ │ │ ├── data-model/
│ │ │ │ │ ├── app-state.mdx
│ │ │ │ │ ├── component-data.mdx
│ │ │ │ │ ├── data.mdx
│ │ │ │ │ ├── item-selector.mdx
│ │ │ │ │ └── root-data.mdx
│ │ │ │ ├── data-model.mdx
│ │ │ │ ├── field-transforms.mdx
│ │ │ │ ├── fields/
│ │ │ │ │ ├── _meta.js
│ │ │ │ │ ├── array.mdx
│ │ │ │ │ ├── base.mdx
│ │ │ │ │ ├── custom.mdx
│ │ │ │ │ ├── external.mdx
│ │ │ │ │ ├── number.mdx
│ │ │ │ │ ├── object.mdx
│ │ │ │ │ ├── radio.mdx
│ │ │ │ │ ├── richtext.mdx
│ │ │ │ │ ├── select.mdx
│ │ │ │ │ ├── slot.mdx
│ │ │ │ │ ├── text.mdx
│ │ │ │ │ └── textarea.mdx
│ │ │ │ ├── fields.mdx
│ │ │ │ ├── functions/
│ │ │ │ │ ├── migrate.mdx
│ │ │ │ │ ├── register-overlay-portal.mdx
│ │ │ │ │ ├── resolve-all-data.mdx
│ │ │ │ │ ├── set-deep.mdx
│ │ │ │ │ ├── transform-props.mdx
│ │ │ │ │ ├── use-get-puck.mdx
│ │ │ │ │ ├── use-puck.mdx
│ │ │ │ │ └── walk-tree.mdx
│ │ │ │ ├── functions.mdx
│ │ │ │ ├── overrides/
│ │ │ │ │ ├── action-bar.mdx
│ │ │ │ │ ├── component-overlay.mdx
│ │ │ │ │ ├── drawer-item.mdx
│ │ │ │ │ ├── drawer.mdx
│ │ │ │ │ ├── field-label.mdx
│ │ │ │ │ ├── field-types.mdx
│ │ │ │ │ ├── fields.mdx
│ │ │ │ │ ├── header-actions.mdx
│ │ │ │ │ ├── header.mdx
│ │ │ │ │ ├── iframe.mdx
│ │ │ │ │ ├── outline.mdx
│ │ │ │ │ ├── preview.mdx
│ │ │ │ │ └── puck.mdx
│ │ │ │ ├── overrides.mdx
│ │ │ │ ├── permissions.mdx
│ │ │ │ ├── plugin.mdx
│ │ │ │ ├── plugins/
│ │ │ │ │ ├── blocks-plugin.mdx
│ │ │ │ │ ├── fields-plugin.mdx
│ │ │ │ │ ├── legacy-side-bar-plugin.mdx
│ │ │ │ │ └── outline-plugin.mdx
│ │ │ │ ├── puck-api.mdx
│ │ │ │ └── theming.mdx
│ │ │ ├── extending-puck/
│ │ │ │ ├── _meta.js
│ │ │ │ ├── composition.mdx
│ │ │ │ ├── custom-fields.mdx
│ │ │ │ ├── field-transforms.mdx
│ │ │ │ ├── internal-puck-api.mdx
│ │ │ │ ├── plugins.mdx
│ │ │ │ ├── theming/
│ │ │ │ │ ├── _meta.js
│ │ │ │ │ ├── fonts.mdx
│ │ │ │ │ └── overview.mdx
│ │ │ │ └── ui-overrides.mdx
│ │ │ ├── getting-started.mdx
│ │ │ ├── guides/
│ │ │ │ ├── _meta.js
│ │ │ │ └── migrations/
│ │ │ │ ├── _meta.js
│ │ │ │ └── dropzones-to-slots.mdx
│ │ │ ├── index.mdx
│ │ │ └── integrating-puck/
│ │ │ ├── _meta.js
│ │ │ ├── categories.mdx
│ │ │ ├── component-configuration.mdx
│ │ │ ├── data-migration.mdx
│ │ │ ├── dynamic-fields.mdx
│ │ │ ├── dynamic-props.mdx
│ │ │ ├── external-data-sources.mdx
│ │ │ ├── feature-toggling.mdx
│ │ │ ├── multi-column-layouts.mdx
│ │ │ ├── overlay-portals.mdx
│ │ │ ├── rich-text-editing.mdx
│ │ │ ├── root-configuration.mdx
│ │ │ ├── server-components.mdx
│ │ │ └── viewports.mdx
│ │ └── index.mdx
│ ├── public/
│ │ ├── manifest.webmanifest
│ │ └── robots.txt
│ ├── releases.json
│ ├── styles.css
│ ├── theme.config.tsx
│ ├── tsconfig/
│ │ ├── base.json
│ │ └── nextjs.json
│ └── tsconfig.json
├── lerna.json
├── package.json
├── packages/
│ ├── core/
│ │ ├── .gitignore
│ │ ├── bundle/
│ │ │ ├── core.css
│ │ │ ├── core.ts
│ │ │ ├── index.css
│ │ │ ├── index.ts
│ │ │ ├── internal.ts
│ │ │ ├── no-external.css
│ │ │ ├── no-external.ts
│ │ │ └── rsc.tsx
│ │ ├── components/
│ │ │ ├── ActionBar/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── AutoField/
│ │ │ │ ├── FieldLabel.tsx
│ │ │ │ ├── context.tsx
│ │ │ │ ├── fields/
│ │ │ │ │ ├── ArrayField/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ ├── DefaultField/
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── ExternalField/
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── ObjectField/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ ├── RadioField/
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── RichtextField/
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── SelectField/
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── TextareaField/
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── lib/
│ │ │ │ │ ├── use-deep-field.ts
│ │ │ │ │ ├── use-is-focused.ts
│ │ │ │ │ └── use-local-value.ts
│ │ │ │ ├── store.ts
│ │ │ │ ├── styles.module.css
│ │ │ │ └── subfield.tsx
│ │ │ ├── AutoFrame/
│ │ │ │ └── index.tsx
│ │ │ ├── Breadcrumbs/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── Button/
│ │ │ │ ├── Button.module.css
│ │ │ │ ├── Button.tsx
│ │ │ │ └── index.ts
│ │ │ ├── ComponentList/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── DefaultOverride/
│ │ │ │ └── index.tsx
│ │ │ ├── DragDropContext/
│ │ │ │ └── index.tsx
│ │ │ ├── DragIcon/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── DraggableComponent/
│ │ │ │ ├── index.tsx
│ │ │ │ ├── styles.css
│ │ │ │ └── styles.module.css
│ │ │ ├── Drawer/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── DropZone/
│ │ │ │ ├── context.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── lib/
│ │ │ │ │ ├── use-content-with-preview.ts
│ │ │ │ │ ├── use-drag-axis.ts
│ │ │ │ │ └── use-min-empty-height.ts
│ │ │ │ ├── styles.module.css
│ │ │ │ └── types.ts
│ │ │ ├── ExternalInput/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── Heading/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── IconButton/
│ │ │ │ ├── IconButton.module.css
│ │ │ │ ├── IconButton.tsx
│ │ │ │ └── index.ts
│ │ │ ├── InlineTextField/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── LayerTree/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── Loader/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── MemoizeComponent/
│ │ │ │ └── index.tsx
│ │ │ ├── MenuBar/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── Modal/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── OutlineList/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── Puck/
│ │ │ │ ├── __tests__/
│ │ │ │ │ ├── __snapshots__/
│ │ │ │ │ │ └── index.tsx.snap
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── components/
│ │ │ │ │ ├── Canvas/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ ├── Components/
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Fields/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ ├── Header/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ ├── Layout/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ ├── Nav/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ ├── Outline/
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Preview/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ ├── ResizeHandle/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── styles.css
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ └── Sidebar/
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── styles.module.css
│ │ │ │ └── index.tsx
│ │ │ ├── Render/
│ │ │ │ └── index.tsx
│ │ │ ├── RichTextEditor/
│ │ │ │ ├── components/
│ │ │ │ │ ├── Editor.tsx
│ │ │ │ │ ├── EditorFallback.tsx
│ │ │ │ │ ├── EditorInner.tsx
│ │ │ │ │ ├── Render.tsx
│ │ │ │ │ └── RenderFallback.tsx
│ │ │ │ ├── extension.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── lib/
│ │ │ │ │ ├── mapDeep.ts
│ │ │ │ │ ├── use-richtext-props.tsx
│ │ │ │ │ └── use-synced-editor.ts
│ │ │ │ ├── selector.ts
│ │ │ │ ├── styles.module.css
│ │ │ │ └── types.ts
│ │ │ ├── RichTextMenu/
│ │ │ │ ├── components/
│ │ │ │ │ ├── Control/
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── styles.module.css
│ │ │ │ │ └── SelectControl/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── controls/
│ │ │ │ │ ├── AlignCenter.tsx
│ │ │ │ │ ├── AlignJustify.tsx
│ │ │ │ │ ├── AlignLeft.tsx
│ │ │ │ │ ├── AlignRight.tsx
│ │ │ │ │ ├── AlignSelect/
│ │ │ │ │ │ ├── fallback.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── loaded.tsx
│ │ │ │ │ │ └── use-options.ts
│ │ │ │ │ ├── Blockquote.tsx
│ │ │ │ │ ├── Bold.tsx
│ │ │ │ │ ├── BulletList.tsx
│ │ │ │ │ ├── CodeBlock.tsx
│ │ │ │ │ ├── HeadingSelect/
│ │ │ │ │ │ ├── fallback.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── loaded.tsx
│ │ │ │ │ │ └── use-options.ts
│ │ │ │ │ ├── HorizontalRule.tsx
│ │ │ │ │ ├── InlineCode.tsx
│ │ │ │ │ ├── Italic.tsx
│ │ │ │ │ ├── ListSelect/
│ │ │ │ │ │ ├── fallback.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── loaded.tsx
│ │ │ │ │ │ └── use-options.ts
│ │ │ │ │ ├── OrderedList.tsx
│ │ │ │ │ ├── Strikethrough.tsx
│ │ │ │ │ ├── Underline.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── full.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── inner.tsx
│ │ │ │ ├── lib/
│ │ │ │ │ └── use-control-context.ts
│ │ │ │ └── styles.module.css
│ │ │ ├── Select/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── ServerRender/
│ │ │ │ └── index.tsx
│ │ │ ├── SidebarSection/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── SlotRender/
│ │ │ │ ├── index.tsx
│ │ │ │ └── server.tsx
│ │ │ ├── Sortable/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.css
│ │ │ └── ViewportControls/
│ │ │ ├── default-viewports.ts
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── globals.d.ts
│ │ ├── index.ts
│ │ ├── jest.config.ts
│ │ ├── lib/
│ │ │ ├── __tests__/
│ │ │ │ ├── insert-component.spec.tsx
│ │ │ │ ├── load-overrides.spec.tsx
│ │ │ │ ├── migrate.spec.tsx
│ │ │ │ ├── move-component.spec.tsx
│ │ │ │ ├── resolve-all-data.spec.tsx
│ │ │ │ ├── resolve-component-data.spec.tsx
│ │ │ │ ├── transform-props.spec.tsx
│ │ │ │ └── use-breadcrumbs.spec.tsx
│ │ │ ├── accumulate-transform.ts
│ │ │ ├── assign-refs.ts
│ │ │ ├── bubble-pointer-event.ts
│ │ │ ├── data/
│ │ │ │ ├── __tests__/
│ │ │ │ │ ├── flatten-data.spec.tsx
│ │ │ │ │ ├── resolve-and-replace-data.spec.tsx
│ │ │ │ │ ├── resolve-data-by-id.spec.tsx
│ │ │ │ │ ├── resolve-data-by-selector.spec.tsx
│ │ │ │ │ ├── walk-app-state.spec.tsx
│ │ │ │ │ └── walk-tree.spec.tsx
│ │ │ │ ├── default-data.ts
│ │ │ │ ├── default-slots.ts
│ │ │ │ ├── find-zones-for-area.ts
│ │ │ │ ├── flatten-data.ts
│ │ │ │ ├── flatten-node.ts
│ │ │ │ ├── for-related-zones.ts
│ │ │ │ ├── get-deep.ts
│ │ │ │ ├── get-ids-for-parent.ts
│ │ │ │ ├── get-item.ts
│ │ │ │ ├── insert.ts
│ │ │ │ ├── make-state-public.ts
│ │ │ │ ├── map-fields.ts
│ │ │ │ ├── populate-ids.ts
│ │ │ │ ├── remove.ts
│ │ │ │ ├── reorder.ts
│ │ │ │ ├── replace.ts
│ │ │ │ ├── resolve-and-replace-data.ts
│ │ │ │ ├── resolve-data-by-id.ts
│ │ │ │ ├── resolve-data-by-selector.ts
│ │ │ │ ├── set-deep.ts
│ │ │ │ ├── setup-zone.ts
│ │ │ │ ├── strip-slots.ts
│ │ │ │ ├── to-component.ts
│ │ │ │ ├── to-root.ts
│ │ │ │ ├── walk-app-state.ts
│ │ │ │ └── walk-tree.ts
│ │ │ ├── dnd/
│ │ │ │ ├── NestedDroppablePlugin.ts
│ │ │ │ ├── collision/
│ │ │ │ │ ├── collision-debug.ts
│ │ │ │ │ ├── directional/
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ └── dynamic/
│ │ │ │ │ ├── get-direction.ts
│ │ │ │ │ ├── get-midpoint-impact.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── store.ts
│ │ │ │ │ └── track-movement-interval.ts
│ │ │ │ ├── use-on-drag-finished.ts
│ │ │ │ ├── use-rendered-callback.ts
│ │ │ │ └── use-sensors.ts
│ │ │ ├── field-transforms/
│ │ │ │ ├── build-mappers.ts
│ │ │ │ ├── default-transforms/
│ │ │ │ │ ├── inline-text-transform.tsx
│ │ │ │ │ ├── rich-text-transform.tsx
│ │ │ │ │ └── slot-transform.tsx
│ │ │ │ ├── use-field-transforms-tracked.tsx
│ │ │ │ └── use-field-transforms.tsx
│ │ │ ├── filter-data-attrs.ts
│ │ │ ├── filter.ts
│ │ │ ├── frame-context.tsx
│ │ │ ├── generate-id.ts
│ │ │ ├── get-changed.ts
│ │ │ ├── get-class-name-factory.ts
│ │ │ ├── get-deep-dir.ts
│ │ │ ├── get-deep-scroll-position.ts
│ │ │ ├── get-frame.ts
│ │ │ ├── get-selector-for-id.ts
│ │ │ ├── get-zone-id.ts
│ │ │ ├── get-zoom-config.ts
│ │ │ ├── global-position.ts
│ │ │ ├── group-zones-by-component.ts
│ │ │ ├── index.ts
│ │ │ ├── insert-component.ts
│ │ │ ├── is-ios.ts
│ │ │ ├── load-overrides.ts
│ │ │ ├── migrate.ts
│ │ │ ├── move-component.ts
│ │ │ ├── on-scroll-end.ts
│ │ │ ├── overlay-portal/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.css
│ │ │ ├── plugin-debug.tsx
│ │ │ ├── resolve-all-data.ts
│ │ │ ├── resolve-component-data.ts
│ │ │ ├── root-droppable-id.ts
│ │ │ ├── scroll-into-view.ts
│ │ │ ├── shallow-equal.ts
│ │ │ ├── throttle.ts
│ │ │ ├── transform-props.ts
│ │ │ ├── use-breadcrumbs.ts
│ │ │ ├── use-component-list.tsx
│ │ │ ├── use-context-store.tsx
│ │ │ ├── use-delete-hotkeys.ts
│ │ │ ├── use-frame.ts
│ │ │ ├── use-hotkey.ts
│ │ │ ├── use-inject-css.ts
│ │ │ ├── use-loaded-overrides.ts
│ │ │ ├── use-on-value-change.ts
│ │ │ ├── use-parent.ts
│ │ │ ├── use-preview-mode-hotkeys.ts
│ │ │ ├── use-puck.ts
│ │ │ ├── use-reset-auto-zoom.ts
│ │ │ ├── use-safe-id.ts
│ │ │ ├── use-sidebar-resize.ts
│ │ │ ├── use-slots.tsx
│ │ │ └── use-why-render.ts
│ │ ├── package.json
│ │ ├── plugins/
│ │ │ ├── blocks/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── fields/
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── legacy-side-bar/
│ │ │ │ └── index.tsx
│ │ │ └── outline/
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── reducer/
│ │ │ ├── actions/
│ │ │ │ ├── __helpers__/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── __tests__/
│ │ │ │ │ ├── duplicate.spec.ts
│ │ │ │ │ ├── insert.spec.ts
│ │ │ │ │ ├── move.spec.ts
│ │ │ │ │ ├── register-zone.spec.ts
│ │ │ │ │ ├── remove.spec.ts
│ │ │ │ │ ├── reorder.spec.ts
│ │ │ │ │ ├── replace.spec.ts
│ │ │ │ │ ├── set-ui.spec.ts
│ │ │ │ │ └── set.spec.ts
│ │ │ │ ├── duplicate.ts
│ │ │ │ ├── insert.ts
│ │ │ │ ├── move.ts
│ │ │ │ ├── register-zone.ts
│ │ │ │ ├── remove.ts
│ │ │ │ ├── reorder.ts
│ │ │ │ ├── replace-root.ts
│ │ │ │ ├── replace.ts
│ │ │ │ ├── set-data.ts
│ │ │ │ ├── set-ui.ts
│ │ │ │ └── set.ts
│ │ │ ├── actions.tsx
│ │ │ └── index.ts
│ │ ├── store/
│ │ │ ├── default-app-state.ts
│ │ │ ├── index.ts
│ │ │ └── slices/
│ │ │ ├── __tests__/
│ │ │ │ ├── fields.spec.tsx
│ │ │ │ ├── history.spec.tsx
│ │ │ │ ├── nodes.spec.tsx
│ │ │ │ └── permissions.spec.tsx
│ │ │ ├── fields.ts
│ │ │ ├── history.ts
│ │ │ ├── nodes.ts
│ │ │ └── permissions.ts
│ │ ├── styles/
│ │ │ ├── color.css
│ │ │ └── typography.css
│ │ ├── styles.css
│ │ ├── tsconfig.json
│ │ ├── tsup.config.ts
│ │ └── types/
│ │ ├── API/
│ │ │ ├── DropZone.ts
│ │ │ ├── FieldTransforms.ts
│ │ │ ├── Overrides.ts
│ │ │ ├── Viewports.ts
│ │ │ └── index.ts
│ │ ├── AppState.tsx
│ │ ├── Config.tsx
│ │ ├── Data.tsx
│ │ ├── Fields.ts
│ │ ├── Internal.tsx
│ │ ├── Props.tsx
│ │ ├── Utils.tsx
│ │ ├── __tests__/
│ │ │ └── internal.spec.ts
│ │ └── index.ts
│ ├── create-puck-app/
│ │ ├── README.md
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── scripts/
│ │ │ └── generate.js
│ │ └── templates/
│ │ ├── next/
│ │ │ └── package.json.hbs
│ │ ├── next-ai/
│ │ │ └── package.json.hbs
│ │ ├── react-router/
│ │ │ ├── package.json.hbs
│ │ │ └── tsconfig.json.hbs
│ │ ├── react-router-ai/
│ │ │ ├── package.json.hbs
│ │ │ └── tsconfig.json.hbs
│ │ ├── remix/
│ │ │ └── package.json.hbs
│ │ └── remix-ai/
│ │ └── package.json.hbs
│ ├── eslint-config-custom/
│ │ ├── index.js
│ │ └── package.json
│ ├── field-contentful/
│ │ ├── README.md
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── plugin-emotion-cache/
│ │ ├── README.md
│ │ ├── index.tsx
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── plugin-heading-analyzer/
│ │ ├── README.md
│ │ ├── globals.d.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── HeadingAnalyzer.module.css
│ │ │ └── HeadingAnalyzer.tsx
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── tsconfig/
│ │ ├── base.json
│ │ ├── nextjs.json
│ │ ├── package.json
│ │ └── react-library.json
│ └── tsup-config/
│ ├── index.ts
│ ├── package.json
│ └── react-import.js
├── recipes/
│ ├── next/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── [...puckPath]/
│ │ │ │ ├── client.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ ├── puck/
│ │ │ │ ├── [...puckPath]/
│ │ │ │ │ ├── client.tsx
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── api/
│ │ │ │ │ └── route.ts
│ │ │ │ └── page.tsx
│ │ │ └── styles.css
│ │ ├── database.json
│ │ ├── lib/
│ │ │ └── get-page.ts
│ │ ├── next-env.d.ts
│ │ ├── next.config.js
│ │ ├── package.json
│ │ ├── proxy.ts
│ │ ├── puck.config.tsx
│ │ ├── tsconfig/
│ │ │ ├── base.json
│ │ │ └── nextjs.json
│ │ └── tsconfig.json
│ ├── next-ai/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── [...puckPath]/
│ │ │ │ ├── client.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── api/
│ │ │ │ ├── pages/
│ │ │ │ │ └── route.ts
│ │ │ │ └── puck/
│ │ │ │ └── [...all]/
│ │ │ │ └── route.ts
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ ├── puck/
│ │ │ │ ├── [...puckPath]/
│ │ │ │ │ ├── client.tsx
│ │ │ │ │ └── page.tsx
│ │ │ │ └── page.tsx
│ │ │ └── styles.css
│ │ ├── database.json
│ │ ├── lib/
│ │ │ └── get-page.ts
│ │ ├── next-env.d.ts
│ │ ├── next.config.js
│ │ ├── package.json
│ │ ├── proxy.ts
│ │ ├── puck.config.tsx
│ │ ├── tsconfig/
│ │ │ ├── base.json
│ │ │ └── nextjs.json
│ │ └── tsconfig.json
│ ├── react-router/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── components/
│ │ │ │ └── puck-render.tsx
│ │ │ ├── lib/
│ │ │ │ ├── pages.server.ts
│ │ │ │ └── resolve-puck-path.server.ts
│ │ │ ├── root.tsx
│ │ │ ├── routes/
│ │ │ │ ├── _index.tsx
│ │ │ │ └── puck-splat.tsx
│ │ │ └── routes.ts
│ │ ├── database.json
│ │ ├── package.json
│ │ ├── puck.config.tsx
│ │ ├── react-router.config.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── react-router-ai/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── components/
│ │ │ │ └── puck-render.tsx
│ │ │ ├── lib/
│ │ │ │ ├── pages.server.ts
│ │ │ │ └── resolve-puck-path.server.ts
│ │ │ ├── root.tsx
│ │ │ ├── routes/
│ │ │ │ ├── _index.tsx
│ │ │ │ ├── api.puck.ts
│ │ │ │ └── puck-splat.tsx
│ │ │ └── routes.ts
│ │ ├── database.json
│ │ ├── package.json
│ │ ├── puck.config.tsx
│ │ ├── react-router.config.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── remix/
│ │ ├── .eslintrc.cjs
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── entry.client.tsx
│ │ │ ├── entry.server.tsx
│ │ │ ├── models/
│ │ │ │ └── page.server.ts
│ │ │ ├── puck.config.tsx
│ │ │ ├── root.tsx
│ │ │ ├── routes/
│ │ │ │ ├── $puckPath.tsx
│ │ │ │ ├── $puckPath_.edit.tsx
│ │ │ │ ├── _index.tsx
│ │ │ │ └── edit.tsx
│ │ │ └── styles/
│ │ │ └── shared.css
│ │ ├── database.json
│ │ ├── package.json
│ │ ├── remix.config.js
│ │ ├── remix.env.d.ts
│ │ └── tsconfig.json
│ └── remix-ai/
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── README.md
│ ├── app/
│ │ ├── entry.client.tsx
│ │ ├── entry.server.tsx
│ │ ├── models/
│ │ │ └── page.server.ts
│ │ ├── puck.config.tsx
│ │ ├── root.tsx
│ │ ├── routes/
│ │ │ ├── $puckPath.tsx
│ │ │ ├── $puckPath_.edit.tsx
│ │ │ ├── _index.tsx
│ │ │ ├── api.puck.$.tsx
│ │ │ └── edit.tsx
│ │ └── styles/
│ │ └── shared.css
│ ├── database.json
│ ├── package.json
│ ├── remix.config.js
│ ├── remix.env.d.ts
│ └── tsconfig.json
├── scripts/
│ ├── create-changelog.js
│ ├── e2e/
│ │ ├── smoke-framework.mjs
│ │ ├── smoke.mjs
│ │ └── utils/
│ │ ├── drag-and-drop.mjs
│ │ ├── get-box.mjs
│ │ ├── pause.mjs
│ │ └── setup.mjs
│ ├── get-unstable-version.js
│ └── publish.sh
└── turbo.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc.js
================================================
module.exports = {
root: true,
// This tells ESLint to load the config from the package `eslint-config-custom`
extends: ["custom"],
settings: {
next: {
rootDir: ["apps/*/"],
},
},
rules: {
"react-hooks/exhaustive-deps": "off",
},
// eslint-config-next causes warning on Remix's default remix.config.js
ignorePatterns: ["recipes/remix/remix.config.js"],
};
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.md
================================================
---
name: "Bug Report 🐛"
about:
Report a problem with Puck. Please provide enough information to reproduce
the problem.
title: ""
labels: ["type: bug 🐛"]
assignees: ""
---
## Description
[Example: "The `
Page does not exist in session storage
Page does not exist in session storage
Description
", buttons: [{ label: "Learn more", href: "#" }], padding: "64px", }, /** * The resolveData method allows us to modify component data after being * set by the user. * * It is called after the page data is changed, but before a component * is rendered. This allows us to make dynamic changes to the props * without storing the data in Puck. * * For example, requesting a third-party API for the latest content. */ resolveData: async ({ props }, { changed }) => { if (!props.quote) return { props, readOnly: { title: false, description: false } }; if (!changed.quote) { return { props }; } // Simulate a delay await new Promise((resolve) => setTimeout(resolve, 500)); return { props: { title: quotes[props.quote.index].author, description: `${quotes[props.quote.index].content}
`, }, readOnly: { title: true, description: true }, }; }, resolveFields: async (data, { fields }) => { if (data.props.align === "center") { return { ...fields, image: undefined, }; } return fields; }, resolvePermissions: async (data, params) => { if (!params.changed.quote) return params.lastPermissions; // Simulate delay await new Promise((resolve) => setTimeout(resolve, 500)); return { ...params.permissions, // Disable delete if quote 7 is selected delete: data.props.quote?.index !== 7, }; }, render: HeroComponent, }; ================================================ FILE: apps/demo/config/blocks/Hero/index.tsx ================================================ export * from "./client"; export { type HeroProps } from "./Hero"; ================================================ FILE: apps/demo/config/blocks/Hero/quotes.ts ================================================ export const quotes = [ { content: "Age is an issue of mind over matter. If you don't mind, it doesn't matter.", author: "Mark Twain", }, { content: "Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps learning stays young. The greatest thing in life is to keep your mind young.", author: "Henry Ford", }, { content: "Wrinkles should merely indicate where smiles have been.", author: "Mark Twain", }, { content: "True terror is to wake up one morning and discover that your high school class is running the country.", author: "Kurt Vonnegut", }, { content: "As I grow older, I pay less attention to what men say. I just watch what they do.", author: "Andrew Carnegie", }, { content: "How incessant and great are the ills with which a prolonged old age is replete.", author: "C. S. Lewis", }, { content: "Old age, believe me, is a good and pleasant thing. It is true you are gently shouldered off the stage, but then you are given such a comfortable front stall as spectator.", author: "Confucius", }, { content: "Old age has deformities enough of its own. It should never add to them the deformity of vice.", author: "Eleanor Roosevelt", }, { content: "Nobody grows old merely by living a number of years. We grow old by deserting our ideals. Years may wrinkle the skin, but to give up enthusiasm wrinkles the soul.", author: "Samuel Ullman", }, ]; ================================================ FILE: apps/demo/config/blocks/Hero/server.tsx ================================================ /* eslint-disable @next/next/no-img-element */ import { ComponentConfig } from "@/core/types"; import HeroComponent, { HeroProps } from "./Hero"; export const Hero: ComponentConfigBody
", }, }; export const RichText = withLayout(RichTextInner); ================================================ FILE: apps/demo/config/blocks/Space/index.tsx ================================================ import React from "react"; import { ComponentConfig } from "@/core"; import { spacingOptions } from "../../options"; import { getClassNameFactory } from "@/core/lib"; import styles from "./styles.module.css"; const getClassName = getClassNameFactory("Space", styles); export type SpaceProps = { direction?: "" | "vertical" | "horizontal"; size: string; }; export const Space: ComponentConfigPuck is the self-hosted visual editor for React. Bring your own components and make site changes instantly, without a deploy.
", buttons: [ { label: "Visit GitHub", href: "https://github.com/puckeditor/puck", }, { label: "Edit this page", href: "/edit", variant: "secondary" }, ], id: "Hero-1687283596554", image: { url: "https://images.unsplash.com/photo-1687204209659-3bded6aecd79?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2670&q=80", mode: "inline", content: [], }, padding: "128px", align: "left", }, readOnly: { title: false, description: false }, }, { type: "Space", props: { size: "96px", id: "Space-1687298109536", direction: "vertical", }, }, { type: "Heading", props: { align: "center", level: "2", text: "Drag-and-drop your own React components", layout: { padding: "0px" }, size: "xxl", id: "Heading-1687297593514", }, }, { type: "Space", props: { size: "8px", id: "Space-1687284122744", direction: "vertical", }, }, { type: "Text", props: { align: "center", text: "Configure Puck with your own components to make change for your marketing pages without a developer.", layout: { padding: "0px" }, size: "m", id: "Text-1687297621556", color: "muted", }, }, { type: "Space", props: { size: "40px", id: "Space-1687296179388", direction: "vertical", }, }, { type: "Grid", props: { id: "Grid-c4cd99ae-8c5e-4cdb-87d2-35a639f5163e", gap: 24, numColumns: 3, items: [ { type: "Card", props: { title: "Built for content teams", description: "Puck enables content teams to make changes to their content without a developer or breaking the UI.", icon: "pen-tool", mode: "flat", layout: { grow: true, spanCol: 1, spanRow: 1, padding: "0px" }, id: "Card-66ab42c9-d1da-4c44-9dba-5d7d72f2178d", }, }, { type: "Card", props: { title: "Easy to integrate", description: "Front-end developers can easily integrate their own components using a familiar React API.", icon: "git-merge", mode: "flat", layout: { grow: true, spanCol: 1, spanRow: 1, padding: "0px" }, id: "Card-0012a293-8ef3-4e7c-9d7c-7da0a03d97ae", }, }, { type: "Card", props: { title: "No vendor lock-in", description: "Completely open-source, Puck is designed to be integrated into your existing React application.", icon: "github", mode: "flat", layout: { grow: true, spanCol: 1, spanRow: 1, padding: "0px" }, id: "Card-09efb3f3-f58d-4e07-a481-7238d7e57ad6", }, }, ], }, }, { type: "Space", props: { size: "96px", id: "Space-1687287070296", direction: "vertical", }, }, { type: "Space", props: { size: "96px", id: "Space-1687298110602", direction: "vertical", }, }, { type: "Heading", props: { align: "center", level: "2", text: "The numbers", layout: { padding: "0px" }, size: "xxl", id: "Heading-1687296574110", }, }, { type: "Space", props: { size: "16px", id: "Space-1687284283005", direction: "vertical", }, }, { type: "Text", props: { align: "center", text: 'This page demonstrates Puck configured with a custom component library. This component is called "Stats", and contains some made-up numbers. You can configure any page by adding "/edit" onto the URL.', layout: { padding: "0px" }, size: "m", id: "Text-1687284565722", color: "muted", maxWidth: "916px", }, }, { type: "Space", props: { size: "96px", id: "Space-1687297618253", direction: "vertical", }, }, { type: "Stats", props: { items: [ { title: "Users reached", description: "20M+" }, { title: "Cost savings", description: "$1.5M" }, { title: "Another stat", description: "5M kg" }, { title: "Final fake stat", description: "15K" }, ], id: "Stats-1687297239724", }, }, { type: "Space", props: { size: "120px", id: "Space-1687297589663", direction: "vertical", }, }, { type: "Heading", props: { align: "center", level: "2", text: "Extending Puck", layout: { padding: "0px" }, size: "xxl", id: "Heading-1687296184321", }, }, { type: "Space", props: { size: "8px", id: "Space-1687296602860", direction: "vertical", }, }, { type: "Text", props: { align: "center", text: "Puck can also be extended with plugins and headless CMS content fields, transforming Puck into the perfect tool for your Content Ops.", layout: { padding: "0px" }, size: "m", id: "Text-1687296579834", color: "muted", maxWidth: "916px", }, }, { type: "Space", props: { size: "96px", id: "Space-1687299311382", direction: "vertical", }, }, { type: "Grid", props: { gap: 24, numColumns: 3, id: "Grid-2da28e88-7b7b-4152-9da0-9f93f41213b6", items: [ { type: "Card", props: { title: "plugin-heading-analyzer", description: "Analyze the document structure and identify WCAG 2.1 issues with your heading hierarchy.", icon: "align-left", mode: "card", layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" }, id: "Card-b0e8407d-9fbb-4e76-aa32-d32f655c11d3", }, }, { type: "Card", props: { title: "External data", description: "Connect your components with an existing data source, like Strapi.js.", icon: "feather", mode: "card", layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" }, id: "Card-f8ebd568-3a30-4099-a068-22cabae4691b", }, }, { type: "Card", props: { title: "Custom plugins", description: "Create your own plugin to extend Puck for your use case using React.", icon: "plug", mode: "card", layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" }, id: "Card-9c3b0acc-ee42-4a4a-8cc7-1b22d98493f1", }, }, { type: "Card", props: { title: "Title", description: "Description", icon: "Feather", mode: "card", layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" }, id: "Card-dbec4ae9-8208-49bf-8910-3347ff13d957", }, }, { type: "Card", props: { title: "Title", description: "Description", icon: "Feather", mode: "card", layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" }, id: "Card-e807464c-4974-4dbb-b1c9-989deabce58d", }, }, { type: "Card", props: { title: "Title", description: "Description", icon: "Feather", mode: "card", layout: { grow: false, spanCol: 1, spanRow: 1, padding: "0px" }, id: "Card-3b4b7d53-2124-4d7a-a67e-36b24fd765b4", }, }, ], }, }, { type: "Space", props: { size: "96px", id: "Space-1687299315421", direction: "vertical", }, }, { type: "Heading", props: { align: "center", level: "2", text: "Get started", layout: { padding: "0px" }, size: "xxl", id: "Heading-1687299303766", }, }, { type: "Space", props: { size: "16px", id: "Space-1687299318902", direction: "vertical", }, }, { type: "Text", props: { align: "center", text: "Browse the Puck GitHub to get started, or try editing this page", layout: { padding: "0px" }, size: "m", id: "Text-1687299305686", color: "muted", }, }, { type: "Space", props: { size: "24px", id: "Space-1687299335149", direction: "vertical", }, }, { type: "Flex", props: { justifyContent: "center", direction: "row", gap: 24, wrap: "wrap", layout: { spanCol: 1, spanRow: 1, padding: "0px" }, id: "Flex-7d63d5ff-bd42-4354-b05d-681b16436fd6", items: [ { type: "Button", props: { label: "Visit GitHub", href: "https://github.com/puckeditor/puck", variant: "primary", id: "Button-bd41007c-6627-414d-839a-e261d470d8f9", }, }, { type: "Button", props: { label: "Edit this page", href: "/edit", variant: "secondary", id: "Button-6a5fa26c-8a2d-4b08-a756-c46079877127", }, }, ], }, }, { type: "Space", props: { size: "96px", id: "Space-1687284290127", direction: "vertical", }, }, ], root: { props: { title: "Puck Example" } }, zones: {}, }, "/pricing": { content: [], root: { props: { title: "Pricing" } }, }, "/about": { content: [], root: { props: { title: "About Us" } }, }, }; ================================================ FILE: apps/demo/config/options.ts ================================================ export const spacingOptions = [ { label: "8px", value: "8px" }, { label: "16px", value: "16px" }, { label: "24px", value: "24px" }, { label: "32px", value: "32px" }, { label: "40px", value: "40px" }, { label: "48px", value: "48px" }, { label: "56px", value: "56px" }, { label: "64px", value: "64px" }, { label: "72px", value: "72px" }, { label: "80px", value: "80px" }, { label: "88px", value: "88px" }, { label: "96px", value: "96px" }, { label: "104px", value: "104px" }, { label: "112px", value: "112px" }, { label: "120px", value: "120px" }, { label: "128px", value: "128px" }, { label: "136px", value: "136px" }, { label: "144px", value: "144px" }, { label: "152px", value: "152px" }, { label: "160px", value: "160px" }, ]; ================================================ FILE: apps/demo/config/root.tsx ================================================ import { DefaultRootProps, RootConfig } from "@/core"; import { Header } from "./components/Header"; import { Footer } from "./components/Footer"; export type RootProps = DefaultRootProps; export const Root: RootConfig<{ props: RootProps; fields: { userField: { type: "userField"; option: boolean }; }; }> = { defaultProps: { title: "My Page", }, render: ({ puck: { isEditing, renderDropZone: DropZone } }) => { return (We provide Puck support, design system builds, and consultancy.
Puck empowers developers to build amazing visual editing experiences into their own React applications, powering the next generation of content tools, no-code builders and WYSIWYG editors.
~ npm i @puckeditor/core
--save
{title}
; }, }} /> ```tsx {1,4} copy import { Autofield } from "@puckeditor/core"; const CustomField = ({ onChange, value }) => (` | ReactNode | Required | | [`title`](#title) | `"Quote"` | string | Required | | [`onClick`](#onclick) | `() => {}` | Function | Required | | [`active`](#active) | `true` | Boolean | - | | [`disabled`](#disabled) | `true` | Boolean | - | ## Required props ### icon The icon to render for the control, as a React node. Puck uses [lucide-react](https://lucide.dev/guide/packages/lucide-react) internally. ```tsx showLineNumbers copy {2}
`. Configured by the [`blockquote`](/docs/api-reference/fields/richtext#options) option. ### \Mark the text as bold. Configured by the [`bold`](/docs/api-reference/fields/richtext#options) option. ### \ Wrap the text with an unordered list (` `). Configured by the [`bulletList`](/docs/api-reference/fields/richtext#options) and [`listItem`](/docs/api-reference/fields/richtext#options) options. ### \
Wrap the text with a code block (` ` and ``). Configured by the [`codeBlock`](/docs/api-reference/fields/richtext#options) option. ### \A select dropdown to set the text as a heading (`h1`, `h2`, `h3`, etc). Configured by the [`heading`](/docs/api-reference/fields/richtext#options) option. ### \ Insert a horizontal rule (`
`). Configured by the [`horizontalRule`](/docs/api-reference/fields/richtext#options) option. ### \Wrap the text with ` `. Configured by the [`code`](/docs/api-reference/fields/richtext#options) option. ### \Mark the text as italic. Configured by the [`italic`](/docs/api-reference/fields/richtext#options) option. ### \ A select dropdown with all list options (`ul`, `ol`). Configured by the [`bulletList`](/docs/api-reference/fields/richtext#options), [`orderedList`](/docs/api-reference/fields/richtext#options) and [`listItem`](/docs/api-reference/fields/richtext#options) options. ### \ Wrap the text with an ordered list (` `). Configured by the [`orderedList`](/docs/api-reference/fields/richtext#options) and [`listItem`](/docs/api-reference/fields/richtext#options) options. ### \
Mark the text as struck-through. Configured by the [`strike`](/docs/api-reference/fields/richtext#options) option. ### \Mark the text as underlined. Configured by the [`underline`](/docs/api-reference/fields/richtext#options) option. ================================================ FILE: apps/docs/pages/docs/api-reference/components.mdx ================================================ # Components Puck provides several components to support different integration approaches. ## Core - [\ ](components/drop-zone) - Place droppable regions (zones) inside other components to enable nested components. - [\ ](components/puck) - Render the Puck editor. - [\ ](components/render) - Render a [`Data`](/docs/api-reference/data-model/data) object for a given [`Config`](/docs/api-reference/configuration/config). ## Compositional - [\ ](components/puck-components) - A draggable list of components when composing a custom Puck UI. - [\ ](components/puck-fields) - The fields for the currently selected item when composing a custom Puck UI. - [\ ](components/puck-layout) - The standard Puck layout. - [\ ](components/puck-outline) - An interactive outline when composing a custom Puck UI. - [\ ](components/puck-preview) - A drag-and-drop preview when composing a custom Puck UI. ## Helper - [\ ](components/action-bar) - An action bar containing a series of actions, normally used with the [actionBar override](/docs/api-reference/overrides/action-bar). - [\ ](components/action-bar-action) - An action for use within the ActionBar component. - [\ ](components/action-bar-group) - A group of actions for use within the ActionBar component. - [\ ](components/action-bar-label) - A label for use within the ActionBar component. - [\ ](components/action-bar-separator) - A separator to divide elements in the ActionBar component. - [\ ](components/drawer) - A reference list of items that can be dragged into a droppable area, normally [` `](components/puck-preview). - [\ ](components/drawer-item) - An item that can be dragged from a [` `](components/drawer). - [\ ](components/field-label) - Render a styled `label` when creating [`custom` fields](/docs/api-reference/fields/custom). - [\ ](components/rich-text-menu) - A menu containing a series of controls for the rich text field. - [\ ](components/rich-text-menu-control) - A control for custom behavior within the RichTextMenu component. - [\ ](components/rich-text-menu-group) - A group of controls for use within the RichTextMenu component. ================================================ FILE: apps/docs/pages/docs/api-reference/configuration/_meta.js ================================================ const menu = { config: {}, }; export default menu; ================================================ FILE: apps/docs/pages/docs/api-reference/configuration/component-config.mdx ================================================ --- title: ComponentConfig --- import { ConfigPreview, PuckPreview } from "@/docs/components/Preview"; import { Drawer } from "@/puck"; # ComponentConfig The configuration for each component defined in [`Config`](/docs/api-reference/configuration/config). ```tsx {3-10} copy const config = { components: { HeadingBlock: { fields: { title: { type: "text", }, }, render: ({ title }) => {title}
, }, }, }; ``` ## Params | Param | Example | Type | Status | | -------------------------------------------------------- | ----------------------------------------------- | -------- | -------- | | [`render()`](#renderprops) | `render: () => ` | Function | Required | | [`fields`](#fields) | `fields: { title: { type: "text"} }` | Object | - | | [`defaultProps`](#defaultprops) | `defaultProps: { title: "Hello, world" }` | Object | - | | [`inline`](#inline) | `inline: true` | Boolean | - | | [`label`](#label) | `label: "Heading Block"` | String | - | | [`metadata`](#metadata) | `metadata: {}` | Object | - | | [`permissions()`](#permissions) | `permissions: { delete: false }` | Object | - | | [`resolveData()`](#resolvedatadata-params) | `resolveData: async ({ props }) => ({ props })` | Object | - | | [`resolveFields()`](#resolvefieldsdata-params) | `resolveFields: async ({ props }) => ({})` | Object | - | | [`resolvePermissions()`](#resolvepermissionsdata-params) | `resolvePermissions: async ({ props }) => ({})` | Object | - | ## Required params ### `render(props)` A render function to render your component. Receives props as defined in `fields`, and some internal Puck props. ```tsx {4} copy const config = { components: { HeadingBlock: { render: () =>Hello, world
, }, }, }; ``` #### Render props | Arg | Example | Type | | ----------------------------------------------- | ------------- | -------- | | [`id`](#id) | `button-1234` | String | | [`puck.dragRef`](#puckdragref) | `null` | Function | | [`puck.isEditing`](#puckisediting) | `false` | Boolean | | [`puck.metadata`](/docs/api-reference/metadata) | `{}` | Object | | [`puck.renderDropZone`](#puckrenderdropzone) | `() => {}` | Function | | [`...props`](#props) | `{}` | Object | ##### `id` A unique identifier for the component. Auto-generated by default. ##### `puck.dragRef` A `ref` that tells Puck which element is draggable. Apply this to your components when using the [`inline` parameter](#inline) for advanced CSS layouts. ```tsx {5} /renderDropZone/1 copy const config = { components: { Example: { inline: true, render: ({ puck: { dragRef } }) => { returnHello, world; }, }, }, }; ``` ##### `puck.isEditing` A boolean describing whether or not this component is being rendered in the `` component. ##### `puck.metadata` An object containing the global metadata provided to the [` `](/docs/api-reference/components/puck#metadata) or [` `](/docs/api-reference/components/render#metadata) component, merged with any metadata defined in this [component's config](#metadata). ```tsx {5} /renderDropZone/1 copy const config = { components: { Example: { inline: true, render: ({ text, puck: { metadata } }) => { return Hello, {metadata.text || text}; }, }, }, }; ``` ##### `puck.renderDropZone` A render method that creates a [``](/docs/api-reference/components/drop-zone) for creating nested components. Use this method instead of the ` ` when implementing React server components. ```tsx {5} /renderDropZone/1 copy const config = { components: { Example: { render: ({ puck: { renderDropZone } }) => { return {renderDropZone({ zone: "my-content" })}; }, }, }, }; ``` ##### `...props` The remaining props are provided by the user-defined [fields](#fields). ## Optional params ### `fields` An object describing which [`Field`](/docs/api-reference/fields) to show for each prop passed to the component. ```tsx {4-8} copy showLineNumbers const config = { components: { HeadingBlock: { fields: { title: { type: "text", }, }, render: ({ title }) =>{title}
, }, }, }; ```{ return {title}
; }, }} /> ### `defaultProps` Default props to apply to a new instance of the component. ```tsx {9} copy showLineNumbers const config = { components: { HeadingBlock: { fields: { title: { type: "text", }, }, defaultProps: { title: "Hello, world" }, render: ({ title }) =>{title}
, }, }, }; ```{ return {title}
; }, }} /> ### `inline` Render your component without a wrapping element. Use this to [create advanced CSS layouts](/docs/integrating-puck/multi-column-layouts#advanced-css-layouts). Defaults to `false`. When `true`, you must to specify which item is draggable via the [`puck.dragRef` prop](#puckdragref). ```tsx {4-5} copy showLineNumbers const config = { components: { HeadingBlock: { inline: true, render: ({ puck }) =>Hello, World
, }, }, }; ```{ return ( {title}
); }, }} /> ### `label` A label to show when referring to your component within the Puck editor. Defaults to the key of your component. ```tsx {4} copy showLineNumbers const config = { components: { HeadingBlock: { label: "Heading Block", render: () =>Hello, World
, }, }, }; ```### `metadata` An object containing any additional values. Provided to their [`render`](#renderprops) and [`resolveData`](#resolvedatadata-params) functions. Will be merged with the global `metadata` prop provided to the [` `](/docs/api-reference/components/puck#metadata) or [` `](/docs/api-reference/components/render#metadata) component, overriding any keys with the same name. ```tsx {4-7} const config = { components: { HeadingBlock: { metadata: { title: "Hello, world", }, render: ({ puck }) => {puck.metadata.title}
, }, }, }; ``` ### `permissions` Set the [permissions](/docs/api-reference/permissions) for all instances of a component to toggle functionality. Inherits [global permissions](/docs/api-reference/components/puck/#permissions). ```tsx {4-6} copy showLineNumbers const config = { components: { HeadingBlock: { permissions: { delete: false, // Disable deletion of all HeadingBlock instances }, render: () =>Hello, World
, }, }, }; ``` ### `resolveData(data, params)` Dynamically change the props and set fields as read-only. Supports asynchronous calls. This function is triggered when [``](/docs/api-reference/components/puck) renders, when a field is changed, when the [`resolveAllData` function](/docs/api-reference/functions/resolve-all-data) is called, or when the component is moved to a different parent. ```tsx {9-14} copy filename="Example mapping 'title' to 'resolvedTitle'" const config = { components: { HeadingBlock: { fields: { title: { type: "text", }, }, resolveData: async ({ props }) => { return { props: { resolvedTitle: props.title }, readOnly: { resolvedTitle: true }, }; }, render: ({ resolvedTitle }) => {resolvedTitle}
, }, }, }; ```{ return { props: { resolvedTitle: props.title }, readOnly: { resolvedTitle: true }, }; }, render: ({ resolvedTitle }) => { return {resolvedTitle}
; }, }} /> #### Args | Prop | Example | Type | | -------- | -------------------------------------------------------------------- | ------ | | `data` | `{ props: { title: "Hello, world" }, readOnly: {} }` | Object | | `params` | `{ changed: { title: true }, metadata: { foo: "bar" }, parent: {} }` | Object | ##### `data.props` The current props for the component. ```tsx copy /props/1,3 const resolveData = async ({ props }) => { return { props: { resolvedTitle: props.title }, }; }; ``` ##### `data.readOnly` The fields currently set to read-only for this component. ##### `params.changed` An object describing which props have changed on this component since the last time `resolveData` was called. ```tsx copy {2-4} /changed/1 filename="Example only updating 'resolvedTitle' when 'title' changes" const resolveData = async ({ props }, { changed }) => { if (!changed.title) { return { props }; } return { props: { resolvedTitle: props.title }, }; }; ``` ##### `params.lastData` The data object from the previous run of this function. ##### `params.metadata` An object containing the global metadata provided to the [``](/docs/api-reference/components/puck#metadata) or [` `](/docs/api-reference/components/render#metadata) component, merged with any metadata defined in this [component's config](#metadata). ```tsx copy {2-4} /changed/1 filename="Example only updating 'resolvedTitle' when 'title' changes" const resolveData = async ({ props }, { metadata }) => { return { props: { title: metadata.title || props.title }, }; }; ``` ##### `params.parent` The [component data](/docs/api-reference/data-model/component-data) of the parent. If the parent implements [resolveData](#resolvedatadata-params), this parameter will return the unresolved data on initial render, as resolvers are executed bottom-up. ##### `params.trigger` The event that triggered this resolveData call. One of: - `insert` - the component was inserted - `replace` - the component was replaced, such as when a field value is changed - `move` - the component moved between parents - `load` - the Puck editor was loaded - `force` - an execution was forced via the [`resolveAllData` function](/docs/api-reference/functions/resolve-all-data) #### Returns | Prop | Example | Type | | ------ | ---------------------------------------------------- | ------ | | `data` | `{ props: { title: "Hello, world" }, readOnly: {} }` | Object | ##### `data.props` A partial props object containing modified props. Will be spread into the other props. ```tsx copy {3} filename="Example only updating resolvedTitle when title changes" const resolveData = async ({ props }) => { return { props: { resolvedTitle: props.title }, }; }; ``` ##### `data.readOnly` A partial object describing fields to set as readonly. Will be spread into the existing readOnly state. ```tsx copy {4} filename="Example only updating resolvedTitle when title changes" const resolveData = async ({ props }) => { return { props: { resolvedTitle: props.title }, readOnly: { resolvedTitle: true }, // Make the `resolvedTitle` field read-only }; }; ``` ### `resolveFields(data, params)` Dynamically set the fields for this component. Supports asynchronous calls. This function is triggered when the component data changes. ```tsx {4-25} copy filename="Example changing one field based on another" const config = { components: { MyComponent: { resolveFields: (data) => { const fields = { drink: { type: "radio", options: [ { label: "Water", value: "water" }, { label: "Orange juice", value: "orange-juice" }, ], }, }; if (data.props.drink === "water") { return { ...fields, waterType: { // ... Define field }, }; } return fields; }, // ... }, }, }; ``` { const fields = { drink: { type: "radio", options: [ { label: "Water", value: "water" }, { label: "Orange juice", value: "orange-juice" }, ], }, }; if (data.props.drink === "water") { return { ...fields, waterType: { type: "radio", options: [ { label: "Still", value: "still" }, { label: "Sparkling", value: "sparkling" }, ], }, }; } return fields; }, defaultProps: { drink: "water", waterType: "still", }, render: ({ drink, waterType }) => ( {drink} {drink === "water" ? ` (${waterType})` : ""}
), }} /> #### Args | Prop | Example | Type | | -------- | ------------------------------------------------------------------------------------- | ------ | | `data` | `{ props: { title: "Hello, world" }, readOnly: {} }` | Object | | `params` | `{ appState: {}, changed: {}, fields: {}, lastData: {}, lastFields: {}, parent: {} }` | Object | ##### `data.props` The current props for the selected component. ##### `data.readOnly` The fields currently set to read-only for this component. ##### `params.appState` An object describing the [AppState](/docs/api-reference/data-model/app-state). ##### `params.changed` An object describing which props have changed on this component since the last time this function was called. ```tsx copy {2-4} /changed/1 filename="Example only updating the fields when 'fieldType' changes" const resolveFields = async ({ props }, { changed, lastFields }) => { if (!changed.fieldType) { return lastFields; } return { title: { type: fieldType, }, }; }; ``` ##### `params.fields` The static fields for this component as defined by [`fields`](#fields). ##### `params.lastData` The data object from the previous run of this function. ##### `params.lastFields` The last fields object created by the previous run of this function. ##### `params.metadata` An object containing the global metadata provided to the [``](/docs/api-reference/components/puck#metadata) or [` `](/docs/api-reference/components/render#metadata) component, merged with any metadata defined in this [component's config](#metadata). ##### `params.parent` The parent data object if this item is within a [DropZone](/docs/api-reference/components/drop-zone). ### `resolvePermissions(data, params)` Dynamically set the [permissions](/docs/api-reference/permissions) for this component to toggle functionality. Can be used to control the permissions on a specific component instance. Inherits [`permissions`](#permissions). Supports asynchronous calls. This function is triggered when [` `](/docs/api-reference/components/puck) renders, when the component is inserted, when its data changes, or when it's moved to a different parent. ```tsx {4-12} copy filename="Setting permissions for a specific instance" showLineNumbers const config = { components: { MyComponent: { resolvePermissions: (data, { permissions }) => { if (data.props.id === "MyComponent-1234") { return { delete: false, // Disable deletion on component with id MyComponent-1234 }; } return { permissions }; // Fallback to inherited permissions }, // ... }, }, }; ``` #### Args | Prop | Example | Type | | -------- | ----------------------------------------------------------------------------------------------- | ------ | | `data` | `{ props: { title: "Hello, world" }, readOnly: {} }` | Object | | `params` | `{ appState: {}, changed: {}, permissions: {}, lastData: {}, lastPermissions: {}, parent: {} }` | Object | ##### `data.props` The current props for the selected component. ##### `data.readOnly` The fields currently set to read-only for this component. ##### `params.appState` An object describing the [AppState](/docs/api-reference/data-model/app-state). ##### `params.changed` An object describing which props have changed on this component since the last time this function was called. This helps prevent duplicate calls when making async operations. ```tsx copy {2-4} /changed/1 filename="Example only updating the permissions when 'example' prop changes" const resolvePermissions = async ({ props }, { changed, lastPermissions }) => { if (!changed.example) { return lastPermissions; // Return the last permissions unless the `example` prop has changed } return await expensiveAsyncOperation(); }; ``` ##### `params.permissions` The static fields for this component as defined by [`permissions`](#permissions). ##### `params.lastData` The data object from the previous run of this function. ##### `params.lastPermissions` The last permissions object created by the previous run of this function. ##### `params.parent` The [component data](/docs/api-reference/data-model/component-data) of the parent. If the parent implements [resolveData](#resolvedatadata-params), this parameter will return the unresolved data on initial render. #### Returns A [`fields`](#fields) object. ================================================ FILE: apps/docs/pages/docs/api-reference/configuration/config.mdx ================================================ # Config The main configuration object describing what Puck can render. ```tsx copy const config = { components: { HeadingBlock: { fields: { children: { type: "text", }, }, render: ({ children }) => { return {children}
; }, }, }, }; ``` ## Params | Param | Example | Type | Status | | --------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------- | -------- | | [`components`](#components) | `components: { HeadingBlock: {{ render: () => } }` | Object | Required | | [`root`](#root) | `root: { render: () => }` | [`ComponentConfig`](/docs/api-reference/configuration/component-config) | - | | [`categories`](#categories) | `categories: { typography: { components: ["HeadingBlock"] } }` | Object | - | ## Required params ### `components` An object describing the components available to Puck. Each component definition implements the [`ComponentConfig` API](/docs/api-reference/configuration/component-config). ```tsx {2-13} copy showLineNumbers const config = { components: { HeadingBlock: { fields: { children: { type: "text", }, }, render: ({ children }) => { return{children}
; }, }, }, }; ``` ## Optional params ### `root` An object describing the root of your component tree. This component wraps the rest of your components. Implements the [`ComponentConfig` API](/docs/api-reference/configuration/component-config). - You must return children to render the default content. ```tsx {2-6} copy showLineNumbers const config = { root: { render: ({ children }) => { return{children}; }, }, // ... }; ``` ### `categories` An object describing categories for your components. Will be used to group the components in the left side-bar. ```tsx {2-6} copy showLineNumbers const config = { categories: { typography: { components: ["HeadingBlock"], }, }, // ... }; ``` #### `categories[key].components` An array of components in this category. - Must use names of [`components`](#components). - A component can appear in more than one category. #### `categories[key].title` The user-facing title for the category. Will use the `category` key if not provided. #### `categories[key].visible` A boolean describing whether or not the category should be visible in the side bar. **Defaults to `true`**. #### `categories[key].defaultExpanded` A boolean describing whether or not the category should be expanded by default in the side bar. **Defaults to `true`**. #### `categories["other"]` The default category for all uncategorized components. Receives all other categories params. ================================================ FILE: apps/docs/pages/docs/api-reference/configuration.mdx ================================================ # Configuration - [Config](configuration/config) - The main configuration object describing what Puck can render. - [ComponentConfig](configuration/component-config) - The configuration of each component defined in the main config. ================================================ FILE: apps/docs/pages/docs/api-reference/data-model/app-state.mdx ================================================ --- title: AppState --- import { Callout } from "nextra/components"; # `AppState`The application state is unstable and is likely to experience breaking changes. The internal state of the [``](/docs/api-reference/components/puck) component. ## `data` The current [`Data`](/docs/api-reference/data-model/data) payload being managed by Puck. ## `ui` The current state of the Puck editor interface. | Param | Example | Type | | ----------------------------------------------- | ----------------------------------------------------- | ----------------------------- | | [`arrayState`](#uiarraystate) | `{}` | Object | | [`componentList`](#uicomponentlist) | `{ typography: { components: [ "HeadingBlock" ] } }` | Object | | [`field.focus`](#fieldfocus) | `"title"` | String | | [`isDragging`](#isdragging) | `false` | Boolean | | [`itemSelector`](#uiitemselector) | `{ index: 0, zone: "my-content" }` | [ItemSelector](item-selector) | | [`leftSideBarVisible`](#uileftsidebarvisible) | `false` | Boolean | | [`leftSideBarWidth`](#uileftsidebarwidth) | `200` | Number | | [`mobilePanelExpanded`](#uimobilepanelexpanded) | `true` | Boolean | | [`plugin`](#uiplugin) | `{ current: "blocks" }` | Object | | [`previewMode`](#uipreviewmode) | `"edit"` | String | | [`rightSideBarVisible`](#uirightsidebarvisible) | `false` | Boolean | | [`rightSideBarWidth`](#uirightsidebarwidth) | `200` | Number | | [`viewports`](#uiviewports) | `{ controlsVisible: true, current: {}, options: [] }` | Object | --- ### `ui.arrayState` An object describing the internal state of array items --- ### `ui.componentList` An object describing the component drawer. Similar shape to the [categories API](/docs/api-reference/configuration/config#categories) #### `ui.componentList[key].components` Array containing the names of components in this category. #### `ui.componentList[key].title` Title of the category. #### `ui.componentList[key].visible` Whether or not the category is visible in the side bar. #### `ui.componentList[key].expanded` Whether or not the category is expanded in the side bar. --- ### `ui.field.focus` The name of the currently focused field. ### `ui.field.metadata` Metadata for the the currently focused field. --- ### `ui.isDragging` A boolean stating whether or not the user is currently dragging a component. --- ### `ui.itemSelector` An [`ItemSelector`](item-selector) for the currently selected item. --- ### `ui.leftSideBarVisible` Whether or not the left side bar is visible. --- ### `ui.leftSideBarWidth` Current width of the left side bar in pixels. --- ### `ui.mobilePanelExpanded` Indicates whether the mobile plugin panel is expanded when the [`mobilePanelHeight` parameter](/docs/api-reference/plugin#mobilepanelheight) is set to `"toggle"`. Users toggle this value via the maximize/minimize button that appears on smaller viewports. --- ### `ui.plugin` | Param | Example | Type | | ----------------------------- | ---------- | ------ | | [`current`](#uiplugincurrent) | `"blocks"` | String | #### `ui.plugin.current` The currently selected plugin in the plugin rail. Matches the plugin [`name` parameter](/docs/api-reference/plugin#name). --- ### `ui.previewMode` The mode for the preview area, controlling whether or not the user can interact with the underlying component. Accepts the following values: - **`"edit"` (default)**: Components can be dragged and modified. An overlay prevents interaction with the underlying component. - **`"interactive"`**: Editing functionality is disabled. The user can interact with the underlying component. Puck does not currently provide the UI to control this value, but it can be toggled via the `cmd+i` or `ctrl+i` hotkeys. --- ### `ui.rightSideBarVisible` Whether or not the right side bar is visible. --- ### `ui.rightSideBarWidth` Current width of the right side bar in pixels. --- ### `ui.viewports` | Param | Example | Type | | ------------------------------------------------ | --------------------------------- | -------------------------------------------------------------------- | | [`controlsVisible`](#uiviewportscontrolsvisible) | `false` | Boolean | | [`current`](#uiviewportscurrent) | `{ width: 1440, height: "auto" }` | Object | | [`options`](#uiviewportsoptions) | `[]` | [Viewports\[\]](/docs/api-reference/components/puck#viewport-params) | #### `ui.viewports.controlsVisible` Whether or not the viewport controls are visible. #### `ui.viewports.current` The currently selected viewport. | Param | Example | Type | | ------------------------------------- | -------- | ------------------ | | [`width`](#uiviewportscurrentwidth) | `1440` | Number | | [`height`](#uiviewportscurrentheight) | `"auto"` | Number \| `"auto"` | ##### `ui.viewports.current.width` The width of the current viewport. ##### `ui.viewports.current.height` The height of the current viewport. #### `ui.viewports.options` The available viewport options, as provided via the [`viewports` API](/docs/api-reference/components/puck#viewports). ================================================ FILE: apps/docs/pages/docs/api-reference/data-model/component-data.mdx ================================================ --- title: ComponentData --- # `ComponentData` An object representing an instance of a component. ```json { "type": "HeadingBlock", "props": { "id": "HeadingBlock-1234", "title": "Hello, world" } } ``` ## Params | Param | Example | Type | Status | | ----------------------- | ----------------------------------------------- | ------ | -------- | | [`type`](#type) | `type: "HeadingBlock"` | String | Required | | [`props`](#props) | `props: { id: "12345", title: "Hello, world" }` | Object | Required | | [`readOnly`](#readonly) | `readOnly: { title: true }` | Object | - | ### Required params #### `type` The type of the component, which tells Puck to run the [`render()`](/docs/api-reference/configuration/component-config#renderprops) method for the component of the [same key](/docs/api-reference/configuration/config#components). #### `props` The props stored based on the [`component config`](/docs/api-reference/configuration/component-config) that Puck will pass to the [`render()`](/docs/api-reference/configuration/component-config#renderprops) method for the component. ```json {3-6} copy { "type": "HeadingBlock", "props": { "id": "HeadingBlock-1234", // Auto-generated "title": "Hello, world" } } ``` Requires a unique `id` prop to be defined. ### Optional params #### `readOnly` An object describing which fields are set to [read-only](/docs/api-reference/configuration/component-config#datareadonly-1). ```json {7-9} copy { "type": "HeadingBlock", "props": { "id": "HeadingBlock-1234", "title": "Hello, world" }, "readOnly": { "title": true } } ``` ================================================ FILE: apps/docs/pages/docs/api-reference/data-model/data.mdx ================================================ import { Callout } from "nextra/components"; # `Data` An object produced by Puck describing the shape of content. ```json copy { "content": [ { "type": "HeadingBlock", "props": { "id": "HeadingBlock-1234", "title": "Hello, world" } } ], "root": { "props": { "title": "Puck Example" } }, "zones": {} } ``` ## Params | Param | Example | Type | Status | | --------------------- | ---------------------------------------- | ---------------------------------------------------------------- | -------- | | [`content`](#content) | `content: []` | [ComponentData](/docs/api-reference/data-model/component-data)[] | Required | | [`root`](#root) | `root: { props: { title: "My page" } }` | [RootData](/docs/api-reference/root-data) | Required | | [`zones`](#zones) | `zones: { "HeadingBlock-123:zone": [] }` | Object | - | ### `content` An array of [ComponentData](/docs/api-reference/data-model/component-data) objects representing the component instances in the default content region. ```json {2-9} copy { "content": [ { "type": "HeadingBlock", "props": { "id": "HeadingBlock-1234", "title": "Hello, world" } } ], "root": {}, "zones": {} } ``` ### `root` An object describing data for the [`root` config](/docs/api-reference/configuration/config#root). An instance of [`RootData`](/docs/api-reference/root-data). ```json {3-7} copy { "content": [], "root": { "props": { "title": "My page" } }, "zones": {} } ``` ### `zones` This parameter will soon be deprecated, as DropZones have been replaced by [`slot` fields](/docs/api-reference/fields/slot). For migration notes, see [these docs](/docs/guides/migrations/dropzones-to-slots). An object describing nested content regions for each [DropZone](/docs/api-reference/components/drop-zone). #### `zones[zoneKey]` An array of [ComponentData](/docs/api-reference/data-model/component-data) objects representing the components instances in a particular DropZone. `zoneKey` is a compound of the component `id` and [DropZone `zone`](/docs/api-reference/components/drop-zone#zone). ```json {5-13} copy showLineNumbers { "content": [], "root": {}, "zones": { "HeadingBlock-1234:my-content": [ { "type": "HeadingBlock", "props": { "id": "HeadingBlock-1234", "title": "Hello, world" } } ] } } ``` ================================================ FILE: apps/docs/pages/docs/api-reference/data-model/item-selector.mdx ================================================ --- title: ItemSelector --- import { Callout } from "nextra/components"; # `ItemSelector` An object describing the location of an item in the Puck [data](data). ```json copy { "index": 0, "zone": "Flex-123:children" // The "children" slot field in the component with id "Flex-123" } ``` ## Params | Param | Example | Type | Status | | ----------------- | ----------------------------- | ------ | -------- | | [`index`](#index) | `index: 0` | Number | Required | | [`zone`](#zone) | `zone: "Flex-123:children" }` | String | - | ### `index` The index of the item. ### `zone` An optional string representing the location of the item. It is a concatenation of two values, joined with a colon (`:`): 1. The id of the parent component 2. The name of the [slot field](/docs/api-reference/fields/slot) or [DropZone `zone`](/docs/api-reference/components/drop-zone#zone) value The root of the payload is represented by the `root` id. The default content is represented by `default-zone`. When not provided, `zone` will contain the value `root:default-zone`, injecting data into [`content`](/docs/api-reference/data-model/data#content)DropZones have been replaced by [`slot` fields](/docs/api-reference/fields/slot). For migration notes, see [these docs](/docs/guides/migrations/dropzones-to-slots). ================================================ FILE: apps/docs/pages/docs/api-reference/data-model/root-data.mdx ================================================ --- title: RootData --- # `RootData` An object representing the root data. Similar to [`ComponentData`](/docs/api-reference/data-model/component-data). ```json { "props": { "title": "Hello, world" } } ``` ## Params | Param | Example | Type | Status | | ------------------------------ | ---------------------------------- | ------ | -------- | | [`props`](#contentprops) | `props: { title: "Hello, world" }` | Object | Required | | [`readOnly`](#contentreadonly) | `readOnly: { title: true }` | Object | - | ### Required params #### `type` The type of the component, which tells Puck to run the [`render()`](/docs/api-reference/configuration/component-config#renderprops) method for the component of the [same key](/docs/api-reference/config#components). #### `props` The props stored based on the [`component config`](/docs/api-reference/configuration/component-config) that Puck will pass to the [`render()`](/docs/api-reference/configuration/component-config#renderprops) method for the component of the [same key](/docs/api-reference/config#components). ```json {3-6} copy { "type": "HeadingBlock", "props": { "id": "HeadingBlock-1234", "title": "Hello, world" } } ``` Requires `id` unless used for [`root`](/docs/api-reference/data-model/data#root). ### Optional params #### `readOnly` An object describing which fields are set to [read-only](/docs/api-reference/configuration/component-config#datareadonly-1). ```json {7-9} copy { "type": "HeadingBlock", "props": { "id": "HeadingBlock-1234", "title": "Hello, world" }, "readOnly": { "title": true } } ``` ================================================ FILE: apps/docs/pages/docs/api-reference/data-model.mdx ================================================ # Data Model The Puck data model - [AppState](data-model/app-state) - Puck's internal state. - [ComponentData](data-model/component-data) - The data model for each component instance. - [Data](data-model/data) - The data model produced by Puck for a page. - [RootData](data-model/root-data) - The data model for the root data. ================================================ FILE: apps/docs/pages/docs/api-reference/field-transforms.mdx ================================================ --- title: FieldTransforms --- # FieldTransforms Transform the data for each [field type](/docs/api-reference/fields) before rendering in the editor. ```tsx copy const fieldTransforms = { text: ({ value }) =>{value}
, // ... }; ``` You can specify a custom render method for each known [field type](/docs/api-reference/fields), or introduce completely new ones. ## Render Props | Prop | Example | Type | | ----------------------------- | -------------------- | ----------------------------------- | | [`componentId`](#componentid) | `"Heading-12345"` | string | | [`field`](#field) | `{ type: "text" }` | [Field](/docs/api-reference/fields) | | [`isReadOnly`](#isreadonly) | `false` | boolean | | [`propName`](#propname) | `"title"` | string | | [`propPath`](#proppath) | `"obj.arr[2].title"` | string | | [`value`](#value) | `"Value"` | any | ### `componentId` The id of the component containing this prop ### `field` The component's field definition for this prop. ### `isReadOnly` Whether or not this field is currently set to read-only. ### `propName` The name of this prop provided to the [component field](/docs/api-reference/configuration/component-config#fields) config. ### `propPath` The path of this prop within the props object. Use in conjunction with the [`setDeep`](/docs/api-reference/functions/set-deep) utility to set data for a key deep within an object or array, normally [ComponentData](/docs/api-reference/data-model/component-data). ### `value` The value of the prop. ================================================ FILE: apps/docs/pages/docs/api-reference/fields/_meta.js ================================================ const menu = { base: { title: "Base", }, }; export default menu; ================================================ FILE: apps/docs/pages/docs/api-reference/fields/array.mdx ================================================ import { ConfigPreview } from "@/docs/components/Preview"; import { Callout } from "nextra/components"; # Array Render a list of items with a subset of fields. Extends [Base](base).{ return ( {items.map((item, i) => (
); }, }} /> ```tsx {5-10} copy const config = { components: { Example: { fields: { items: { type: "array", arrayFields: { title: { type: "text" }, }, }, }, render: ({ items }) => { return (- {item.title}
))}{items.map((item, i) => (
); }, }, }, }; ``` ## Params | Param | Example | Type | Status | | ----------------------------------------------- | --------------------------------------------- | ------------------ | -------- | | [`type`](#type) | `type: "array"` | "array" | Required | | [`arrayFields`](#arrayfields) | `arrayFields: { title: { type: "text" } }` | Object | Required | | [`defaultItemProps`](#defaultitemprops) | `defaultItemProps: { title: "Hello, world" }` | Object \| Function | - | | [`getItemSummary()`](#getitemsummaryitem-index) | `getItemSummary: (item) => item.title` | Function | - | | [`max`](#max) | `max: 3` | Number | - | | [`min`](#min) | `min: 1` | Number | - | ## Required params ### `type` The type of the field. Must be `"array"` for Array fields. ```tsx {6} copy const config = { components: { Example: { fields: { items: { type: "array", arrayFields: { title: { type: "text" }, }, }, }, // ... }, }, }; ``` ### `arrayFields` Describe the fields for each item in the array. Shares an API with `fields`. Can include any field type, including nested array fields. ```tsx {7-9} copy const config = { components: { Example: { fields: { items: { type: "array", arrayFields: { title: { type: "text" }, }, }, }, // ... }, }, }; ``` ## Optional params ### `defaultItemProps` Set the default values when a new item is added to the array. Can be an object or a function that receives the current array index. ```tsx {10-12} copy const config = { components: { Example: { fields: { items: { type: "array", arrayFields: { title: { type: "text" }, }, defaultItemProps: { title: "Hello, world", }, }, }, // ... }, }, }; ```- {item.title}
))}{ return ( {items.map((item, i) => (
); }, }} /> You can also use a function to generate dynamic defaults: ```tsx {10-14} copy const config = { components: { Example: { fields: { items: { type: "array", arrayFields: { title: { type: "text" }, order: { type: "number" }, }, defaultItemProps: (index) => ({ title: `Item ${index + 1}`, order: index + 1, }), }, }, // ... }, }, }; ```- {item.title}
))}({ title: `Item ${index + 1}`, order: index + 1, }), }, }, defaultProps: { items: [] }, render: ({ items }) => { return ( {items.map((item, i) => (
); }, }} /> ### `getItemSummary(item, index)` Get a label of each item in the array. Returns a [ReactNode](https://react.dev/learn/typescript#typing-children).- {item.title} (Order: {item.order})
))}Avoid using interactive HTML elements like `