I would like to add a custom bulk action to a custom post type. I came across the filter bulk_actions-screenid, which according to its documentation, would do exactly as I wish. However, after about two hours of debugging I found the following comment // This filter can currently only be used to remove actions. on line 278 of class-wp-list-table.php - great!

I figured I could hack it by using jQuery to inject the action as an option

 * Hack to add a custom bulk action. 
public function admin_footer() {
    if($_GET['post_type'] != self::POST_TYPE) return;
    <script type="text/javascript">
        jQuery(document).ready(function() {

This works. The action now appears in the bulk actions menu. I was under the assumption I could then add some logic into admin_init to do the necessary processing - however, it appears that create_invoice is never posted. Is there something I'm doing wrong?

=== UPDATE ===

I updated the code to use the load-* hook. When I apply the bulk action on users.php - I see create_invoice is passed through the request. However, on edit.php create_invoice is never printed.

function a39x2_admin_footer() {
    <script type="text/javascript">
        jQuery(document).ready(function() {
add_action('admin_footer', 'a39x2_admin_footer');

function a39x2_load() {
    echo "<pre>" . print_r($_REQUEST, true) . "</pre>";
add_action('load-edit.php', 'a39x2_load');
add_action('load-users.php', 'a39x2_load');

I think the latest major release warrants a new answer to this question, considering the popularity of this question.

Since WordPress 4.7 (released December 2016) it is possible to add custom bulk actions without using JavaScript.

The filter bulk_actions-{$screen} (e.g. bulk_actions-edit-page for the pages overview) now allows you to add custom bulk actions. Furthermore, a new action called handle_bulk_actions-{$screen} (e.g. handle_bulk_actions-edit-page) allows you to handle execution of the action.

This is all explained pretty well in this blog post. For example, let's say we want to add a bulk action to email the titles of the selected items on the pages overview. We could do it like this:

For a small example, where we add an action to the bulk actions dropdown and add a handler function to it.

Adding the bulk action to the dropdown:

function wpse29822_page_bulk_actions( $actions ) {
    // Add custom bulk action
    $actions['my-action-handle'] = __( 'My Custom Bulk Action' );
    return $actions;
add_action( 'bulk_actions-edit-page', 'wpse29822_page_bulk_actions' );

Adding a handler for the bulk action:

function wpse29822_page_bulk_actions_handle( $redirect_to, $doaction, $post_ids ) {
    // Check whether action that user wants to perform is our custom action
    if ( $doaction == 'my-action-handle' ) {
        // Do stuff
    return $redirect_to;
add_action( 'handle_bulk_actions-edit-page', 'wpse29822_page_bulk_actions_handle', 10, 3 );
  • This handler action doesn't work for me. I'm using WordPress 4.7.2. I would appreciate any help on this. add_action( 'handle_bulk_actions-edit-page', 'wpse29822_page_bulk_actions_handle', 10, 3 ); – Maria Daniel Deepak Feb 22 '17 at 11:13
  • What isn't working @MariaDanielDeepak? It's working fine on 4.7.2 for me. A few things to consider: (1) The callback is for the "Pages" overview in the admin (as the edit-page part is its screen id) (2) The action will not be executed if no pages are selected (i.e. trying to do the bulk action on an empty set of pages) – engelen Feb 22 '17 at 13:27
  • I appreciate your comment. I've added the complete explanation of what isn't working in the WordPress forum. – Maria Daniel Deepak Feb 22 '17 at 14:03

The bulk_actions-* filter doesn't allow you to add custom bulk actions precisely because it's tricky to then add a handler for a that action. Updated the Codex.

I would suggest using the load-* action instead of admin_init. Note that you have to do all the security checks like check_ajax_referrer() and current_user_can().

  • Thanks! The load-* hook appears to be working. Also, thanks for updating the codex and reopening the trac ticket. – v0idless Sep 29 '11 at 16:16
  • 1
    I may have prematurely accepted the answer. I updated the code to use the load-* hook. When I apply the bulk action on users.php - I see create_invoice is passed through the request. However, on edit.php create_invoice is never printed. – v0idless Sep 29 '11 at 16:40
  • Try adding an exit; call after echo "<pre>" . print_r($_REQUEST, true) . "</pre>"; – scribu Sep 29 '11 at 17:22
  • 1
    I think there is a better action hook than load-* : the admin_action_* (admin_action_create_invoice in your case). It is called just after the load-* hook, and only when the custom bulk action is triggered. – Fabien Quatravaux Feb 8 '14 at 17:51

You must use bulk_actions filters when WP_Screen object is defined. like this:

add_action( 'current_screen', 'custom_bulk_actions' );
function custom_bulk_actions() {
    if( current_user_can( 'administrator' ) {
        add_filter( 'bulk_actions-edit-post'       , 'register_custom_bulk_actions' );
        add_filter( 'handle_bulk_actions-edit-post', 'custom_bulk_action_handler', 10, 3 );
        add_action( 'admin_notices'                , 'custom_bulk_action_admin_notice' );

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.