Field types

When you give a partial a field, you give it a type:

code
wonderpress partial create --name Hero \  --prop title:string:required --prop photo:image

The type decides two things: which control an editor gets (a text input, a media picker, repeater rows), and what PHP value lands in your view. This page is the answer to the question you hit while writing a view template: what exactly is in $photo?

The short version: for every type there is one defined value shape, and it is the same no matter where the data came from. Whether an editor filled in an ACF field or set a block’s sidebar controls, your view reads the same plain variable with the same shape, and never has to ask which editor produced it. The available types are string, boolean, email, select, image, link, post_object, repeater, and partial. (Earlier versions also had array and object; those are removed, and explicit primitives, repeaters, or partial embeds cover what they did.)

How values reach the view

Two paths in, one shape out:

PathMechanics
ACFwonder_partial_props() → { acf: get_field( … ) } → ingestion copies keys onto flat properties
Blockwonder_normalize_property_value() reduces structured attributes to the same shapes, then new Partial( $attributes )

Views read flat properties ($title, $photo) and never branch on the source. For a dual partial (ACF-compatible plus a block wrapper), this is a guarantee the CLI enforces at create and sync time: every type on a dual partial must be authorable in both editors with the same value reaching the view, including transitively through partial embeds. All nine types qualify today, so in practice you only notice the rule when it catches a mistake.

Scalars

TypePHP property valueBlock attributeNotes
stringstringstringOptional format / rows on the property root shape the editor control.
booleanboolboolean
emailstringstring
selectstring (choice key)stringStatic choices also emit an enum in block.json.

image

ACF maps to an image field with return_format: array. The value on both paths is an associative array aligned with ACF’s image array:

KeyType
IDintrequired
urlstringrequired
altstringrecommended
width, heightint/stringrecommended
sizesobjectrecommended when using the core Image partial’s sizes

Block storage is one object attribute holding that array; the editor control is MediaUpload.

A fixed four-field group. This is type: "link" on a property, distinct from the rich Link primitive (below). The value shape:

code
{  "content": "Read more",  "url": "https://example.com",  "open_in_new_tab": true,  "title": "Read more about us"}

Block storage is a single object attribute matching the ACF group.

post_object

Declare post_type on the property root (for example ["page"]); it filters both the ACF field and the block editor’s REST search combobox. The value on the flat property is an int post ID or null. When ACF returns a WP_Post or an array with ID, normalization reduces it to the int. Multi-select is out of scope for now.

repeater

Rows are declared as sub-properties (--sub parent:name:type[:required], or nested properties in JSON). The value is a JSON array of row objects, each keyed by sub-property name, each value following its own type’s spec recursively. Empty is [].

Allowed sub-types: boolean, email, image, link, partial, select, string. Repeaters are one level deep: no repeater inside a repeater. ACF PRO is required, which is why wonderpress acf install defaults to PRO.

partial (embed)

Embeds another partial’s fields by reference:

code
{ "name": "cta", "type": "partial", "partial": "link", "label": "Call to action" }

The value is an object keyed by the referenced manifest’s property names, recursive. Render in the theme with wonder_render_partial_ref( $slug, $value ), which passes the value as { acf: $value } to core primitives, or read the nested object directly.

Transitive rule: when a dual partial declares a partial embed, every property on the referenced manifest must itself be dual-authorable, all the way down. The CLI walks referenced manifests (theme index first, then core bundles) and rejects unknown slugs and embed cycles.

Global conventions

  • JSON-safe block storage: IDs and plain arrays or objects only. No WP_Post, no PHP resources.
  • Shared property keys (post_type, format, rows) sit on the property root and drive both ACF and the block editor. Do not nest them under acf.
  • The manifest acf key is a passthrough for ACF-only UI overrides (ui, return_format, wrapper, …). The block-side storage format stays fixed per type.
  • when conditionals are authoring-only: they hide editor fields, and saved values are retained, matching ACF behavior. In repeater rows, when evaluates against the row.
  • Required validation happens in Abstract_Partial::render(), on both paths.

Core’s full Link primitive (partial: "link": type switch, internal, file, email, telephone targets, query params) is a separate profile. Composition and embeds use it today via wonder_render_partial_ref(). Full dual block authoring for that profile is specified later, if a block ever needs the whole primitive.