Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/camaleaun/gutenbergcontenttypesmetafields/archive/refs/heads/main.zipProof-of-concept extension plugin for gutenbergcontenttypes.
Adds a Meta Fields section to the post type edit form where you can configure a sortable, duplicatable, removable list of custom meta fields — including conditional logic — that then appear as a Settings meta box on the post edit screen.
Requirements
- WordPress ≥ 6.5
- PHP ≥ 8.1
gutenbergcontenttypesplugin active (Requires Pluginsheader enforces this)- Node 20+ / npm 10+ (build only)
Build
cd wp-content/plugins/gutenbergcontenttypesmetafields
npm install
npm run build # production
npm run dev # watch mode
Output goes to build/. The PHP plugin enqueues build/index.tsx.js
(and build/style-index.tsx.css) automatically via admin_enqueue_scripts.
Dependencies bundled: @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/modifiers, @dnd-kit/utilities
Externalized (loaded from WordPress): all @wordpress/* packages, React, React DOM
Architecture
PHP side (lib/)
| File | Purpose |
|---|---|
sections.php |
Registers the "Meta Fields" section (priority 40) via gct_post_type_form_sections |
sanitize.php |
gct_sanitize_post_type_meta filter — validates mf_fields array + typeConfig per field type |
js.php |
gct_export_post_type injects meta_fields into exports; gct_export_schema extends the JSON Schema |
meta-box.php |
Registers a "Settings" meta box for every post type with mf_fields; renders + saves field values |
Meta values are stored in _gct_meta (JSON, managed by the core plugin's extension API)
as mf_fields: MetaField[].
Each field's runtime value is stored individually as _mf_{field_name} post meta.
JS side (src/)
| Path | Purpose |
|---|---|
index.tsx |
Entry point — calls gctRegisterSectionRenderer('meta_fields', renderer) |
types.ts |
TypeScript types: MetaField, SelectConfig, NumberConfig, TextConfig, MediaConfig, ConditionalLogic, … |
components/meta-field-repeater/ |
Sortable repeater (DnD Kit) with collapse/expand, duplicate, remove |
components/meta-field-repeater/field-item.tsx |
Single field item: Label→Name auto-slug, all field props, FieldTypeConfigPanel |
components/field-type-config/ |
Per-type config panels: options repeater (select/radio/checkbox), number range, text config, media types |
components/conditional-modal/ |
Full conditional logic modal: enable toggle, AND/OR relation, multiple rules (field + operator + value) |
How the renderer connects
gutenbergcontenttypesinjectswindow.gctRegisterSectionRenderervia an inline script ongct-flags(runs before the ES module)index.tsxcallsgctRegisterSectionRenderer('meta_fields', renderer)ExtensionSectionPanel(core) detects the renderer on every render and callsrenderer(container, { meta, onChange })- The renderer uses a
WeakMap<HTMLElement, Root>socreateRootis called only once per container — subsequent calls useroot.render()to update props
MetaField type
interface MetaField {
id : string; // crypto.randomUUID()
label : string; // Display label
name : string; // DB key → stored as _mf_{name}
objectType : 'field' | 'tab' | 'accordion' | 'endpoint';
fieldType : FieldType; // 19 types: text|date|time|datetime|textarea|wysiwyg|
// switcher|checkbox|iconpicker|media|gallery|
// radio|repeater|select|number|colorpicker|
// posts|html|map
description : string;
fieldWidth : '100%' | '75%' | '66.6%' | '50%' | '33.3%' | '25%';
revisionSupport: boolean;
conditional : ConditionalLogic;
typeConfig ?: SelectConfig | NumberConfig | TextConfig | MediaConfig;
}
typeConfig by fieldType:
select/radio/checkbox→SelectConfig { options: SelectOption[], multiple?: boolean }number→NumberConfig { min?, max?, step? }text/textarea→TextConfig { maxLength?, placeholder? }media/gallery→MediaConfig { allowedTypes?: string[] }
Meta Box rendering
meta-box.php reads mf_fields from _gct_meta for each active post type
and registers a "Settings" meta box (normal/high priority).
Fields are grouped into rows by fieldWidth:
100%→ own row50% + 50%,33.3% + 33.3% + 33.3%→ shared row (fills to ~100%)
Field renderers: switcher (CSS toggle), select, radio, checkbox, textarea, number, date/time/datetime, colorpicker, media (wp.media picker), text.
Values are saved as _mf_{name} post meta on save_post.
Export integration
js.php hooks into the GCT export pipeline:
gct_export_post_type→ appendsmeta_fieldskey to each exported post typegct_export_schema→ addsmeta_fieldsproperty todefinitions.post_typein the JSON Schema served atGET /gct/v1/schema; removesadditionalProperties: false
The $schema URL embedded in YAML/JSON exports points to this filtered endpoint,
so yaml-language-server validates meta_fields without warnings.
Known limitations / next steps
- Conditional logic is stored and UI is complete, but runtime enforcement (show/hide fields in the meta box based on conditions) is not yet implemented
tab/accordion/endpointobject types are registered but not rendered in the meta box (onlyfieldtype is rendered)- Import:
meta_fieldsfrom YAML/JSON is not yet applied back to_gct_metaduring import (only the post type config is imported by the core plugin) repeaterfield type has no nested config UI yetposts/iconpickerfield types have no specialized UI yet