Creating Intro.js Powered Tours in WordPress

By Rakhitha Nimesh

In a previous article, An Introduction to Product Tours with Intro.js, we discussed the importance of creating product tours and how we can use Intro.js to create awesome product tours. Unfortunately, we configured each step manually – a methodology that is not suitable for users with limited programming knowledge. However, the real power of Intro.js comes from its ability to create product tours on dynamic content without much programming. This tutorial will teach you how to integrate Intro.js into WordPress.

Building an Intro.js WordPress Plugin

Plugins are the recommended components for adding custom behavior into WordPress. We will be using a plugin to integrate Intro.js into WordPress. Begin by creating a new folder within the wp-content/plugins folder and name it wpintrojs. Next, create the main plugin file, index.php, and use the following code to define the plugin.

Plugin Name: Introjs Tours
Plugin URI: 
Description: Integrating intro.js powered product tours into WordPress
Author: Your Name
Version: 1.0
Author URI: http://www.innovativephp.com/

Next, login as admin and activate the plugin in the Plugins section.

Creating Product Tour Steps

In the previous tutorial, we configured each step manually by assigning the necessary data-attributes and values. Here, we need a simpler way to configure tour steps, as the content is generated dynamically within WordPress. So, it’s essential to build a plugin specific page to configure the steps. Let’s start by adding new a plugin specific page using the following code.

add_action( 'admin_menu', 'wpintro_menu_page' );

