WordPress Development

ACF vs Native Custom Fields: Choosing a Field Layer

What a field plugin buys you over WordPress's own custom fields, what it costs in lock-in, and how to structure fields so the site survives changing your mind.

By 6 min read
Stacked block layouts representing structured content fields

WordPress has had custom fields since long before field plugins existed. The native interface is a key-value box at the bottom of the editor, and it works — it's just unusable for anyone who isn't a developer.

That gap is what field plugins fill. Here's what you're actually choosing between, and how to make the choice cheap to reverse.

What native custom fields give you

Post meta is core WordPress. Any post can have arbitrary key-value pairs, stored in wp_postmeta, retrievable with get_post_meta, queryable with a meta query. It's stable, well-documented and going nowhere.

You can register meta with register_post_meta to declare a type, a sanitisation callback and whether it appears in the REST API — which matters for the block editor and for anything headless.

What you don't get is an interface. The native custom fields panel is a flat list of key-value text inputs with no validation, no field types, no repeatable groups and no way to stop an editor typing a date into a number field. It's fine for a developer editing one value and hopeless for a client managing content.

What a field plugin actually buys

None of that changes where the data lives. Most field plugins store values in ordinary post meta, which is the important thing to understand about the lock-in question.

  • Field types: image, file, date picker, colour, WYSIWYG, relationship to another post, taxonomy selector, true/false. Each with an appropriate control.
  • Validation and required fields, so incomplete content can't be published.
  • Repeatable groups, for things like a list of team members or a set of specifications.
  • Conditional logic, so fields appear only when relevant.
  • Field groups assigned to specific post types, templates or taxonomies.
  • A vastly better editing experience, which is the real product.

How much lock-in is there really?

Less than people fear, with caveats. Simple field values are stored as normal post meta under the field's name, readable with get_post_meta without the plugin present. Deactivate the plugin and your content is still there.

The caveats matter though. Complex field types — repeaters, flexible content, nested groups — are stored in a plugin-specific naming scheme that's tedious to read without the plugin's functions. Relationship fields store arrays of IDs in serialised form. And the field definitions themselves live in the plugin's format.

So the honest position: your content is portable, your field structure isn't, and the more you use the advanced field types the more work a migration becomes. That's an acceptable trade for most projects, but it's worth making knowingly.

Define fields in code

This is the practice that makes the difference over a project's life, and it's the one most often skipped.

If fields are defined only in the database through the admin UI, they aren't in version control, they can't be deployed with code, and staging and production drift apart. Someone adds a field on production, someone else adds a different one on staging, and now the two environments render differently and nobody knows why.

Most field plugins support exporting definitions to PHP or JSON that lives in your repository and syncs automatically. Use it. Fields become part of the codebase, deploys carry them, and the environments stay identical.

Naming and structure

  • Prefix field names so they can't collide with a plugin's meta keys.
  • Name for what the value is, not how it looks. hero_image, not top_banner_pic.
  • Store IDs for images and posts, not URLs or titles. IDs survive a domain change, a CDN switch and a retitle.
  • Keep booleans as booleans, not the strings a text field will give you.
  • Don't store presentation — CSS classes, inline styles, colour hex values chosen per post. Store meaning and let the template decide the presentation, or a redesign becomes a data migration.
  • Group related fields, so the editing screen has structure rather than thirty flat inputs.

Performance considerations

Post meta is fast to read for a single post — WordPress loads all of a post's meta in one query and caches it. Reading twenty fields from one post costs no more than reading one.

Where it gets expensive is querying by meta value across many posts. Meta queries join a large table on values that aren't efficiently indexed, and they get slower as the site grows. If you need to filter or sort by something at scale, that something should probably be a taxonomy, not a field.

Repeaters with many rows generate a lot of meta rows — each sub-field of each row is its own entry. A repeater with ten rows of five fields is over fifty meta rows for one post. Usually fine; worth knowing when a page has several such repeaters and feels slow.

Fields, blocks or a post type?

These three solve overlapping problems and choosing wrong produces a site that's awkward to edit.

  • Fields are right when every item of a type has the same structured attributes — a case study always has a client, an industry and a result.
  • Blocks are right when editors compose a page from components in an order they choose.
  • A custom post type is right when the thing has its own identity, URL and archive.
  • A common mistake is a huge field group for page content that editors actually want to reorder — that's blocks. Another is a block per content type — that's usually a post type with fields.

Headless considerations

If a separate frontend will consume this data, check the API story before committing. Fields are not automatically exposed — native meta needs register_post_meta with show_in_rest, and plugin fields usually need a bridging extension.

Those bridges vary in quality and maintenance, particularly for complex field types. A repeater or flexible content field that's awkward to consume over an API is a problem you discover late, after the content model is built.

For a headless project, test the API output of your intended field structure early, with realistic content in it.

A reasonable default

For most client sites: use a field plugin for the editing experience, define the fields in code and keep them in version control, name fields semantically, store IDs rather than URLs, and use taxonomies for anything you'll filter by.

That gets you the good interface without the parts of the lock-in that actually hurt, and it means the next developer can read the field structure in the repository rather than clicking through an admin screen.

Frequently asked questions

Is my content locked in if I use a field plugin?

Your content is mostly portable — simple field values are stored as ordinary post meta, readable without the plugin. What isn't portable is the field definitions and the storage format of complex types like repeaters and flexible content. The more you use advanced field types, the more work a migration becomes.

Can I use native custom fields instead of a plugin?

For storage, absolutely — post meta is core and perfectly capable. The problem is the interface: the native panel is flat key-value text inputs with no validation or field types, which is unusable for non-developers. If only developers edit the content, native fields plus register_post_meta is a legitimate choice.

Should ACF fields be defined in code or the admin UI?

In code, or exported to code that lives in your repository. Definitions that exist only in the database aren't version controlled, don't deploy with your code, and let staging and production drift apart. Most field plugins support syncing from files — use it.

Do custom fields slow down WordPress?

Reading fields from a single post is cheap; WordPress loads all of a post's meta in one cached query. What's expensive is querying by meta value across many posts, because meta queries join a large, poorly indexed table. If you filter or sort by something at scale, make it a taxonomy instead.

Should I use fields or blocks for page content?

Fields when every item has the same structured attributes in a fixed arrangement. Blocks when editors need to compose a page from components in an order they choose. A large field group for content editors keep asking to reorder is the clearest sign you wanted blocks.

Topics

  • ACF
  • WordPress custom fields
  • Advanced Custom Fields
  • WordPress meta fields