ACF replacement: custom fields with Gutenberg blocks and repeaters
ACF Pro costs up to $249/year, and after purchasing WP Engine, the future of the plugin is in question. I’ll tell you how we built a complete replacement: 20+ field types, repeater, Flexible Content, Gutenberg blocks - with storage in optimized custom tables instead of the bloated wp_postmeta.
COS / KNOWLEDGE BASE
Six months ago, an old acquaintance called me, the technical director of an industrial distributor who has an online store on WooCommerce with sixteen thousand products. He sounded like a man who had just received a bill for bumper repairs and at the same time found out that CASCO does not cover it. “Oleg,” he says, “our ACF Pro subscription is running out, they changed the pricing, and now for our volume of sites they are asking two hundred and forty-nine dollars a year. I also read that WP Engine bought them, and now everyone is wondering what will happen next. We are completely tied to them - all product cards, all characteristics, selection by viscosity, temperature ranges. If they close or break the API, we will stand up.” And this phrase - “we will rise” - it caught my attention, because this is not the first time I have heard it. I hear it at least once a month from different people, and behind it lies the same problem, which few people think about until it hits them on the forehead.
The problem sounds simple: dependence on a third-party plugin for critical functionality. ACF - Advanced Custom Fields - has become the de facto standard for custom fields in WordPress. Almost every WooCommerce store that has gone beyond a simple “name-price-description” uses ACF or its analogues in one way or another. Product characteristics, technical specifications, documentation, connections between products, delivery conditions for specific items - all this lives in custom fields. And all this, in fact, is in the hands of a third-party developer who can change the rules of the game at any time. Which is exactly what happened.
When WP Engine acquired ACF, a wave of anxiety went through the community. Not because WP Engine is a bad company, but because the history of WordPress is full of examples where the takeover of a plugin by a major player led to unexpected consequences. Sometimes a plugin simply stops developing. Sometimes pricing policies change so that small businesses are forced to look for alternatives. Sometimes changes are made that break backward compatibility. And you sit with your sixteen thousand products, which have twenty to thirty custom fields on each, and you think - what should you do? Move? Where? On what? These questions are not abstract; I have received them in the form of direct calls from clients who have invested years of work into their WooCommerce stores.
I remember how, at a WordPress conference in Moscow, I got into a conversation with the owner of a network of car service centers, who had a spare parts catalog on WooCommerce of twelve thousand items. He said that when updating ACF from version 6.1 to 6.2, all the conditional fields on product cards “fell off”. The fields simply stopped showing up - because the internal mechanics of conditional logic changed. They fixed it in two days, but during these two days the site worked with incorrect product cards. Twelve thousand cards with missing characteristics is not just an “inconvenience”, it is a direct loss in conversion. And it’s not ACF’s fault - they improved the product. But dependence on third-party code means that you accept any changes, even those for which you are not ready.
I thought about this not as a theorist, but as a practitioner. On our main project - a large distributor of lubricants - the wp_postmeta table has grown to nine hundred thirty-seven megabytes. Almost a gigabyte of metadata alone. Sixteen thousand eight hundred and forty-four goods, each with dozens of fields. Viscosity, flash point, pour point, manufacturer approvals, GOSTs, series, line, type of base - and I’ve just begun to list this. And all this is in wp_postmeta, because that’s how ACF works. Each value is a separate row in the table. Each repeater is a few more lines with meta information about the number of elements. And when you do WP_Query with a meta query on three or four fields at the same time, the database starts to cry. I'm not exaggerating. I've seen queries take eight seconds to complete on a server with NVMe drives and thirty-two gigabytes of RAM.
What if we look at it differently? Not as a technical task - replacing one plugin with another - but as a strategic decision about who you trust with your business data? When I worked with industrial companies that built ERP systems, not one of them seriously considered the option “let’s move the key logic into a third-party module that we don’t control and for which we pay an annual subscription.” It would be absurd. But for some reason in the WordPress world this is considered the norm. A company with a catalog of twenty thousand products stores all the characteristics, all connections, all technical parameters in a third-party plugin. And he pays for this privilege. And risks losing access with every update. Maybe it's time to stop considering this the norm and start building infrastructure that belongs to you?
That’s why, when we designed the custom fields module for COS WP Woo, I set the task broader than just “do it like ACF, only for free.” Free is a nice bonus, but not the goal. The goal is to do it right. Make sure it doesn’t hurt in two years, when the base has tripled in size. And what’s even more important is not to find yourself in a situation where the key functionality of your business depends on the decisions of a vendor that you do not control.
Why wp_postmeta is an architectural trap
Let's see why I'm so insistent that storing custom fields in wp_postmeta is a bad idea, especially for online stores with a large catalog. WordPress was created as a blogging platform. The postmeta table was designed to store additional information about records - a key-value pair for each record. This works great when you have a hundred articles and each has three or four fields attached. But when you have sixteen thousand products and twenty-five fields for each, you get four hundred thousand rows only for direct values. And if there are also repeaters with three elements in each, multiply by three. Plus technical strings that ACF uses to store field keys, the order of elements in the repeater, and flags. As a result, we easily get a million to one and a half rows in one table.
And this is where the fun begins. MySQL handles a million rows just fine—if you index and query the data correctly. But wp_postmeta has a specific structure: post_id, meta_key, meta_value. The index is on post_id and meta_key. When you search for “all products with viscosity = 5W-30 AND flash point greater than two hundred AND KAMAZ approval = yes,” MySQL must make several JOINs of the postmeta table onto itself. Each meta query is a separate JOIN. Three conditions - three JOINs of the same table of nine hundred megabytes. And the query optimizer begins to work wonders, but not the miracles you expect. It tries to find the optimal execution plan, goes through the options, sometimes chooses a full search of the table - and you get a request that hangs the server.
We measured. On our production server with Redis cache and optimized MySQL, a query with three meta-conditions on a catalog of sixteen thousand products took from three to eight seconds without a cache. With Redis it’s faster, but only while the cache is warm. And the cache is invalidated every time the product is updated. And when you are synchronizing with 1C, which updates balances and prices once an hour, the cache is invalidated cyclically. It turns out to be a vicious circle: you install Redis to speed up meta queries, but synchronization kills the cache, and users again wait for five to eight seconds on the filtering page. This is not a theoretical problem, it is the daily reality of any WooCommerce store with integration with an accounting system.
When we designed the CF module in COS WP Woo, we immediately envisioned a different storage architecture. Three custom tables: wpaic_cf_field_groups for field groups, wpaic_cf_fields for field descriptions, and wpaic_cf_values for values. The value table is structured differently - it knows about data types, relationships between fields, and nesting. It doesn't store everything as text in meta_value, but uses typed columns. Numeric values are stored as numbers, dates as dates, JSON as JSON. This allows MySQL to use normal indexes and normal optimization. The same query with three conditions that took eight seconds in postmeta is executed in two hundred to three hundred milliseconds on custom tables. The difference is twenty to thirty times. And this is not a marketing figure - this is a measurement based on real data, on a real production server with a real load.
There is another non-obvious nuance that is rarely discussed. The meta_value column in wp_postmeta is of type LONGTEXT. This means that the index on this column is partial, based only on the first characters. When you do a meta query with the equals operator, MySQL can use the index. But when you need a LIKE or a numeric comparison, the index is useless because MySQL doesn't know that meta_value stores a number. It is forced to convert the string to a number for each row of the table. On nine hundred megabytes of data, this means a complete search. And no MySQL settings will help - the problem is in the storage architecture itself, and not in the server configuration. We tested this using EXPLAIN on real queries - MySQL honestly showed type: ALL, that is, a full table scan.
But here's the thing. Fast storage is just the foundation. The user in the WordPress admin does not care where his data is physically located. It is important for him that the interface works conveniently, that fields are created easily, that repeaters do not glitch, and that Gutenberg displays custom content normally. And here we come to what actually constitutes the core value of ACF - not data storage, but the management interface. And here we needed not just to repeat what ACF did, but to take a step forward.
Twenty field types and an interface that requires no instructions
When I analyzed how our customers use ACF, I compiled a list of field types that were encountered most often. Text field, textarea, number, email, URL - these are the basic things you can’t do without. Select, checkbox, radio are also standard. But then more interesting things begin: relationships between objects, an image gallery, date and time, color picker, WYSIWYG editor, file attachments, true/false switches, Google Maps (although this last one, to be honest, is needed by only a few). And these “advanced” types are what make custom fields a truly powerful tool.
We have implemented more than twenty field types in the CF module for COS WP Woo. I won’t turn the article into a technical documentation and list each one, but I want to highlight a few that I consider key for WooCommerce stores and which we implemented in a fundamentally different way than ACF.
CfRelationshipField - relationships between objects. This is what almost all of our clients use, and what ACF does not work ideally on large catalogs, to put it mildly. When you have sixteen thousand products and you need to select related ones, AJAX search in ACF starts to slow down. You enter three letters, wait a second or two, the results come in, but you are not sure that you have found all the matching products, because ACF limits the output. Our implementation of CfRelationshipField uses indexed search and pagination. You start entering the name of the product - and the results appear instantly, even on a catalog of twenty thousand items. Because the search is not carried out by meta_value via LIKE, but by normal full-text indexes. And the results are paginated - you can scroll through all the matches, rather than hoping that the product you need is in the first twenty.
CfGalleryField - image gallery. Another sore point. In ACF, the gallery runs through the standard WordPress media loader, and it's... tolerable. But when you need to upload ten photos of a product, sort them in the correct order, crop one, replace another - the process turns into a sequence of clicks on modal windows. I opened the media library, selected a file, closed it, realized that I needed to trim it - opened it again, found the file, opened the editor, cropped it, saved it, closed it. Our CfGalleryField is built right into the metabox: drag-and-drop uploading files directly into the field area, drag-and-drop sorting, cropping images without leaving the product editor. This seems like a small thing, but when a content manager processes fifty products a day, each saved click is multiplied by fifty. In a week, an hour and a half of pure time accumulates. In a month - a whole working day. I don't like to count efficiency in minutes, but when the difference is so obvious, the numbers speak for themselves.
And now about what distinguishes professional work with custom fields from amateur work - Repeater and Flexible Content. Two types of fields that ACF has kept behind a pay wall for a long time, and which essentially determine whether you can build a serious directory with a complex data structure on WordPress or not.
Repeater and Flexible Content: when simple fields are not enough
You know what's funny? When I show new clients a list of field types, they usually say, “Well, we only need text and select, the rest is unnecessary.” And after three months of work, they use relationship, gallery, repeater and flexible content, and ask: “Is there a field type for...” The needs grow with the understanding of the possibilities. And therefore, it was fundamentally important for us to implement all twenty-odd types at once, and not add them gradually. Because when the client realizes that he needs a repeater, but there is none, he goes back to ACF. And then it is almost impossible to return it. First impressions matter, and if on the first day of use a person does not find the desired field type, that’s it, there will be no second chance.
Honestly, repeater is the functionality for which most people buy ACF Pro. The free version of ACF does not include it, and this is the main reason why forty-nine dollars a year is spent on subscription. What is repeater in the context of WooCommerce? Imagine a product card - motor oil. It has approvals from car manufacturers: BMW Longlife-01, Mercedes-Benz MB 229.5, Volkswagen VW 502.00. Each tolerance is more than just a line of text. This is an object with a name, number, status (valid or obsolete), and a link to a confirmation document. And one oil can have from two to fifteen such tolerances. How to store this without a repeater? You can, of course, stuff everything into one text field separated by commas. But then you won’t be able to properly filter by tolerances, you won’t be able to display them in a structured form, you won’t be able to check their validity. Or you can create fifteen separate fields “Tolerance 1”, “Tolerance 2”... “Tolerance 15” - but this is ugly and inflexible.
Repeater solves this problem elegantly. You create a group of subfields - “Name of approval”, “Number”, “Status”, “Document” - and indicate that this group is repeatable. In the product editor, the content manager sees the “Add permission” button, clicks it, and a new block with four fields appears. Filled it out, clicked again - the second block appeared. You need to change the order - drag and drop with the mouse. Need to delete - click the cross. Everything is intuitive, everything works. The manager does not need to know anything about the database, JSON, or meta fields - he simply clicks “Add” and fills out the form.
Our CfRepeaterField in COS WP Woo goes beyond the standard ACF repeater in several important ways. In ACF, repeater data is stored in postmeta as a set of separate meta fields: _tolerances_0_name, _tolerances_0_number, _tolerances_1_name, _tolerances_1_number, and so on. Plus a separate field _tolerances, which stores the number of elements. For a product with ten tolerances and four subfields, that’s forty-one postmeta entries for just one repeater. Forty-one rows in a table that already weighs almost a gigabyte. And if you have three repeaters on a product - one for approvals, one for technical characteristics, one for documentation - you easily get one hundred and twenty lines in postmeta for one product. Multiply by sixteen thousand products and you will get almost two million lines from repeaters alone. Our implementation stores repeater data as a JSON document in one field of a typed table. All ten tolerances with all subfields - one entry instead of forty-one. At the same time, search and filtering work through MySQL JSON functions, which in version 8.0 and higher are quite fast and support indexing.
But repeater is just a small thing. Real power starts with Flexible Content. Imagine that you need to create not just a repeatable set of identical fields, but a constructor from different blocks. On a brand page, for example, there may be: a text block with a description, a block with characteristics in the form of a table, a block with a product gallery, a block with a video review, a block with dealer reviews. Each of these blocks has its own field structure - the text block has only a WYSIWYG editor, the gallery has a set of images with captions, the characteristics table has a repeater with parameter-value pairs. And the user should be able to add any blocks in any order - like a Lego constructor. This is Flexible Content.
CfFlexibleContentField in our module implements exactly this scenario. You define a set of “layouts” - each with its own fields. And in the editor, the content manager selects the desired layout, adds it to the page, and fills in the fields. Maybe add another one the same or different. Can drag blocks and change order. The result is a completely custom page structure, assembled from ready-made components, without a single line of code. And I believe Flexible Content is what transforms WordPress from a blogging platform into a full-fledged enterprise-grade content management system. Because without it, every non-standard page requires developer intervention, but the content manager can handle it himself. For B2B companies this is critically important: brand pages, product catalogs by industry, technical sections - all this must be updated regularly, and it is simply unprofitable to attract a programmer every time.
And here we come to a topic that was a real revelation for me - the integration of custom fields with Gutenberg. When I started this project, I was a skeptic. I thought Gutenberg was clunky and redundant for WooCommerce. But I was wrong, and here's why.
Gutenberg blocks and visual rule builders
Do you know what irritated me most about ACF? The fact that custom fields live separately from the content. You open the product editor, at the top there is Gutenberg with a description, and somewhere below, under the five scrolling screens, there are ACF metaboxes with custom fields. Two separate universes that know nothing about each other. The content manager is forced to scroll back and forth, remember what he is responsible for, and hope that he did not miss a required field somewhere in the lower metaboxes. This is not an architectural problem - this is a user experience problem. And for a content manager who works with fifty products a day, this experience determines his productivity.
I've seen projects where people invent their own repeatable fields at the theme level - via JavaScript and AJAX in the admin. It worked, but every WordPress or WooCommerce update turned into a lottery. The self-written repeater could break due to a change in jQuery, due to a new metabox format, or due to an update to the TinyMCE editor. And each time the developer spent hours fixing what should, in good faith, be the standard functionality of the platform. Custom fields with repeatable blocks are not a luxury, they are the basic infrastructure for any serious directory. And the fact that WordPress still doesn't include this in the core is one of the main reasons why there is an entire plugin industry around custom fields.
When we designed the CF module, I set a goal: each group of custom fields should automatically receive a block view in Gutenberg. Not “can be configured via a separate plugin”, not “requires additional development”, but automatically. I created a group of fields and the corresponding block immediately appeared in Gutenberg. This is implemented through the CfBlocks component, which dynamically registers Gutenberg blocks based on the configuration of field groups. Technically, it works like this: when saving a group of fields, the PHP code generates a block registration via register_block_type(), and the React component renders the input form directly in the editor.
What does this look like in practice? Let’s say you created a group of fields “Oil Technical Characteristics” with the following fields: viscosity, flash point, pour point, manufacturer’s tolerances, area of application. After saving the group, the Oil Specifications block appears in the Gutenberg block library. The content manager can insert it directly into the product content, next to the description, between paragraphs - anywhere. And the fields are displayed in the context of the content, rather than in a separate metabox at the bottom of the page. This fundamentally changes the workflow. Instead of “fill out the description at the top, then scroll down and fill out the characteristics,” you get a single flow - description, then a block with characteristics, then another description, then a block with tolerances. Everything is in one place, in the right order.
Of course, we also retained the classic approach with metaboxes. Not all users are ready for Gutenberg, and not all scripts require block representation. There are companies where the WordPress editor is set to classic mode, and changing people’s habits is not our job. If you are used to metaboxes, please, they work exactly the same as in ACF. But the opportunity to switch to blocks is there, and it does not require additional effort. This is a matter of team readiness, not a matter of technical limitations.
Now about the visual designers - one of the things I'm most proud of in this module. CfLocationRulesBuilder is a visual builder that determines exactly where your custom fields will be displayed. In ACF, this works through dropdown lists: "Show this field group if the record type is product AND the product category is masla." Dropouts, small text, non-obvious AND/OR logic that confuses even experienced users. Our builder is a visual interface built in React, where the rules are represented as cards. You literally see the logic: this block of conditions is connected through AND, and to another block through OR. You drag the cards, change the conditions, and you immediately understand what will happen. I showed this to several content managers who had previously been afraid to touch the ACF settings for months - they figured it out in five minutes. No documentation, no training.
CfConditionalLogicBuilder goes even further. This is conditional logic at the individual field level: show or hide a field depending on the values of other fields. For example, if the oil type is “motor”, show the “engine tolerances” field. If the type is “hydraulic”, show the “ISO class” field. If the manufacturer “Shell” is selected, show the additional field “Shell line”. Why is this necessary? Then, the product card for motor oil and hydraulic oil are two different cards with a different set of fields. And instead of showing the manager all thirty fields at once (half of which are not applicable), conditional logic shows only the relevant ones. Fewer fields - fewer errors - faster completion. ACF has this, but it's implemented as a simple list of conditions with no visual feedback. Our builder shows the connections between fields visually - you see which fields depend on which ones, where which logic is applied. And most importantly, you can test the conditions directly in the designer, without going to the product editor.
I've been wondering for a long time whether these visual builders are worth my time. The code is just that: it could just be a configuration file or a software API. But then I remembered how many times clients called me with the question “why is this field not showing up in such and such a category?” - and the answer was always the same: the location rules were configured incorrectly because the configuration interface was incomprehensible. The visual designer does not solve all the problems in the world, but it solves this specific one - people stop making mistakes when setting up, and they stop calling me. And this, believe me, is worth the weeks of development that we spent on these components.
Compatibility layer and ecosystem: why it's not just another plugin
When we started designing the CF module, I immediately realized that ninety percent of potential users were people who were already using ACF. And they are not ready to just “pick up and switch.” They have theme templates that call get_field() and the_field(). They have plugins that integrate with ACF through its API. They have custom code in functions.php that uses ACF hooks. Moving without a compatibility layer means: rewriting the entire frontend of the site, all templates, all integrations. For a site with sixteen thousand products, this means months of work and a huge risk of breaking something. This is not a risk that a smart business would take, even if the new tool is objectively better.
Therefore, we implemented register_cf_compat() - a compatibility layer that intercepts all standard ACF functions and routes them to our module. The get_field(), the_field(), get_sub_field(), have_rows() functions all work. You can deactivate ACF, activate our module, and your templates will continue to work without changing a single line of code. The compatibility layer works at the level of wrapper functions. When your template calls get_field with a field key and a post ID, our wrapper determines which field group the field belongs to, accesses the wpaic_cf_values table instead of postmeta, and returns the value in the same format that ACF would return. For repeaters, the logic is more complicated - have_rows() initializes the internal iterator, the_row() moves the pointer, get_sub_field() retrieves the value from the current row. All these mechanics are reproduced exactly because we understand: one broken place in the template and the client returns to ACF forever.
But I want to be honest: 100% compatibility is a utopia. If your code uses ACF internal classes directly, or if you are tied to specific hooks that are not in the documentation, there may be nuances. We have covered the public API, which is described in the documentation and which is used in ninety-five percent of cases. The remaining five are exotics that need to be analyzed individually. For the vast majority of sites, the migration looks like this: installed COS WP Woo, activated the CF module, imported field groups from ACF, deactivated ACF, checked - it works.
Data migration is a separate process. The compatibility layer allows your site to work immediately, but the data is still physically in postmeta. To get performance benefits, we need to transfer data from postmeta to our custom tables. This is a separate operation that is performed by a background process through the Action Scheduler - without stopping the site, without downtime. For sixteen thousand products, migration takes approximately twenty to thirty minutes. After migration, you get both compatibility with the old code and the performance of the new storage. And the old data in postmeta can be left as a backup or deleted when you are sure that everything is working correctly.
What about the REST API? This is a question that developers are asking, and it's completely legitimate. In modern projects, the frontend is increasingly separated from the backend - headless architecture, mobile applications, integration with external systems. Access to custom fields via API is not a luxury, but a necessity. ACF Pro provides a REST API, but with caveats: you need to explicitly enable API support for each group of fields, the response format is not always predictable, and for repeaters, the data comes in a flat form, which you need to independently assemble into a structure. Our module provides a full REST API right from the start. One controller with full CRUD for field and value groups. Field values are available in nested format: repeater is returned as an array of objects, Flexible Content is returned as an array of blocks indicating the layout type. No manual data collection on the client side. The format is predictable, documented and does not change between versions.
Nine services run the module behind the scenes. CfFieldGroupService manages field groups. CfFieldService - individual fields and their configurations. CfFieldValueService - reading and writing values. CfRelationshipService - connections between objects. Six React pages in the admin panel give full control: list of field groups, group editor, layout rules designer, conditional logic designer, migration from ACF, module settings. Everything is built on standard WordPress components, so the interface looks native and does not conflict with other plugins.
I know that many will say: “Why do all this if there are Carbon Fields, Pods, Meta Box?” It's a fair question, and I'll answer it directly. Carbon Fields is a great free plugin, but it does not have a visual interface for creating fields in the admin panel, everything is done through code. This is normal for a developer, but a dead end for a content manager. Pods is a powerful framework, but it's overkill for most tasks and has a steep learning curve that intimidates even experienced WordPress users. Meta Box is good, but paid for advanced functions, and is again tied to its own ecosystem of a dozen addons. Each of them solves part of the problem, but none solves the one I described at the beginning: dependence on a third-party vendor for mission-critical functionality.
But the most important thing is that none of these plugins are integrated with your search, your 1C, your B2B module. The CF module in COS WP Woo is part of the ecosystem. It works together with the 1C integration module, which maps product attributes from 1C: Trade Management to custom fields. It works with a search module that indexes custom fields for full-text search via Typesense. It works with the B2B module, which uses custom fields to configure group access rights. When a manager adds a new permit to the repeater, our module writes data to wpaic_cf_values, updates the Typesense search index through the search module hooks, and checks if the permit refers to KAMAZ or MAZ, automatically adds the product to the “For Russian equipment” category. One manager action results in three results in different systems.
I'm not saying ACF is a bad product. He's great. He set the standard for custom fields in WordPress, and for that we are very grateful. But the world has changed. WordPress has become a platform for serious e-commerce, for B2B portals, and for integration with ERP systems. And the requirements for custom fields have grown so much that the “everything in postmeta, everything through meta queries” approach simply stopped scaling. It's not ACF's fault - it's the evolution of the tasks. I often use the analogy of transportation. The ACF is a reliable sedan that drives well around town. But when you need to transport twenty tons of cargo from Chelyabinsk to Moscow, you take a truck. Not because the sedan is bad, but because the task is different.
As for the cost, let's count honestly. ACF Pro costs from forty-nine to two hundred and forty-nine dollars per year, depending on the number of sites. Over five years - from two hundred forty-five to one thousand two hundred forty-five dollars only for custom fields. Add to this the WooCommerce extensions that are needed for filtering by meta fields, for B2B pricing based on meta fields, for search indexing of meta fields - and the total cost of ownership increases three to four times. COS WP Woo includes all this in one license. Custom fields, search, B2B, 1C - everything works together, everything is supported by one team, everything is updated synchronously. No need to check the compatibility of five plugins every time you update. There is no need to write hooks to link one plugin to another. There is no need to pray that the ACF update does not break the integration with the search plugin.
But I don't want this article to sound like a sales pitch. Therefore, I will be honest about what our module does not yet do. We do not have a separate plugin for the frontend that would generate forms based on custom fields - we have our forms module for this. We don't have blocks for Elementor or WPBakery - we rely on Gutenberg, and if your site is built entirely on Elementor, the integration will be through shortcodes rather than native widgets. And our compatibility layer with ACF does not cover one hundred percent - I already talked about exotic scenarios. This is an honest position, and I prefer to be open about the limitations rather than deal with disappointed users later.
That's what I think is most important. When you choose a tool for storing and managing your business data - and custom product fields are your business data - you should think on a five- to ten-year horizon. Will ACF still exist in five years? Most likely yes. But will it cost the same? Will it support the same architecture? Will WP Engine take it in the direction your business needs? Nobody knows this. And when you have sixteen thousand products and the postmeta table weighs a gigabyte, every wrong answer to these questions is very expensive. I choose predictability—a tool that I control, that stores data efficiently, that is integrated with my ecosystem, and that is not dependent on the strategic decisions of a third-party vendor.
There is one thought I want to leave for last. We live in an era where data is the main asset of business. The characteristics of your products, their connections, their technical parameters are not just fields in a database. This is knowledge that your company has accumulated over the years. The viscosity of each oil, compatibility with each type of engine, recommendations for each industry - behind each field filled out are hours of technical work. And this knowledge is stored in a tool that you do not control, in a format that is not optimal for your tasks, in a table that weighs almost a gigabyte and slows down with every request. Maybe it's time to think about how to store this knowledge correctly? It's time to choose a tool that respects your data as much as you respect your customers.
And if you are currently sitting in front of the screen with an open letter about renewing your ACF Pro subscription and thinking “what if I don’t renew?” — try asking yourself three questions. How much does your dependence on ACF cost in money - not only the subscription, but also the time to support integrations? How much does your postmeta table weigh and how does this affect site speed? And do you have a plan B in case ACF changes the API or pricing policy? If at least one of the answers does not suit you, then it’s time to think about changes.
Try COS WP Woo - fourteen days free. Install, activate the custom fields module, import one group from ACF, work with it for a week. Feel the difference in request speed, try the visual rules builder, see how repeater works in a Gutenberg block. And then decide - with your own hands, using your own data, without marketing promises. Because the best argument is your own experience.