#1817·puck

Support partial slot overrides for array field UI

Author: DamianKocjanCreated Aug 27, 2026Updated Aug 28, 2026
Labelstype: featurein triage

Description

Currently, customizing the layout or specific UI elements of an array field requires replacing the entire field renderer. To change just one piece of the interface (such as the "Add item" button, the drag handle, the item actions/delete menu, or the item summary header), developers have to re-implement the internal state management, reordering, and item lifecycle logic from scratch.

Providing slot-based component overrides for array fields would allow developers to customize individual sub-components without taking on the maintenance burden of the underlying field logic.

Considerations

  • As discussed with maintainers, full headless hooks/APIs across all fields represent a larger architectural refactor. Scoped slot/component overrides provide a practical, incremental solution that solves the immediate need without requiring a complete rewrite of internal field state.
  • Overrides should receive standard context/props (e.g., index, totalItems, onAdd, onRemove, onDuplicate, isDragging) so custom components can wire up default actions easily.
  • The API pattern introduced here should align with Puck's existing override conventions and be easily extensible to other complex field types in the future.

Proposals

Proposal 1: Sub-component overrides on the array field config

Extend the array field definition object with an overrides or render map, allowing developers to target specific slots.

typescript
export const config = {
  components: {
    Hero: {
      fields: {
        items: {
          type: "array",
          arrayFields: {
            title: { type: "text" },
          },
          overrides: {
            addButton: ({ onAdd }) => (
              <button onClick={onAdd} className="custom-add-btn">
                + Add Slide
              </button>
            ),
            itemActions: ({ index, onRemove, onDuplicate }) => (
              <div className="custom-actions">
                <button onClick={onDuplicate}>Copy</button>
                <button onClick={onRemove}>Delete</button>
              </div>
            ),
            itemSummary: ({ item, index, isOpen }) => (
              <span>{item.title || `Slide ${index + 1}`}</span>
            ),
            dragHandle: ({ dragHandleProps }) => (
              <span {...dragHandleProps} className="custom-handle">
                ...
              </span>
            ),
          },
        },
      },
      render: ({ items }) => <div>{/* ... */}</div>,
    },
  },
};
  • Pros: Non-breaking, fits naturally into the existing field configuration shape, and solves most of UI customization needs with minimal boilerplate.
  • Cons: Each new slot needs to be explicitly defined and plumbed through internal field components.

Proposal 2: Root-level Puck overrides extension

Add array-specific slot overrides under Puck's top-level overrides prop to apply styling/behavior globally across all array fields.

typescript
export function Editor() {
  return (
    <Puck
      overrides={{
        arrayField: {
          addButton: ({ onAdd }) => (
            <CustomGlobalAddButton onClick={onAdd} />
          ),
          itemActions: ({ onRemove }) => (
            <CustomGlobalDelete onClick={onRemove} />
          ),
        },
      }}
    />
  );
}
  • Pros: Useful for establishing a consistent design system across all array fields in an application.
  • Cons: Less flexible for field-specific customization unless combined with Proposal 1.