v0.1.0 WordPress plugin for headless projects

Structured data for your headless WordPress.

Define custom data structures and manage rows of data exposed through the REST API. A small, standalone alternative to ACF options pages and repeaters.

WordPress 6.2+ Tested up to 7.1 PHP 7.4+ GPLv2 or later

Features

Everything you need to model content outside of posts and serve it to a front end.

Custom structures

Define named sets of fields with a visual builder. Keys are generated from labels, e.g. "Hero title" becomes hero_title.

Rows editor

Add rows one at a time, reorder them up or down and delete them. Each structure is simply a list of rows.

Public REST API

Read-only endpoints under ldcdt/v1 return clean JSON ready for any headless front end.

Own database tables

Data lives in dedicated tables instead of post meta. A small, standalone alternative to ACF options pages and repeaters.

17 field types

From text and WYSIWYG to images, links, dates, colors, post objects, relationships and repeaters.

Safe type changes

Changing a field type or its settings keeps saved data where possible, e.g. a select value becomes a checkbox list.

Installation

Requires WordPress 6.2+ and PHP 7.4+.

  1. 1

    Copy the lukdev-custom-data-tables directory to wp-content/plugins/ (web/app/plugins/ on Bedrock).

  2. 2

    Activate LUKDEV Custom Data Tables in Plugins. Two database tables are created on activation.

Table Columns
{prefix}ldcdt_structures id, name, slug (unique), fields (JSON field definitions), created_at
{prefix}ldcdt_rows id, structure_id, data (JSON values keyed by field key), sort_order

Usage

Go to LUKDEV Custom Data Tables in the admin menu (requires manage_options).

  1. 1

    Create a structure

    Give it a name and optionally a slug, then add fields. Keys are generated from labels and must be unique within a structure.

  2. 2

    Add rows

    After saving the structure, the rows editor opens. Rows are saved one at a time and can be moved up, down or deleted.

  3. 3

    Fetch the data

    Read your structures from the public, read-only REST API in any front end.

Heads up: deleting a structure also deletes all its rows.

Field types

17 built-in types. Values are sanitized on save and formatted for the API on read.

Type Settings Stored value API value
text none string string
textarea none string string
wysiwyg none HTML (wp_kses_post) string
number none numeric string string
email none string string
url none string string
boolean none "1" / "0" true / false
select options, default option value string
radio options, default option value string
checkbox options, defaults list of option values array of strings
image none attachment ID image object or null
color_picker default, opacity, return format #rrggbb or rgba(r,g,b,a) string or { red, green, blue, alpha }
date_picker display format, return format, first day Ymd date in the return format
post_object post types, multiple post ID or list of IDs post object, null or array of posts
relationship post types, filters, max list of post IDs array of posts
link none { page, url, title, target } { title, url, target } or null
repeater sub-fields list of items array of objects

Repeaters

Repeaters can't be nested. A repeater added as a sub-field is saved as text.

Post object & relationship

Accept any public post type (except attachments) when none are selected. The API returns published posts only.

Links

Point to a page or a custom URL. The page wins if both are set, and its title is used when the link text is empty.

Date picker

Uses PHP date formats. The default for both display and return is d/m/Y.

REST API

Both endpoints are public (no authentication) and read-only.

GET /wp-json/ldcdt/v1/data Lists all structures.
[
  {
    "name": "Team",
    "slug": "team",
    "fields": [
      { "key": "name", "label": "Name", "type": "text" },
      { "key": "photo", "label": "Photo", "type": "image" }
    ],
    "url": "https://example.com/wp-json/ldcdt/v1/data/team"
  }
]

Extending

Add a field type

Register it in ldcdt_field_types(), handle settings in ldcdt_sanitize_fields(), values in ldcdt_sanitize_value() and ldcdt_format_value(), and inputs in the admin partials and admin/js/admin.js.

Hooks & actions

Everything uses the ldcdt prefix: functions (ldcdt_), constants (LDCDT_), tables, options and the REST namespace. Sites upgraded from Custom Headless Data (old chd / chdata prefixes) have their tables and option renamed automatically. Admin form actions:

ldcdt_save_structure ldcdt_delete_structure ldcdt_save_row ldcdt_delete_row ldcdt_move_row ldcdt_search_posts

Ready to go headless?

Install the plugin, define a structure and fetch your data in minutes.

Download v0.1.0