function wpintro_menu_page(){
  add_menu_page( 'Product Tours', 'Product Tours', 'manage_options', 'wpintro_tour', 'wpintro_menu_page_display', plugins_url( 'myplugin/images/icon.png' ), 6 ); 
  add_submenu_page( 'wpintro_tour', 'Manage Steps', 'Manage Steps', 'manage_options', 'wpintro_tour_steps', 'wpintro_manage_steps_display' ); 

In this code, we have a main admin page as well as a sub menu page. The main menu page is used to configure the steps, while the sub menu page is used to configure the order of the steps within the tour. First, we are going to look at the product step creation page by implementing the wpintro_menu_page_display function used in the previous code block. This function is shown below.

function wpintro_menu_page_display() {
  global $wip_message;
  $pages = get_pages();
  $html = '';

  if ($wip_message != '') {
    $html .= '<div style="background:#9FD09F;padding:5px;">' . $wip_message . '</div>';

  $html .= '<h2>Create Product Tour</h2>';
  $html .= '<form action="" method="post">';
  $html .= '<table class="form-table"><tbody>';
  $html .= '  <tr valign="top">
  <th scope="row"><label>Select Page</label></th>
  <td><select class="regular-text" id="wpi_page" name="wpi_page">';

  foreach ($pages as $page) {
    $html .= '<option value="' . get_page_link($page->ID) . '">';
    $html .= $page->post_title;
    $html .= '</option>';

  $html .= '      </select></td>
  $html .= '  <tr valign="top">
  <th scope="row"><label>Intro Text</label></th>
  <td><textarea class="regular-text" id="wpi_intro_text" name="wpi_intro_text"></textarea></td>
  $html .= '  <tr valign="top">
  <th scope="row"><label>Element ID</label></th>
  <td><input type="text" class="regular-text"  id="wpi_element_id" name="wpi_element_id"/></td>
  $html .= '  <tr valign="top">
  <th scope="row"><label></label></th>
  <td><input type="hidden" class="regular-text"  id="wpi_action" name="wpi_action" value="save_steps" />
  <input type="submit" class="regular-text"  id="wpi_submit" name="wpi_submit" value="Save" /></td>
  $html .= '</tbody></table></form>';
  echo $html;

This function is used to display the contents of the menu page. The page consists of a heading and a HTML form for generating steps. The form contains three fields for adding new steps.

  • Element ID – This is used to define the ID of the HTML element used for the step.
  • Select Page – This is used to select the page containing the assigned Element ID.
  • Intro Text – This is used to define the description to be displayed with each step in product tour.

Once implemented, the step creation screen will resemble the following image.

Creating Product Steps

You can use the Product Tour page to add any number of steps to the tour. Next, let’s move into the data saving process.

add_action('init', 'wpintro_save_steps');

function wpintro_save_steps() {
  global $wip_message;

  if (isset($_POST['wpi_action']) && $_POST['wpi_action'] == 'save_steps') {
    $intro_text = $_POST['wpi_intro_text'];
    $page = $_POST['wpi_page'];
    $element_id = $_POST['wpi_element_id'];
    $step_data = get_option('wpi_step_data', '');

    if ($step_data != '') {
      $step_data = unserialize($step_data);
      $step_data["wip" . rand(1000, 1000000)] = array("desc" => $intro_text, "url" => $page, "id" => $element_id);
    } else {
      $step_data = array("wip" . rand(1000, 1000000) => array("desc" => $intro_text, "url" => $page, "id" => $element_id));

    $step_data = serialize($step_data);
    update_option('wpi_step_data', $step_data);
    $wip_message = "Step saved successfully";
  } else {
    $wip_message = "";

Here, we have used the init action to define a function called wpintro_save_steps. Generally, the init action is used to intercept user submitted data through $_GET or $_POST. We used a hidden field inside the form to define an action for the saving process. We have to check the action as this function can intercept user submitted data though other forms. Then, we assign the $_POST data into variables.

Next, we check the existing step data from the database using the wpi_step_data key in the wp_options table. Since we are saving the first step, the wpi_step_data key will not be available in the database. Therefore, we assign the values into an array with random unique keys, and save to database as a serialized value. For the remaining steps, we can unserialize the existing values, add the new values, and save the values again as serialized value. Finally, we display a success message to the user.

Up until this point, we looked at how to create and save product steps into database. Next, we are going to look at the sub menu page for ordering steps.

Configuring the Order of Steps

We can add any number of steps using the previous form, and they will be saved in the order of creation by default. Unless you have a very good plan for the product tour, it’s difficult to create the steps in the correct order. And, in some cases you may need to add new steps to the middle of the tour. In such cases, it’s important to have the ability to configure the ordering of the steps. Drag and drop is the easiest way of enabling step reordering. We can use jQuery UI’s sortable functionality to create the sort order for the steps.

Let’s display all the available steps for sorting, within the sub menu page created earlier.

function wpintro_manage_steps_display() {
  $step_data = get_option('wpi_step_data', '');

  if ($step_data != '') {
    $step_data = unserialize($step_data);

  echo "<div id='sortable'>";

  foreach ($step_data as $key => $data) {
    echo "<div class='wip_sort' style='margin:5px; border:2px solid; background: #fff;' data-identifier=" . $key . "  >
  <li>" . $data['url'] . "</li>
  <li>" . $data['desc'] . "</li>
  <li>" . $data['id'] . "</li>

  echo "</div>";

The wpi_step_data option is used to retrieve the existing steps from the database. After unserializing the data, we use div containers to display the steps inside a container with the ID sortable. Your screen should now look similar to the following image.

Ordering Product Steps

Next, we have to integrate jQuery UI for enabling drag and drop. Let’s move to the code block for inserting the necessary JavaScript files.

function wpintro_admin_scripts() {
  wp_register_script('wpintro_admin', plugin_dir_url(__FILE__) . 'admin.js', array('jquery'));

  $config_array = array(
    'ajaxURL' => admin_url('admin-ajax.php'),

  wp_localize_script('wpintro_admin', 'conf', $config_array);

add_action('admin_enqueue_scripts', 'wpintro_admin_scripts');

It is good practise to use the admin_enqueue_scripts action hook for assigning scripts and styles required for the admin section. Here, we have used wp_enqueue_script for the jQuery UI core and sortable source files, as they are available by default inside WordPress core. We have to use both the wp_register_script and wp_enqueue_script functions, as admin.js is a custom JavaScript file. Finally, we use the wp_localize_script function for adding necessary configuration data to admin.js.

Let’s look at the admin.js file (shown below) for handling sort order of steps.

jQuery(document).ready(function() {
    update: function() {
      var selected_ids = "";

      jQuery(".wip_sort").each(function() {
        selected_ids += jQuery(this).attr("data-identifier")+"@";

      jQuery.post(conf.ajaxURL, { 
        action: "wpintro_update_step_order", 
        data: selected_ids,
      }, function(data) { }, "json");


We enable the sortable functionality on the #sortable container. All the main div elements inside the #sortable container will get the drag and drop features. We can move the div elements to setup the correct order of steps. jQuery UI will execute the update function each time we move the elements by drag and drop.

Once the update function is executed, we can get the element order using the random ID’s we assigned earlier. Then, we make an AJAX request to the server with the ID’s. On the server side, we have to rearrange the step data using the order of ID’s received from the AJAX request.

add_action( 'wp_ajax_nopriv_wpintro_update_step_order', 'wpintro_update_step_order');
add_action('wp_ajax_wpintro_update_step_order', 'wpintro_update_step_order');

function wpintro_update_step_order() {
  $step_data = get_option('wpi_step_data', '');
  $step_data = unserialize($step_data);
  $updates_step_order = array();
  $step_order = explode('@', $_POST['data']);

  for ($i = 0; $i < count($step_order); $i++) {
    $updates_step_order[$step_order[$i]] = $step_data[$step_order[$i]];

  $step_data = serialize($updates_step_order);
  update_option('wpi_step_data', $step_data);
  echo json_encode($updates_step_order);

The wpintro_update_step_order function is used to handle AJAX requests on the server side. First, we retrieve the existing step data from the database. Then, we loop through the step ID’s and rearrange the step data according to the updated order. Finally, we save the updated step data into the database.

We have completed the backend functionality of the product tour. At this point, we have all the product step data for implementing the tour. In the next section, we are going to look at the implementation of the product tour on the frontend.

Integrating Step Data with Intro.js

It’s time to build the product tour by using existing step data with Intro.js. First, we must include the JavaScript and CSS files with the configuration data. I assume you have already read the previous tutorial and downloaded the Intro.js related files. Copy the intro.js and introjs.css files into the wpintrojs folder. The following code shows the inclusion of the script files on the frontend.

add_action('wp_enqueue_scripts', 'wpintro_enqueue_script');

function wpintro_enqueue_script() {
  wp_register_script('introjs', plugin_dir_url(__FILE__) . 'intro.js');
  wp_register_style('introjscss', plugin_dir_url(__FILE__) . 'introjs.css');
  wp_register_script('wpintro_custom', plugin_dir_url(__FILE__) . 'custom.js', array('jquery'));
  $step_data = get_option('wpi_step_data', '');
  $step_data = unserialize($step_data);
  $step_data = array_values($step_data);
  $stepdata_array = array(
    'steps' => json_encode($step_data),
  wp_localize_script('wpintro_custom', 'stepData', $stepdata_array);

We used admin_enqueue_scripts for the backend script inclusion. wp_enqueue_scripts is the recommended action for including scripts on the frontend. In the last section, we get the step data from the database and assign it to a new JavaScript file called custom.js as a JSON encoded string. Finally, we have to create the custom.js file and include the following code to initialize the product tour.

jQuery(document).ready(function() {
  var currentstep = 0;

    currentstep = QueryString.currentstep;

  var steps_json = stepData.steps;
  var wip_index=0;

  steps_json = jQuery.parseJSON(steps_json);

  jQuery.each(steps_json, function(k, v) {
    jQuery("#"+v.id).attr("data-step", wip_index).attr("data-intro", v.desc).attr("data-url", v.url);

  jQuery("#flexi_form_start").click(function() {
    introJs().setOption("doneLabel", "Next page").start().oncomplete(function() {
      window.location.href = steps_json[currentstep].url + "?multipage=true&currentstep=" + currentstep;
    }).onbeforechange(function(targetElement) {
      currentstep = jQuery(targetElement).attr("data-step");    

First, we set the current step to zero. We use a custom function called QueryString to retrieve the query parameters from the URL. You can find the implementation of the QueryString function inside the source codes folder.

Then, we pass the step data through the jQuery.parseJSON function to create a JavaScript object. Next, we have to add the Intro.js specific data attribues to the step elements. While looping through step data, we find the element with the given ID and assign the data attributes using the values in the steps_json object.

Finally, we initialize Intro.js on button click as we did in the previous tutorial. Here, it’s slightly different from the previous tutorial as we are using the product tour across multiple pages instead of a standalone page. Hence, we set the URL of the page with the Intro.js multipage attribute after the completion of the first page. Also, we change the value of currentstep on each step change.

Once the user completes the product tour steps on the first page, he/she will be redirected to the next page with the multipage parameter. Let’s see how to handle the steps of subsequent pages using the following code.

if (RegExp("multipage", "gi").test(window.location.search)) {
  var introObj =  introJs();

  introObj.setOption("doneLabel", "Next page").start().oncomplete(function() {
    if (steps_json[currentstep]) {
      window.location.href = steps_json[currentstep].url + "?multipage=true&currentstep=" + currentstep;
  }).onbeforechange(function(targetElement) {
    currentstep = jQuery(targetElement).attr("data-step");

    if (steps_json.length <= (parseInt(currentstep) + 1)) {
      introObj.setOption("doneLabel", "Done");

We have to use regular expressions to filter the multipage attribute as Intro.js depends on this parameter for enabling multipage support. Inside the if statement, we have to use the same code as we did previously to enable recursive page support. We are using the completion button as Next Page. We have to check for the last step of the tour and set the label as Done using the last section of code.

Now it’s time to see the product tour in action. Follow the steps given here to generate the product tour and see it in action.

  1. Create a few empty pages and assign content.
  2. On each page create few HTML elements and add ID’s.
  3. Navigate to the product tour section of the plugin and select the previously created page.
  4. Repeat the previous steps for all the steps in all the created pages.
  5. Finally, add the following code to one of the pages and click the link to start the tour
<a id="flexi_form_start">Start Tour</a>

Extending the Plugin

The goal of this tutorial was to introduce you to dynamic implementations of product tours with Intro.js. At this stage, the functionality of this plugin is very basic to match the scope of this tutorial. You can extend the plugin using the following suggestions to make it more effective on your WordPress site.

  • Validate step creation with mandatory fields.
  • Add step removing capabilities to the step ordering section.
  • Enable the ability to create multiple product tours instead of standalone product tour.
  • Enable other configuration options of Intro.js like positions, labels, exit functions, etc.


This tutorial has introduced you to dynamic product tours with Intro.js. So, we integrated Intro.js into WordPress for simplifying the dynamic product tour creation process. You can use the following links to download and view the demo.

Download Source Codes
Preview the Demo

Let us know your experiences of using Intro.js as well as suggestions for improving the features of this plugin.

  • afshin

    Great! thanks for such a useful tutorial

  • http://www.innovativephp.com/ Rakhitha Nimesh

    You are welcome and thanks for developing intro.js.

  • http://coreypein.net/ Corey Pein

    Great tutorial

Get the latest in WordPress, once a week, for free.