Dealing with custom fields and meta fields in WordPress is a daily part of the job for any WordPress developer. Although most devs and that includes some of the houses that sell popular plugins still rely on Advanced Custom Fields to manage custom fields, maybe we should move to CMB2 instead.

Don’t take me wrong: ACF is a powerful framework with a comprehensive API, that makes possible to anyone turn a WP website into a completely different application. Then, why bother to change it?

First thing we need to understand about ACF doesn’t have anything to do with its features, but the business model around that system. ACF alone, in its free version, already offers hundreds of useful tools. However, most dev-friendly features still need the Pro version to be widely used.

Really free

First of all, CMB2 is really free – all of it. ACF can be used to perform a serie of functionalities for free, but some widely used features remain exclusive for those who pay for the Pro version: such as options pages.

Besides the absence of any cost, CMB2 is well documented and stands as a library rather than a plugin. With ACF, it’s the opposite. One of the great advantages of ACF is the interface it offers when used as plugin – while it gives it a wide range of possibilities for web designers and users, it turns life harder for some devs.

Oh, and if you are not familiar with CMB2 yet, take a peek on its repo and check not only its wiki, but also the great number of extensions the community already developed. Altogether, it gives us even more tools than those offered by ACF Pro.

Dark Mode

CMB2 (this link opens in a new window) by CMB2 (this link opens in a new window)

CMB2 is a developer’s toolkit for building metaboxes, custom fields, and forms for WordPress that will blow your mind.

OOP possibilities

Object-oriented possibilities have been attracting more and more devs within the WordPress community. Some of them still regret the way WordPress core have been changing rapidly in terms of JS features, especially regarding the block editor, while remaining basically the same in terms of PHP coding.

Nonetheless, every PHP programmer nowadays want apps to be well-designed, usually in an OOP way. More than just a “trend”, the OOP thinking allow WP users to make use of any dependencies and frameworks that may appear, while simply following the WordPress API can unnecessarily limit projects and plans.

By default, nor CMB2 neither ACF are developed in an OOP way. However, the simplicity of CMB2 provides a more promising horizon for those who want to create interface, abstracts and patterns on its coding.

A simple example

Now to the practice. Let’s consider we have ACF plugin installed in a WP instance, and we want to create an options page for a new plugin or theme. ACF can produce beautiful settings and options pages, but for such we’d need to purchase its Pro version.

Once we are all devs, we don’t want to spend that kind of money in something we can easily solve. Thus, we decide to simply scaffold a new plugin and, using Composer, require CMB2 library.

Then, it’s just a matter of looking over the CMB2’s wiki examples to find the one which fits best in your case. Using them, you can:

  • Create new options or settings pages
  • Add fields to existing settings pages
  • Manage and create tabs
  • Add menu and submenu items to WP admin

All fields possible using ACF can be also reproduced by CMB2, including repeatables and ajax-based fields. The basic framework can be missing some, but they can be easily included requiring the extensions suggested in the repo.

But let’s make it more interesting. I propose a class which can be extended for creating new options pages, or add fields to existing ones. So, let’s create a basic class for getting it done:

class Settings {

	protected $metabox;	
	protected $fields = array();
	public function __construct( array $metabox,  array $fields ) {
		$this->metabox = $metabox;
		$this->fields = (array) $fields;		
		add_action('cmb2_init', array($this, 'create'));		
	public function create() {
		$box = new_cmb2_box($this->metabox);		
		foreach($this->fields as $field) {
		return $this;

The class __construct admits two arguments: an array of settings for the metabox, in our case, the options page itself, and another array of fields. The function create() will get those arguments and, applying CMB2 native methods, will generate the metabox and the fields, or the options page we want.

Now we just need to extend that class for creating a new admin page where the the options will be managed.

class ControlPage extends Settings {

  public function __construct() {
        'id' => '',
        'title' => '',
        'description' => '',
        'object_types' => array( 'options-page' ), // Indicate CMB2 this is an options page
        'display_cb' => array($this, 'admin_page'), // The callback that renders the page template or form
      [/* field_1 settings */], 
      [/* field_2 settings */]
      // and so on...

  public function admin_page($hookup) {
      include __DIR__ . '/templates/settings.php // Page render or an include to a template file

That’s it: you extend the helper class and calls the parent __construct(), passing two arrays as arguments – the CMB2 metabox data and then an array of all included fields. The parameter display_cb on the metabox data determines the template callback, the admin_page() method in this case.

But now a demonstration on how powerful CMB2’s API can be. Let’s check the template file, settings.php. CMB2 has special helpers to pass the metabox and field variables to the template files. The variable $hookup declared in the class render method will be used to generate the respective view, like this:

<div class="wrap cmb2-options-page option-<?php echo $hookup->option_key; ?>">

    <form class="cmb-form" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="POST" id="<?php echo $hookup->cmb->cmb_id; ?>" enctype="multipart/form-data" encoding="multipart/form-data">

        <input type="hidden" name="action" value="<?php echo esc_attr( $hookup->option_key ); ?>">

        <?php $hookup->options_page_metabox(); ?>

        <nav class="navbar fixed-bottom navbar-light bg-light">
            <ul class="navbar-nav ml-auto">
                <li class="nav-item">
                <?php submit_button( esc_attr( $hookup->cmb->prop( 'save_button' ) ), 'primary', 'submit-cmb' ); ?>

More possibilities

Just like ACF plugin, CMB2 has endless possibilities. Metaboxes can be generated in any post or term type as well, and also to create widgets or ajax forms. Currently, it is still not compatible with Gutenberg’s blocks, but that would be the sole advantage point for ACF.

For developers, though, its API offers a more flexible and code-based framework that can be applied in any plugin or theme project, and perhaps even in non-Wordpress PHP projects.