Adding WordPress Admin Tables to Plugins with WP_List_Table

Adding WordPress Admin Tables to Plugins with WP_List_Table

WordPress administration tables, or list tables, are used extensively in admin areas to list posts, pages, users, etc.

If you’ve ever wanted to add such list tables with your own data to your plugin, WordPress provides a nifty way to do so with the WP_List_Table class.

In this article, I’ll show you how to use the WordPress API to add WordPress-like administration tables or list tables to your plugin’s admin screen.

The WordPress list tables are used almost everywhere in the dashboard. You’ll find them used to display posts, pages, users, comments, etc. as well as in many popular plugins. Over the years, they’ve become the de facto standard for displaying tabular information in WordPress.

Note: This article is for intermediate-advanced WordPress developers. You will require a working knowledge of WordPress, PHP and the WordPress Plugin API before going ahead. If you’re unsure or would like a refresher I recommend you read through the following:

For this article, I’ve built a custom Admin Table with the help of a plugin that uses object-oriented constructs. The plugin can be downloaded from here to follow along with the article. But before we get into that, let’s understand what Admin Tables really do.

I’ll also be using the terms Admin Tables and List Tables interchangeably for the rest of the article.

WordPress Admin Tables – The Background

You’ve already interacted with an Admin Table while accessing pages like Posts and Users in the Dashboard. A WordPress Administration Table is implemented using the WP_List_Table class located at /wp-admin/includes/class-wp-list-table.php. It provides the necessary framework to display information in a tabular fashion as well as manipulate data intuitively. Here’s an overview of what a typical WordPress Admin Table entails:

wordpress admin table
A WordPress Admin Table

You’ll notice the familiar bulk actions, row actions with hover links, pagination and screen options, all of which have been an integral part of WordPress ever since.

While Screen Options are not directly part of WP_List_Table, they work very closely with it. They allow control over the visibility of columns as well as the pagination by limiting the information displayed on the page.

When to Use the WordPress Admin Table
Just because the WordPress API provides a way to build native List Tables doesn’t mean one must use it. In my opinion, a big factor in deciding to use the WordPress provided Admin Tables should be User Experience. With the WP_List_Table class, WordPress takes care of styling the table UI for you. The familiarity of working with Admin Tables (all over WordPress) and the native look-and-feel are elements that will certainly help users interact with your table data in a seamless manner.

Anatomy of the WP_List_Table Class

Since its inception in version 3.1.0 of WordPress, the WP_List_Table class has increasingly grown in popularity among developers. The WordPress Codex class reference for the WP_List_Table provides comprehensive information about its methods, properties and usage.

The infographic below outlines the building blocks of an Admin Table from the class reference.

anatomy-wp-list-table-class
Building blocks of the WP_List_Table

A more complex Admin Table would also feature additional drop-down filters and action links above the table such as those used to show “Published Posts” and “Drafts”.

Now that we have a fair understanding of an Admin Table and the WP_List_Table class, let’s put this knowledge to use with the help of a custom plugin.

The Custom List Table Plugin for User Meta Operations

Before I get into the implementation details of List Tables, I want to briefly touch upon the custom plugin that I’ve prepared for this article. You can download it from here to follow along. I recommend that you have it opened in a suitable editor and install it on a local development WordPress setup only.

The plugin is based on my own plugin template which is a fork of the original WordPress Plugin Boilerplate project. It’s similar to the original project in many aspects but also has support for namespaces and autoloading. This way, I don’t need to have unique prefixes for every class or function, and don’t end up with a lot of include and require statements. However, the minimum required PHP version for my plugin is 5.6.0.

