Thursday, June 25, 2026

WordPress Null Handling Confusion in Custom Fields

I recently built a WordPress plugin that was going to handle custom fields. The functionality was similar to the plugin ACF (Advanced Custom Fields). However, I struggled with understanding how the null handling was made.
After some discussions with ChatGPT it seemed that WordPress treated null as "nothing existing in the database".
To complicate things further, I also used the postmeta table in WordPress which has a EAV type structure, which is used to create a flexible way of adding custom fields.
The structure of the table is: meta_id, post_id, meta_key and meta_value, where meta_key are the name of a custom field added (a so called meta field) and meta_value is the value the user puts in that field.
Simple enough.

So by going by the way, allegedly, WordPress sees null, if nothing is stored in the custom field, neither meta_key nor meta_value is stored and WordPress reads this as null.

This was my initial understanding of null in WordPress and I figured I would adhere to this logic. However, when saving from the Gutenberg editor and not from the front-end posting, a discrepancy was noted.
When posts where submitted from the front-end, it behaved like a WordPress "null": neither meta_key or meta_value was saved, if nothing had been put in that custom field.
However, from Gutenberg, when saving posts there it would save a meta_key and nothing in the meta_value field. The value was however not a database null, but likely an empty string.

This was the behavior I noticed in ACF.
So in the case of a post where many custom fields had been added, when saving from Gutenberg it saved multiple empty fields in the database. After some research I figured that was mostly a space concern which may not be so important, but as it had some type of exponential growth it could easily start to fill up this table with many empty values.

That didn't seem great but as I was writing code with some requirements, I simply followed the logic I thought was necessary. But I also thought, intuitively that maybe deleting both meta_key and meta_value would be a good thing and created a "nullable option" for the custom fields.

This followed the WordPress null logic, where nothing in the database meant "nullable => true" and when keeping meta_key:s, then "nullable => false".

The problem was that this caused some confusion, both for myself and the team that were using the code.

After an extra check, I realized that this was not how ACF handled it when their nullable option was set ("allow null"). To ACF "allow null" meant rather converting empty strings to null values in PHP. And when stored in the database, null was converted to empty string.

Something like this simple schematic:

Database <- '' <- null 
'' -> null -> PHP

Now, ACF used their own function to handle this feature, as a layer in PHP. It was called get_field().
It turns out WordPress has its own function that gives the same result: get_post_meta()
where the null handling however, looks a little bit different:

When using

get_post_meta($post_id, 'fieldname' , true) it returns the first value in a array of values: [value*]

and when using
get_post_meta($post_id, 'fieldname' , false) it returns all of the values in a array of values: [value1, value2]

* value = for example: 1, or "string"

So when the field is empty, it will return [] (nullable, or null)
or [''] when the field contains an empty string ('')

(Whereas ACF seem to do (convert): [value] => value, [''] = null, [] = null.)

Now this functionality wasn't requested and it turned out that deleting both meta_key and meta_value was the requested behavior, hence my code worked using "nullable => true".
But of course, the confusion prevailed until I changed the name of the option from nullable to "remove_when_empty".

Semantically this made a lot more sense and since null handling wasn't requested, no further adjustments needed to be done at that point.

What can also be added, however, is that when creating these options, I had to handle the empty values, and normalize them to empty strings (' ') if no value was added.

When I write this writeup I realize it is still somewhat confusing and by writing this text I try to create some clarity also for myself, besides telling a story about this mysterious null issue.

Maybe it is not so mysterious but I think that I begin to understand the issues that has been raised about "null hell". In this case, I think it stemmed from a question of definition. What is null, really?
How should null be defined?

Going back to theory, it could be conceived as:  null => 'nothing' was ever inserted at all,
while empty string (' '), or ('') means the same as 0.
The difference could for example be a user field, where either "no users has been added", or "users has been added, but was removed".

It is a slight difference in these cases but enough to create a lot of confusion.

tl; dr: I implemented a null that wasn't null, so eventually the function was renamed to remove_when_empty.

Front-ends and SPA-functionality on WordPress

I have for the last couple of weeks been exploring some techniques for creating a SPA type front-end using WordPress as the backend.

Althought WordPress is a stable and trustworthy CMS system, one thing that may be a bit old school is the way the CMS works with regards to page reloads. Since everything is rendered on the server, using PHP, every time a page needs to be updated, a page reload is required.

However, using JavaScript and AJAX, it is possible to achieve a Single Page Application (SPA) type front-end.

My first idea was simply to just use WordPress as a backend, using the REST-API that is built in and not worry about trying to use the front-end part of WordPress: working with the theme in wp-content.

The setup is basically that you use WordPress as a backend server and configure the web server so that WordPress is not on port 80.

Instead you create a standalone front-end in your preferred front-end framework – my choice was React and then you use the REST-API endpoints from your WordPress “bare metal” back-end server.

This worked quite well, but one thing that became obvious was that you would also need to handle the authorization and authentication as well. At least if you wanted user system functionality.

You could keep the front-end and “admin system”, using the login /wp-login.php or /wp-admin for administrative use and only use material from WordPress that is considered “public”.

This can be good enough for certain purposes but if you want users to be able to login and so on (and not login using the above mentioned wp-admin area), this might not be ideal.

However, I quite early on this attempt and it could be interesting to explore it a bit further at a later point.

My second attempt was to work with the theme and not a stand alone front-end. Instead, I opted for using some JavaScript/AJAX technology. Some possible options would include vanilla JavaScript for fetching JSON, jQuery AJAX/fetch or HTMX.

I decided to try HTMX as I had not used it before… And it turns out it was quite simple to implement and I got some nice results, being able to swap out parts of the DOM.

This worked well for certain type of material but I soon ran into some difficulties with state management. For example, to keep track of “back link URL:s”, turned out the be more troublesome than I first expected. However, a similar problem had occurred without HTMX so maybe it is JavaScript that introduces such issues.

I got my custom post type “works” to function quite well with HTMX, which made it possible to filter type of work, that was categorized using a custom taxonomy. Left to to do is the mentioned back links, so that you get back to filtered results. Another thing that needs to be solved is pagination, and pagination for filtered results.

But for now, it works quite well and is not over-engineered nor underdeveloped, in my humble opinion… But of course, every time one develops features like this, one runs into the risk of reinventing the wheel. Which I guess adds to the *fun* of web development.

To conclude, this is not my final say in this matter, but some reflections on my progress. Hopefully I will be able to come back to this topic at some point, perhaps when I have gotten some more experience with this type of development.

WordPress Null Handling Confusion in Custom Fields

I recently built a WordPress plugin that was going to handle custom fields. The functionality was similar to the plugin ACF (Advanced Custom...