Skip to main content

Displaying product variations - Meta Box + Oxygen

Let’s display product variations that are stored in custom fields to the product page. We’ll use Oxygen to build the page.

Example of product variations

Video version

(Coming soon)


For this practice, we need these tools:

The last one is Oxygen. You should use the 3.9 version or higher, which already has integration with Meta Box.

1. Creating a new custom post type

Go to Meta Box > Post Types > New Post Type.

Create a new custom post type

2. Creating custom fields

The product normally has a lot of information that we need to save in custom fields. Go to Meta Box > Custom Fields > Add New to create them.

create custom fields

In this tutorial, just take a typical example of a fashion product and look at the below structure for fields:

Field Types of Field ID
Variations of ProductGroupvariations_of_product
Product ImageImage Advancedproduct_image
SizeCheckbox Listsize
Original PriceTextoriginal_price
Promotional PriceTextpromotional_price
Color NameSelectcolor_name

Since the product may have more than one variation, I set this group to be cloneable.

Set the Variations of Product group as cloneable to have more spaces to add variations.

In the Settings tab of the field group, remember to set location for it to be your product’s post type.

Back in the post editor of your product, you’ll see the created fields.

The created custom fields are displayed in post editor.

Just enter information for the product.

3. Displaying the variations information

Go to the Oxygen menu and create a new template.

In the settings of the template, choose a design that you want the template inherit from, then choose your post type in the Singular section to apply the template to it.

Choose the design and post type for the template.

Back to the post editor of your post type, you will see the section with Edit with Oxygen button. Click it to edit the template.

Click Edit with Oxygen to edit the template.

Now, add a Section component for having a container.

Add a Section component

Then, add the Code Block inside and choose the PHP & HTML section to add code.

Go to PHP & HTML in Code Block to add code.

Add code to the Code Block

This code was uploaded to Github, you can refer to it for more details.

There are some points in the code we should clarify:

First, our group is cloneable, so we must create a loop to get information. Usually, you can create a loop only, then display all the fields inside the loop. But, for styling later and setting display rules for the variation easier, we should create a loop for each information separately.

To get product images, I use this code:

<?php foreach ( $variations_of_product as $gallery ) : ?>
<?php $variation_image_ids = $gallery['product_image'];?>
<?php foreach ( $variation_image_ids as $large_image ) : ?>
<?php $large = RWMB_Image_Field::file_info( $large_image, ['size' => 'large'] ); ?>
<img src="<?php echo $large['full_url'] ?>">
<?php endforeach; ?>
<?php foreach ( $variation_image_ids as $thumbnail_image ) : ?>
<?php $thumbnail = RWMB_Image_Field::file_info( $thumbnail_image, ['size' => 'thumbnail'] ); ?>
<img src="<?php echo $thumbnail['full_url'] ?>">
<?php endforeach; ?>
<?php endforeach; ?>

In there:

  • <?php $variation_image_ids = $gallery['product_image'];?>: is to get value from the field that has ID product_image;
  • <?php foreach ( $variation_image_ids as $large_image ): ?>: a loop to display all the images saved in the field;
  • RWMB_Image_Field::file_info(): is to get images url.

I displayed all the images twice, one in large size, and one in thumbnail size. They’ll be used to set in the slider later.

For the price, there are Promotional Price and Original Price, but promotional price is not always available. So, I set a rule to display them as below:

<?php $j = 0; ?>
<?php foreach ( $variations_of_product as $price ) : ?>
<?php $j ++; ?>
<?php if ($price['promotional_price']): ?>
<?php echo $price['promotional_price'] ?>
<?php echo $price['original_price'] ?>
<?php else: ?>
<?php echo $price['original_price'] ?>
<?php endif; ?>
<?php endforeach; ?>

For the Size, it is a Checkbox List field with several options and allows choosing more than one option. So that we need a loop to get all the saved options. In addition, each option always has both the value and label. We use this syntax to get the values of options:

<?php $k = 0; ?>
<?php foreach ( $variations_of_product as $size_group ) : ?>
<?php $k ++; ?>
<?php $o = 0; ?>
<?php $values = $size_group['size']; ?>
<?php foreach ( $values as $value ) : ?>
<?php $o ++; ?>
<?php echo $value; ?>
<?php endforeach;?>
<?php endforeach; ?>

The Status field is a Select field (which has options as well as the checkbox list), but instead of getting the value of options as the Size, we should get the label. Then, the code will be much different from the Size’s one.