We’ll mostly be working with files in the following directories of the plugin:

  • inc/core/* – for core functionality of the plugin
  • inc/admin/* – for displaying the List Table in the admin area
  • inc/libraries/* – libraries for the plugin

Feel free to structure the plugin based on your own coding and layout preferences. I prefer to use Boilerplate Starting Points as they’re among the many best practices listed in the WordPress Plugin Handbook.

Purpose
The custom plugin displays all registered WordPress users in its own Admin Table with control over screen options, pagination and row action links to perform operations on the user’s metadata. Each row action leads to a plugin page to carry out these operations.

This use-case certainly isn’t the ideal one as I could simply have added the functionality to the default WordPress Users page using user_row_actions; however, it serves the purpose of implementing a List Table from scratch. So let’s get started.

Inheriting the WP_List_Table Class

To create an Admin Table you will need to define a child class in your plugin that extends the core WP_List_Table class provided by WordPress. So, among the first things that you should do, is copy the file class-wp-list-table.php under /wp-admin/includes/ to your plugin. I strongly recommend this because the core class has been marked as private, and so, any potential changes in the class or its methods by WordPress will not break your plugin.

In my plugin, I’ve copied the file to inc/libraries and added a Namespace directive. If you’re not using Namespaces, you will need to prefix everything.

importing the wp-list-table class manually
Copying the wp-list-table class file

I then defined my own PHP child class User_List_Table in inc/admin/class-user-list-table.php that extended the base WP_List_Table class under the folder inc/libraries of my plugin.

The snippet above is a barebone implementation with two methods that must be overridden. Obviously, there’s a lot that needs to be done in the child class but I wanted to start off by just rendering an empty List Table first.

Rendering a List Table

At this stage, you may choose to add the constructor and other methods to the child class; however, I prefer to render an empty List Table first. I find this top-down approach rather effective as I can see the output of the List Table at each stage, making it easier to iron out any bugs that may creep in.

Rendering a List Table typically requires three steps:

  • Instantiate the child List Table class
  • Call prepare_items() – which handles the data prep prior to rendering the table
  • Call display() – which does the actual rendering of the table

These two methods set the wheels in motion. So let’s look at where and how these methods are called in the plugin.

Adding the Plugin Menu Page
The plugin has a submenu page under Users, which is where the custom User List Table is displayed.

user-menu for the list table
Menu Page for the List Table

To see how I added the user submenu page, take a look at the define_admin_hooks() method in inc/core/class-init.php and the add_plugin_admin_menu() method in inc/admin/class-admin.php of the plugin.

Notice the callback load_user_list_table()for add_users_page() in the gist. This is where the methods prepare_items() and display() are invoked.

However, I am instantiating the subclass in load_user_list_table_screen_options() instead, which is the callback for the page hook. It executes just before the plugin page is loaded, and creating the instance of my child class here will allow WordPress to automatically add the columns of the List Table in the screen options panel. The screen options to control the pagination in the List Table are added here as well.

Also note that display() needs to be wrapped in an HTML form if you want to take advantage of features like bulk actions. I prefer to not mix the HTML which is why I used the include_once directive to load partials-wp-list-table-demo-display.php where the method is finally invoked.

Verifying the Barebone List Table Setup

When I opened the plugin page under the Users menu, WordPress noticed that a List Table is being requested, and as there was no data supplied to it, it rendered one with a message “No items found”.

barebone-list-table-implementation
The barebone User List Table

Notice how WordPress populated the HTML form elements, and the list table in the inspect view. It also automatically added the class attributes to control the styling to conform to the WordPress user interface standards. With the table rendering properly, let’s look at the other methods to complete the User List Table.

Note: At this stage, WordPress will have generated a lot of warnings in the debug log due to the incomplete implementation of the subclass. Use a plugin like developer to keep a watch on the WordPress log.

Overriding Methods of the WP_List_Table Class

Continuing with my top-down approach, I went ahead and added the table columns. To do this, you need to override get_columns() in the subclass.

WP_List_Table::get_columns()

The method expects an array that contains key-value pairs, where key is the column slug, and value is the text of the column’s title in your List Table.

Note the column cb, which is a special column that renders the checkboxes to use with bulk actions. I also modified the “No items found” message by overriding the no_items() method. Here’s my table after adding the column details.

wp-list-table-get-columns method
Columns retrieved after overriding get_columns()

With the table shaping up well, I then added the necessary code to prepare_items() to populate the data from the database into the List Table.

WP_List_Table::prepare_items()

You have to provide your own implementation of prepare_items() to handle the table data. The method should be explicitly invoked just before rendering the table. So, all data related operations such as fetching, sorting, pagination, filtering, etc. must be handled here before the table is displayed.

To load the table data, I queried the WordPress database in fetch_table_data() and stored the result in an array before assigning it to the items variable of the parent class.

However, this won’t be sufficient to display data in the table. To complete the display, you also need to provide column-specific implementations using the column_*() methods.

WP_List_Table::column_default()

In the method get_columns() above, I provided the column slugs for each column. These slugs can be used to identify the individual column methods. While it’s a good practice to provide column-specific methods for each column, you can also simply use the column_default() method instead. The WP_List_Table class defaults to column_default() when it can’t find a column-specific method.

This rendered the columns, and I was able to see data in my List Table.

wp-list-table-display-column-data
List Table data after adding the column_default() method

WP_List_Table::column_cb()

If you want to use bulk actions to process rows, you will also need to provide an implementation for column_cb(), which is a special case of the column-specific method.

This rendered checkboxes for all the rows in my List Table. The value of the checkbox field should be unique such as a user_login or an id so that you can identify a record distinctly.

I then added support for sorting and pagination in the List Table.

Sorting with WP_List_Table::get_sortable_columns()

Overriding get_sortable_columns() will make the specified columns sortable in the List Table. However, the code to actually sort the data needs to written in prepare_items(). In my example, I performed the sorting in the SQL itself while fetching the data in fetch_table_data() as shown above.

When a sort is performed on a column, its reference is available via the supergloabls $_GET['orderby'] and $_GET['order'].

wp-list-table-sortable-columns
Defining the sortable columns in the List Table

Handling Pagination with WP_List_Table::set_pagination_args()

The screen options that I added earlier already had the controls to limit the data in the table. To link these with the table, I used get_items_per_page() and set_pagination_args() to set the pagination arguments in the prepare_items().

To render a subset of data for the individual table pages, I used array_slice() on my result array prior to rendering it.

wp-list-table-pagination
Adding pagination the List Table

Next, I added the Search Box, Bulk Actions and Row Actions to my List Table.

WP_List_Table::search_box()

To add a search box, you need to wrap it in a form along with a hidden field to ensure it submits to the plugin’s page. I added these in the partials-wp-list-table-demo-display.php just before the display(). The handling of the data for the search is done in prepare_items().

wp-list-table-search-data
Adding a search box to the List Table

When a search is performed, the search key will be available via the superglobal $_REQUEST['s']. In my example, I manipulated the result array using array_filter and the search key.

WP_List_Table::get_bulk_actions()

Bulk actions are added by overriding get_bulk_actions(), and you need to specify the action in an associative array. The actual handling of the actions needs to be performed in prepare_items().

On submitting the bulk action, two variables action and action2 will be set. The value will depend on which bulk action you applied, the one above or below the table.

wp-list-table-bulk-actions
Bulk Actions and Row Actions in the List Table

In my plugin, the handle_table_actions() method call in prepare_items() takes care of processing the user actions.

Once the nonce is verified, I loaded the partials-wp-list-table-demo-bulk-download.php under inc/admin/views/ to handle the bulk actions.

Finally, I added the table row action links.

Adding Row Action Links to the List Table

To add row action links, you need to provide a column-specific implementation for the table column that should display the action links. In my plugin, I added two action links – Add Meta and View Meta to the user_login column.

In my example, when a row action is executed, the user id, a custom nonce, and the action are all supplied as part of the URL parameters./users.php?page=nds-wp-list-table-demo&action=view_usermeta&user_id=11&_wpnonce=9c38dd6c2c
These are used to handle the user action in the prepare_items(). I handled the row actions in the same method handle_table_actions() as that of bulk actions.

You can identify the action being peformed with current_action(), and direct the user accordingly. Take a look at the partials-wp-list-table-demo-view-usermeta.php under inc/admin/views to see how I processed the request for the View Meta action.

That’s it! Here’s the completed List Table.

custom-user-list-table
The custom User List Table built with the WP_List_Table class

Wrapping Up

I hope you found this article useful. Here are some more examples of the WP_List_Table class being used in popular plugins:

Karan Gupta
Have you used List Tables? Let us know your experience in the comments below.