How to build a custom features comparison table block

WordPress custom features comparison table block

Welcome back to The Lab.

In this tutorial, I’m going to show you how to build a custom features comparison table block. If you’ve got a site where you’re comparing product features, having something like this would be pretty handy. A great use case we see regularly is for product reviews, particularly for affiliate marketing focused content.

We’re going to build one focused on a few fictional managed WordPress hosts. When we’re done, it’s going to look like this:

Fair warning, we’ll use the repeater field in this block which is a Block Lab Pro feature, but you could still build this without the repeater and use a fixed number of columns.

Let’s do this

1. Block Setup

In our WordPress admin, with Block Lab installed, we’re going to register (add) a new block, configure the settings, and add some fields for our block form. When done, it looks like this:

Things to note:

  • All the fields for this block are wrapped in a repeater field called hosts
  • Mosts fields (sub-fields) in the repeater are the features we are reviewing for the hosts
  • The link field is where we will link to the host (affiliate link)
  • The final featured field will let us conditionally style a column to highlight a particular host in a certain way

Done! Next step…

2. Building the Block Template

Let’s build this out now. In our theme, we add a blocks folder and then within it, add a folder for our newly registered block. We need to give the folder the same name as our block slug, which in this case is comparison-table. In this folder we add 2 files: block.php and block.css.

For clarity’s sake:


Let’s open our block.php file. We’ll be working with the functions that Block Lab gives us for interacting with data saved via our block form. If these are new to you, check out the docs here.

Our block.php file code looks like this:

<div class="block-comparison-table alignwide">
    <div class="block-comparison-table__column block-comparison-table__column--row-label">
        <div>Daily Backups</div>
        <div>Staging Site</div>
        <div>Security Scans</div>
        <div>24/7 Support</div>
        <div>Shared, VPS, or Dedicated</div>
        <div>Price per month</div>
        <div>Number of Sites</div>
        <div>Traffic (visits per month)</div>
        <div>Performance Score</div>


    if ( block_rows( 'hosts' ) ):

        while ( block_rows( 'hosts' ) ) :
            block_row( 'hosts' );

            $featured_class = '';

            if ( true === block_sub_value( 'featured' ) ) {
                $featured_class = 'featured';

            echo '<div class="block-comparison-table__column ' . $featured_class . '">';
            echo '<div class="block-comparison-table__header">' . block_sub_value( 'host-name' ) . '</div>';
            echo '<div>';
            if ( true === block_sub_value( 'daily-backups' ) ) {
                echo '<div class="checked"></div>';
            } else {
                echo '<div class="unchecked"></div>';
            echo '</div>';
            echo '<div>';
            if ( true === block_sub_value( 'staging-site' ) ) {
                echo '<div class="checked"></div>';
            } else {
                echo '<div class="unchecked"></div>';
            echo '</div>';
            echo '<div>';
            if ( true === block_sub_value( 'security-scans' ) ) {
                echo '<div class="checked"></div>';
            } else {
                echo '<div class="unchecked"></div>';
            echo '</div>';
            echo '<div>';
            if ( true === block_sub_value( '247-support' ) ) {
                echo '<div class="checked"></div>';
            } else {
                echo '<div class="unchecked"></div>';
            echo '</div>';
            echo '<div>' . block_sub_value( 'shared-vps-dedicated' ) . '</div>';
            echo '<div>$' . block_sub_value( 'price' ) . ' p/m</div>';
            echo '<div>' . block_sub_value( 'number-sites' ) . '</div>';
            echo '<div>' . number_format( block_sub_value( 'traffic' ) ) . '</div>';
            echo '<div>' . block_sub_value( 'performance-score' ) . '/100</div>';
            echo '<div><a class="host-link" href="' . block_sub_value( 'link' ) . '">Learn More</a></div>';
            echo '</div>';



    reset_block_rows( 'hosts' );

Things to note:

  • Our row headers are set using static HTML. These correspond with the fields we added to our block.
  • We used the repeater field functions to loop through our repeater rows. Docs here on those functions.
  • Within our repeater loop, we check if the featured field is set to true and if so, update the $featured_class to featured and then echo this in the HTML. This lets us uniquely style any column with this field set to true (checked).

With this done, we can style away. Keep in mind that every theme is unique (we’re using the twentytwenty theme), so CSS is rarely copy-paste. Seeing what we’ve done is always helpful though as it can give you good ideas for how you can implement for yourself.

In our block.css file we have:

.block-comparison-table {
    color: #29275F;
    display: flex;
    font-size: 14px;
    background-color: #fff;
    box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.05);
.block-comparison-table>div {
    flex-grow: 1;
.block-comparison-table__column {
    display: grid;
    grid-auto-rows: 50px;
    border-top: 2px solid #fff;
    border-bottom: 2px solid #fff;
    background-color: #fff;
.block-comparison-table__column--row-label {
    font-weight: 600;
.block-comparison-table__column>div {
    display: flex;
    align-items: center;
    justify-content: center;
    border-bottom: 1px solid #eee;
    border-right: 1px solid #eee;
    padding: 0 12px;

.block-comparison-table__column>div:last-of-type {
    border-bottom: none;

.block-comparison-table__column:first-child div {
    justify-content: flex-start;

.block-comparison-table__header {
    font-weight: 700;
    font-size: 18px;

.unchecked {
    position: relative;
    display: grid;
    width: 18px;
    height: 18px;
    background-color: #ddd;
    border-radius: 50%;

.checked {
    background-color: #7bbb5e;
.checked:after {
    display: block;
    content: "";
    position: absolute;
    width: 6px;
    height: 10px;
    border-right: 2px solid rgba(250,250,250,0.8);
    border-bottom: 2px solid rgba(250,250,250,0.8);
    transform: rotate(45deg);
    left: 6px;
    top: 3px;

.featured {
    border: 4px solid #F64B53;
    border-radius: 10px;
    padding-top: 10px;
    padding-bottom: 10px;
    margin-top: -12px;
    margin-bottom: -12px;

.host-link {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 80%;
    height: 40px;
    background-color: #4C56BA;
    color: #fff;
    text-decoration: none;
    border-radius: 8px;
    margin: 12px auto;

.featured .host-link{
    background-color: #F64B53;

Things to note:

  • We use flexbox to handle our horizontal layout and positioning of columns
  • We use the pseudo-classes of :first-child and :last-child to handle a few cases where we want the peripheral columns and rows to be styled differently
  • We use the class checked and the pseudo :after to create our little green checkmark
  • There’s a bunch of general border, font, and colour styling
  • We do some unique styling for our featured class

With this saved, the block is all done and ready to use!

On a new page, we can add the block and fill out the block form:

This is what it looks like on the frontend:

Very nice!

3. Further work

To improve this block even further, you could:

  • Add some further styling to improve how it renders in the editor preview. Docs here on how to write editor/preview specific styling
  • Refine the use of flexbox to handle different screen sizes better

That’s all. I hope you found this one useful. Comparison tables like these are a great example of a design that can be very unique to a particular brand or site, so I can imagine being able to build a custom one is very useful.

Until next time, happy block building.

New Kid on the block?

Subscribe, stay connected, and keep up to date with the latest.

  • This field is for validation purposes and should be left unchanged.

Leave a Reply

Your email address will not be published. Required fields are marked *