foreach ( $variations_of_product as $key => $variation ) {
$class = $key === 0 ? ' active' : '';
$status = isset( $variation['status'] ) ? $variation['status'] : '';
$group = rwmb_get_field_settings( 'variations_of_product' );
foreach ( $group['fields'] as $field ) {
if ( empty( $field['options'] ) ) {
<?php if($field['options'][$status]): ?>
<?= $field['options'][$status]; ?>
<?php endif; ?>

Furthermore, variations are differentiated by color, so we have this code to get colors:

<?php foreach ( $variations_of_product as $color ) : ?>
<?php $variation_color = $color['color_name']; ?>
<a href="#<?php echo $variation_color ?>" class="color <?php echo $variation_color ?>" title="<?php echo $variation_color ?>"><?php echo $variation_color ?></a>
<?php endforeach; ?>

In there, I added an a tag and a dynamic class. There will be different classes for each color and each one will be the name of the color. This class plays an important role in helping us to define which variations are showing.

Then, I added some div tags to the code to set elements in a reasonable layout. I also added an attribute named data-id in the gallery, price, size, and status sections.

data-id="<?php echo $price['color_name'] ?>"

This attribute will obtain the name of the corresponding color, then we’ll know which images, prices, status, or sizes are of which variation. So that we can easily choose which information should be shown to fit the chosen color.

Apply the code then back to a single product page, you’ll see all the information of product display.

Apply the code then back to a single product page

4. Setting rules to display the variations

Information of each variation is not always displayed. They will be shown only when the matching color displays. To set that rule and have a slider for the image gallery, we need some JS code. As well as, we should use some CSS to have a beautiful display.

Please notice that you can use a 3rd party plugin to add JS and CSS instead of inserting them directly into the theme file.

Downloading the js and css library

For the slider and the rule, I use the Slick library. It is also available on Github. We just need three files here.

JS and CSS files in the Slick library.

Creating custom js for slider and rules

Next, create a new custom.js file in the js folder and add code to it.

Add code to custom.js file.

jQuery(document).ready(function ($) {
slidesToShow: 1,
slidesToScroll: 1,
arrows: false,
adaptiveHeight: false,
infinite: false,
useTransform: true,
speed: 400,
cssEase: 'cubic-bezier(0.77, 0, 0.18, 1)',


.on('init', function (event, slick) {
$('.slider-nav .slick-slide.slick-current').addClass('is-active');
slidesToShow: 4,
slidesToScroll: 4,
dots: false,
focusOnSelect: false,
infinite: false,
responsive: [{
breakpoint: 1024,
settings: {
slidesToShow: 5,
slidesToScroll: 5,
}, {
breakpoint: 640,
settings: {
slidesToShow: 4,
slidesToScroll: 4,
}, {
breakpoint: 420,
settings: {
slidesToShow: 3,
slidesToScroll: 3,

$('.slider-single').on('afterChange', function (event, slick, currentSlide) {
$('.slider-nav').slick('slickGoTo', currentSlide);
var currrentNavSlideElem = '.slider-nav .slick-slide[data-slick-index="' + currentSlide + '"]';

$('.slider-nav').on('click', '.slick-slide', function (event) {
var goToSingleSlide = $(this).data('slick-index');

$('.slider-single').slick('slickGoTo', goToSingleSlide);
jQuery(".grouped-product .color-contain-group .color-group .color-name a").click(function (e) {
jQuery(".color-contain-group .color-group .color-name").removeClass("active");
jQuery("div[data-id='" + jQuery(this).attr("href").replace("#", "") + "']").addClass("active");

jQuery(".size-contain-group .size-group .info .list-size a").click(function (e) {

jQuery('.size-contain-group .size-group .info .list-size .size-name').click(function(){
jQuery('.size-contain-group .size-group .info .list-size .size-name').not(this).removeClass('active')


  • $('.slider-single').slick({ }) to create a slider for the elements that have the .slider-single class. They are product images which I set to display in the large size.
  • $('.slider-nav'): to create a slider as well. The elements which have the .slider-nav class are product images which I set to display in the thumbnail size.
  • .on('init', function (event, slick) { }): to identify which thumbnail is in the current slide. And, that thumbnail will be added a class as “is active”.
  • $('.slider-single').on('afterChange', function (event, slick, currentSlide) { }): to trigger the event that someone clicks on the large image to move to the other one, then the thumbnail slider will be changed to the corresponding thumbnail.
  • $('.slider-nav').on('click', '.slick-slide', function (event) { }): to trigger that event when someone clicks on the thumbnail slider. Then, it also displays the corresponding large image in the large slider.
  • jQuery(".grouped-product .color-contain-group .color-group .color-name a").click(function (e) { }): to trigger when someone clicks on a product color using the A tag we added in the view.
  • jQuery(".color-contain-group .color-group .color-name").removeClass("active"); jQuery(this).show(); jQuery(this).parent().addClass("active"): to remove the active class from the unselected color and add it to the selected one.
  • jQuery("div[data-id]").removeClass("active"); jQuery("div[data-id='" + jQuery(this).attr("href").replace("#", "") + "']").addClass("active"): to remove and add the active class to all the elements that have value of the data-id attribute as the name of the color. It means that when you click on a color, all the corresponding information of that variation such as price, size, status, and image gallery will be displayed.
  • jQuery('.slider-single').slick('refresh') and jQuery('.slider-nav').slick('refresh'): to refresh both sliders to load new images.

Declaring the js and css files

Add code inside the function custom_enqueue_files() in the plugin.php file in the case you use the 3rd party plugin. Otherwise, add to the functions.php to declare all the above js and css files.

            wp_enqueue_style('slick', plugin_dir_url( __FILE__ ).'/assets/css/slick.css');
wp_enqueue_style('slick-theme', plugin_dir_url( __FILE__ ).'/assets/css/slick-theme.css');

wp_enqueue_script('custom', plugin_dir_url( __FILE__ ).'/assets/js/custom.js', ['jquery']);
wp_enqueue_script('slick-min', plugin_dir_url( __FILE__ ).'/assets/js/slick.min.js', ['jquery']);
wp_enqueue_script('script', plugin_dir_url( __FILE__ ).'/assets/js/script.js', ['jquery']);

Go back to a single product page, there is a slider and some differences.

The product images have already turned into a slider.

5. Styling the product page

Back to page editor by Oxygen, go to Manage > Stylesheets > Add Stylesheet to have space to add CSS.

Go to Manage > Stylesheets > Add Stylesheet to have space to add CSS

Then add code into the box. All the code is uploaded into Github, so you can refer to it.

Add CSS to the box

Now, on the single product page, you’ll see a final result. When you select a color, the photo gallery will change to that color automatically. At the same time, the sizes and prices also change correspondingly.

The product variations turned into a new look.