How to Create a Custom Element for Visual Composer

Visual Composer website builder is packed with many built-in elements to design the frontend more quickly. Today we will learn how to create a custom element for visual composer.

We are going to build a simple visual composer element to display the latest posts at the frontend. We required following fields to generate the output.

  • The Number of Posts to display
  • Sorting Order By Date / Title
  • Collect Post by Category
  • The text length for the excerpt text

Table of content

We need to start the implementation with a good structure to make the files well organized. We are creating a vc-elements folder for our selected theme directory. And create a file latest_posts.php under the vc-elements directory.

Register Before Initialization

The next step is to register the latest_posts.php file before visual composer initialization. We are going to use the vc_before_init action hook to do so.

add_action( 'vc_before_init', 'wp23598_vc_before_init_actions' );
 
function wp23598_vc_before_init_actions() {
    // Require New Custom Element
    require_once( get_template_directory().'/vc-elements/latest_posts.php' );
}

Initializing New Element

The next step to open the latest_posts.php file and start writing the codes for the new element.

class LatestPosts
{
    public function __construct()
    {
        add_action( 'init', array( $this, 'vcMap' ) );
        add_shortcode( 'vc_latest_posts', array( $this, 'render' ) );
    }

    public function vcMap()
    {
        ....
        ....
    }

    public function render($atts, $content = null)
    {
        ....
        ....
    }
}

We are extending the visual composer with the vcMap() method that generates the output like the screenshot.

public function vcMap()
{
    vc_map(array(
        "name" => "Latest Posts",
        "base" => "vc_latest_posts",
        "category" => ' Webomnizz Elements',
        "allowed_container_element" => 'vc_row',
        "params" => array(
            array(
                "type" => "textfield",
                "heading" => "Number of Posts",
                "param_name" => "number_of_posts",
                "admin_label" => true
            ),
            array(
                "type" => "dropdown",
                "heading" => "Order By",
                "param_name" => "order_by",
                "value" => array(
                    "Title" => "title",
                    "Date" => "date"
                ),
                'save_always' => true,
                "admin_label" => true
            ),
            array(
                "type" => "dropdown",
                "heading" => "Order",
                "param_name" => "order",
                "value" => array(
                    "ASC" => "ASC",
                    "DESC" => "DESC"
                ),
                'save_always' => true,
                "admin_label" => true
            ),
            array(
                "type" => "textfield",
                "heading" => "Category Slug",
                "param_name" => "category",
                "description" => "Leave empty for all or use comma for list",
                "admin_label" => true
            ),
            array(
                "type" => "textfield",
                "heading" => "Text length",
                "param_name" => "text_length",
                "description" => "Number of characters"
            ),
        )
    ));
}

The render() method is basically for the frontend section that takes care of the User Interface.

public function render($atts, $content = null)
{
    $args = array(
        "number_of_posts"       => "",
        "order_by"              => "",
        "order"                 => "",
        "category"              => "",
        "text_length"           => "",
    );

    $params	= shortcode_atts($args, $atts);

    $query = new \WP_Query(
        array('orderby' => $params['order_by'], 'order' => $params['order'], 'posts_per_page' => $params['number_of_posts'], 'category_name' => $params['category'])
    );

    $html = '<div class="lp-holder">';
    $html .= '<ul>';
    
    while ($query->have_posts()) : $query->the_post();
        $html .= '<li>
            <div class="latest_post">
                <h2><a href="'.get_permalink().'">'.get_the_title().'</a></h2>
                '.wp_kses_post($this->getExcerpt(get_the_ID(), $params['text_length'])).'
            </div>
        </li>';
    endwhile;

    $html .= '</ul>
    </div>';

    return $html;
}

We have created the getExcerpt() method to generate the excerpt string as per the text length option.

This is how our latest_posts.php file looks.

<?php
class LatestPosts
{
    public function __construct()
    {
        add_action( 'init', array( $this, 'vcMap' ) );
        add_shortcode( 'vc_latest_posts', array( $this, 'render' ) );
    }

    public function vcMap()
    {
        vc_map(array(
            "name" => "Latest Posts",
            "base" => 'vc_latest_posts',
            "category" => 'Webomnizz Elements',
            "allowed_container_element" => 'vc_row',
            "params" => array(
                array(
                    "type" => "textfield",
                    "heading" => "Number of Posts",
                    "param_name" => "number_of_posts",
                    "admin_label" => true
                ),
                array(
                    "type" => "dropdown",
                    "heading" => "Order By",
                    "param_name" => "order_by",
                    "value" => array(
                        "Title" => "title",
                        "Date" => "date"
                    ),
                    'save_always' => true,
                    "admin_label" => true
                ),
                array(
                    "type" => "dropdown",
                    "heading" => "Order",
                    "param_name" => "order",
                    "value" => array(
                        "ASC" => "ASC",
                        "DESC" => "DESC"
                    ),
                    'save_always' => true,
                    "admin_label" => true
                ),
                array(
                    "type" => "textfield",
                    "heading" => "Category Slug",
                    "param_name" => "category",
                    "description" => "Leave empty for all or use comma for list",
                    "admin_label" => true
                ),
                array(
                    "type" => "textfield",
                    "heading" => "Text length",
                    "param_name" => "text_length",
                    "description" => "Number of characters"
                ),
            )
        ));
    }

    public function render($atts, $content = null)
    {
        $args = array(
            "number_of_posts"       => "",
			"order_by"              => "",
			"order"                 => "",
			"category"              => "",
			"text_length"           => "",
        );

        $params	= shortcode_atts($args, $atts);

        $query = new \WP_Query(
            array('orderby' => $params['order_by'], 'order' => $params['order'], 'posts_per_page' => $params['number_of_posts'], 'category_name' => $params['category'])
        );

        $html = '<div class="lp-holder">';
        $html .= '<ul>';
        
        while ($query->have_posts()) : $query->the_post();
            $html .= '<li>
                <div class="latest_post">
                    <h2><a href="'.get_permalink().'">'.get_the_title().'</a></h2>
                    '.wp_kses_post($this->getExcerpt(get_the_ID(), $params['text_length'])).'
                </div>
            </li>';
        endwhile;

        $html .= '</ul>
        </div>';

        return $html;
    }

    public function getExcerpt($postId, $text_length = 100)
    {
        $excerpt = '';

		if($text_length !== '0') {
			$excerpt .= '<p class="excerpt">';
			$excerpt .= $text_length > 0 ? mb_substr(get_the_excerpt(), 0, intval($text_length)) : get_the_excerpt($postId);
			$excerpt .= '...</p>';
		}
		
		return $excerpt;
    }
}

Now the final step is to load our latest_posts.php file. Open the functions.php file and the following at the very bottom of the file.

include_once get_template_directory() . '/vc-elements/latest_posts.php';

Once you finished the editing to the functions.php file then hit save. Now open/edit any page and you will see a new tab with the custom element on the visual composer page builder section.

Posted by Jogesh Sharma

Jogesh Sharma is a web developer and blogger who loves all the things design and the technology, He love all the things having to do with PHP, WordPress, Joomla, Magento, Durpal, Codeigniter, jQuery, HTML5 etc. He is the author of this blog.