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.
Continue reading, or jump ahead using the links below:
- WordPress Admin Tables – The Background
- Anatomy of the WP_List_Table Class
- The Custom List Table Plugin for User Meta Operations
- Inheriting the WP_List_Table Class
- Rendering a List Table
- Overriding Methods of the WP_List_Table Class
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.
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.
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:
- WordPress Development for Beginners: Getting Started
- WordPress Development for Beginners: Widgets and Menus
- WordPress Development for Beginners: Building Plugins
- Advanced WordPress Development: Introduction to Object-Oriented Programming
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:
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.
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.
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
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.
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
/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.
I then defined my own PHP child class
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
prepare_items()– which handles the data prep prior to rendering the table
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.
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
add_users_page() in the gist. This is where the methods
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”.
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.
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.
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.
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
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.
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()
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
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
set_pagination_args() to set the pagination arguments in the
To render a subset of data for the individual table pages, I used
array_slice() on my result array prior to rendering it.
Next, I added the Search Box, Bulk Actions and Row Actions to my List Table.
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
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.
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
On submitting the bulk action, two variables
action2 will be set. The value will depend on which
bulk action you applied, the one above or below the 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
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.
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
inc/admin/views to see how I processed the request for the View Meta action.
That’s it! Here’s the completed List Table.
I hope you found this article useful. Here are some more examples of the WP_List_Table class being used in popular plugins:Tags: