PHPackages                             arraypress/wp-register-row-actions - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. arraypress/wp-register-row-actions

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

arraypress/wp-register-row-actions
==================================

Lightweight library for registering custom row actions in WordPress admin tables.

113PHP

Since Nov 30Pushed 5mo agoCompare

[ Source](https://github.com/arraypress/wp-register-row-actions)[ Packagist](https://packagist.org/packages/arraypress/wp-register-row-actions)[ RSS](/packages/arraypress-wp-register-row-actions/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

WordPress Register Row Actions
==============================

[](#wordpress-register-row-actions)

A lightweight library for registering custom row actions in WordPress admin tables. This library provides a clean, simple API for adding URL-based and AJAX-powered actions to posts, users, taxonomies, comments, and media without complex configuration.

Features
--------

[](#features)

- **Simple API**: Register custom row actions with minimal code
- **Action Positioning**: Position actions before or after existing actions
- **Three Action Types**:
    - Static URL actions
    - Dynamic URL via callbacks
    - AJAX actions with automatic handling
- **Permission Control**: Control action visibility based on user capabilities
- **Automatic AJAX Handling**: Built-in AJAX processing with security
- **Multiple Post Types**: Register same actions across multiple post types or taxonomies
- **Remove Default Actions**: Clean up unwanted default actions
- **Lightweight**: Minimal JavaScript, clean architecture

Requirements
------------

[](#requirements)

- PHP 7.4 or higher
- WordPress 5.0 or higher

Installation
------------

[](#installation)

Install via Composer:

```
composer require arraypress/wp-register-row-actions
```

Basic Usage
-----------

[](#basic-usage)

### Static URL Actions

[](#static-url-actions)

Simple actions that link to a URL:

```
register_post_row_actions( 'post', [
    'preview_external' => [
        'label'    => __( 'Preview External', 'textdomain' ),
        'url'      => 'https://example.com/preview/',
        'position' => 'after:view',
        'target'   => '_blank',
        'icon'     => 'external',
    ]
] );
```

### Dynamic URL Actions

[](#dynamic-url-actions)

Actions with URLs generated via callback:

```
register_post_row_actions( 'post', [
    'duplicate' => [
        'label'        => __( 'Duplicate', 'textdomain' ),
        'url_callback' => function( $post_id ) {
            return wp_nonce_url(
                admin_url( "admin.php?action=duplicate_post&post=$post_id" ),
                "duplicate_post_$post_id"
            );
        },
        'position'     => 'after:edit',
        'capability'   => 'edit_posts',
    ]
] );
```

### AJAX Actions

[](#ajax-actions)

Actions that execute via AJAX with automatic handling:

```
register_post_row_actions( 'post', [
    'mark_featured' => [
        'label'    => __( 'Mark Featured', 'textdomain' ),
        'position' => 'after:edit',
        'ajax'     => true,
        'callback' => function( $post_id, $options = [] ) {
            // Perform the action
            update_post_meta( $post_id, 'featured', true );

            // Return response
            return [
                'success' => true,
                'message' => __( 'Post marked as featured', 'textdomain' ),
                'reload'  => true, // Optional: reload the page
            ];
        },
        'confirm'  => __( 'Mark this post as featured?', 'textdomain' ),
    ]
] );
```

Action Configuration Options
----------------------------

[](#action-configuration-options)

OptionTypeDescription`label`stringAction label text`url`stringStatic URL for the action`url_callback`callableFunction to generate URL dynamically`ajax`boolWhether this is an AJAX action`callback`callableFunction to execute for AJAX actions`position`stringAction position (e.g., 'after:edit', 'before:trash')`permission_callback`callableFunction to check if user can see action`capability`stringRequired capability (default: 'manage\_options')`confirm`stringConfirmation message for AJAX actions`class`stringAdditional CSS classes`target`stringLink target (e.g., '\_blank')`icon`stringDashicon name (without 'dashicons-' prefix)Table-Specific Examples
-----------------------

[](#table-specific-examples)

### Post Actions

[](#post-actions)

```
register_post_row_actions( 'post', [
    'send_notification' => [
        'label'    => __( 'Send Notification', 'textdomain' ),
        'position' => 'after:edit',
        'ajax'     => true,
        'callback' => function( $post_id ) {
            // Send notification logic
            $result = send_post_notification( $post_id );

            return [
                'message' => $result ?
                    __( 'Notification sent!', 'textdomain' ) :
                    __( 'Failed to send notification', 'textdomain' ),
                'reload'  => false,
            ];
        },
        'icon'     => 'email',
    ]
] );
```

### User Actions

[](#user-actions)

```
register_user_row_actions( [
    'send_welcome_email' => [
        'label'               => __( 'Send Welcome Email', 'textdomain' ),
        'position'            => 'after:edit',
        'ajax'                => true,
        'callback'            => function( $user_id ) {
            wp_mail(
                get_userdata( $user_id )->user_email,
                'Welcome!',
                'Welcome to our site!'
            );

            return [
                'message' => __( 'Welcome email sent!', 'textdomain' ),
            ];
        },
        'permission_callback' => function( $user_id ) {
            return current_user_can( 'edit_user', $user_id );
        },
    ]
] );
```

### Taxonomy Actions

[](#taxonomy-actions)

```
register_taxonomy_row_actions( [ 'category', 'post_tag' ], [
    'export_terms' => [
        'label'        => __( 'Export', 'textdomain' ),
        'url_callback' => function( $term_id ) {
            return admin_url( "admin-ajax.php?action=export_term&term_id=$term_id" );
        },
        'position'     => 'after:edit',
        'icon'         => 'download',
    ]
] );
```

### Comment Actions

[](#comment-actions)

```
register_comment_row_actions( [
    'mark_helpful' => [
        'label'    => __( 'Mark Helpful', 'textdomain' ),
        'position' => 'after:approve',
        'ajax'     => true,
        'callback' => function( $comment_id ) {
            update_comment_meta( $comment_id, 'helpful', true );

            return [
                'message' => __( 'Comment marked as helpful', 'textdomain' ),
            ];
        },
    ]
] );
```

### Media Actions

[](#media-actions)

```
register_media_row_actions( [
    'regenerate_thumbnails' => [
        'label'    => __( 'Regenerate Thumbnails', 'textdomain' ),
        'position' => 'after:edit',
        'ajax'     => true,
        'callback' => function( $attachment_id ) {
            require_once ABSPATH . 'wp-admin/includes/image.php';

            $file = get_attached_file( $attachment_id );
            wp_generate_attachment_metadata( $attachment_id, $file );

            return [
                'message' => __( 'Thumbnails regenerated', 'textdomain' ),
            ];
        },
    ]
] );
```

AJAX Callback Responses
-----------------------

[](#ajax-callback-responses)

Your AJAX callback can return various response types:

### Simple Success

[](#simple-success)

```
'callback' => function( $object_id ) {
    // Do something

    return [
        'message' => __( 'Action completed', 'textdomain' ),
    ];
}
```

### Reload Page

[](#reload-page)

```
'callback' => function( $object_id ) {
    // Do something

    return [
        'message' => __( 'Action completed', 'textdomain' ),
        'reload'  => true, // Page will reload after success
    ];
}
```

### Redirect

[](#redirect)

```
'callback' => function( $object_id ) {
    // Do something

    return [
        'redirect' => admin_url( 'edit.php?success=1' ),
    ];
}
```

### Remove Row

[](#remove-row)

```
'callback' => function( $object_id ) {
    // Do something (like soft delete)

    return [
        'message'    => __( 'Item removed', 'textdomain' ),
        'remove_row' => true, // Row will fade out and be removed
    ];
}
```

### Update Row HTML

[](#update-row-html)

```
'callback' => function( $object_id ) {
    // Do something

    // Generate new row HTML (advanced)
    ob_start();
    // Output new row HTML
    $row_html = ob_get_clean();

    return [
        'row_html' => $row_html, // Row will be replaced
    ];
}
```

Permission Control
------------------

[](#permission-control)

Control who can see actions:

```
register_post_row_actions( 'post', [
    'publish_now' => [
        'label'               => __( 'Publish Now', 'textdomain' ),
        'ajax'                => true,
        'callback'            => function( $post_id ) {
            wp_update_post( [
                'ID'          => $post_id,
                'post_status' => 'publish',
            ] );

            return [
                'message' => __( 'Post published', 'textdomain' ),
                'reload'  => true,
            ];
        },
        'permission_callback' => function( $post_id ) {
            // Only show for draft posts
            $post = get_post( $post_id );
            return $post->post_status === 'draft' && current_user_can( 'publish_post', $post_id );
        },
    ]
] );
```

Multiple Post Types
-------------------

[](#multiple-post-types)

Register the same actions across multiple post types:

```
register_post_row_actions( [ 'post', 'page', 'custom_post_type' ], [
    'custom_action' => [
        'label'    => __( 'Custom Action', 'textdomain' ),
        'position' => 'after:edit',
        // ... action config
    ]
] );
```

Removing Default Actions
------------------------

[](#removing-default-actions)

Remove unwanted default actions:

```
register_post_row_actions( 'post', [
    'custom_action' => [
        'label' => __( 'My Action', 'textdomain' ),
        // ... config
    ]
], [ 'inline hide-if-no-js', 'trash' ] ); // Remove Quick Edit and Trash
```

Advanced Examples
-----------------

[](#advanced-examples)

### Conditional Actions Based on Meta

[](#conditional-actions-based-on-meta)

```
register_post_row_actions( 'product', [
    'sync_inventory' => [
        'label'               => __( 'Sync Inventory', 'textdomain' ),
        'position'            => 'after:edit',
        'ajax'                => true,
        'callback'            => function( $post_id ) {
            // Sync with external API
            $result = sync_product_inventory( $post_id );

            return [
                'message' => $result['message'],
                'reload'  => $result['success'],
            ];
        },
        'permission_callback' => function( $post_id ) {
            // Only show for products with external sync enabled
            return get_post_meta( $post_id, 'enable_external_sync', true ) === '1';
        },
        'icon'                => 'update',
        'confirm'             => __( 'Sync inventory with external system?', 'textdomain' ),
    ]
] );
```

### Bulk Action Trigger

[](#bulk-action-trigger)

```
register_post_row_actions( 'post', [
    'add_to_queue' => [
        'label'    => __( 'Add to Queue', 'textdomain' ),
        'position' => 'after:edit',
        'ajax'     => true,
        'callback' => function( $post_id, $options = [] ) {
            // Add to processing queue
            add_to_processing_queue( $post_id );

            return [
                'message' => __( 'Added to processing queue', 'textdomain' ),
            ];
        },
        'icon'     => 'plus-alt',
    ]
] );
```

### Export Action

[](#export-action)

```
register_post_row_actions( 'post', [
    'export_pdf' => [
        'label'        => __( 'Export PDF', 'textdomain' ),
        'url_callback' => function( $post_id ) {
            return add_query_arg( [
                'action'  => 'export_post_pdf',
                'post_id' => $post_id,
                '_wpnonce' => wp_create_nonce( 'export_pdf_' . $post_id ),
            ], admin_url( 'admin-ajax.php' ) );
        },
        'position'     => 'after:view',
        'target'       => '_blank',
        'icon'         => 'media-document',
    ]
] );
```

JavaScript Events
-----------------

[](#javascript-events)

The library fires custom JavaScript events you can hook into:

```
jQuery(document).on('rowActionSuccess', function(e, data) {
    console.log('Action succeeded:', data);
});

jQuery(document).on('rowActionError', function(e, data) {
    console.log('Action failed:', data);
});

jQuery(document).on('rowActionComplete', function(e, data) {
    console.log('Action completed (success or error):', data);
});
```

Best Practices
--------------

[](#best-practices)

1. **Always Use Nonces**: For URL actions, generate nonces in `url_callback`
2. **Validate Permissions**: Use `permission_callback` for dynamic permission checks
3. **Provide Feedback**: Always return a `message` in AJAX callbacks
4. **Use Confirmations**: Add `confirm` messages for destructive actions
5. **Handle Errors**: Wrap callback code in try-catch and return appropriate errors
6. **Keep Callbacks Light**: For heavy operations, queue them for background processing
7. **Test Capabilities**: Ensure proper capability checks in both permission callbacks and AJAX handlers

Error Handling
--------------

[](#error-handling)

Handle errors gracefully in your callbacks:

```
'callback' => function( $post_id ) {
    try {
        // Attempt action
        $result = do_something_that_might_fail( $post_id );

        if ( ! $result ) {
            throw new Exception( __( 'Operation failed', 'textdomain' ) );
        }

        return [
            'message' => __( 'Success!', 'textdomain' ),
        ];

    } catch ( Exception $e ) {
        return [
            'success' => false,
            'message' => $e->getMessage(),
        ];
    }
}
```

Comparison with Native WordPress
--------------------------------

[](#comparison-with-native-wordpress)

**Before (Manual Approach):**

```
add_filter( 'post_row_actions', function( $actions, $post ) {
    $actions['custom'] = sprintf(
        'Custom Action',
        $post->ID
    );
    return $actions;
}, 10, 2 );

add_action( 'wp_ajax_my_custom_action', function() {
    // Manual nonce checking
    // Manual capability checking
    // Manual AJAX handling
    // Manual response formatting
} );
```

**After (This Library):**

```
register_post_row_actions( 'post', [
    'custom' => [
        'label'    => 'Custom Action',
        'ajax'     => true,
        'callback' => function( $post_id ) {
            // Just the business logic
            return [ 'message' => 'Done!' ];
        },
    ]
] );
```

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

License
-------

[](#license)

GPL-2.0-or-later

Author
------

[](#author)

David Sherlock - [ArrayPress](https://arraypress.com/)

Support
-------

[](#support)

- [Documentation](https://github.com/arraypress/wp-register-row-actions)
- [Issue Tracker](https://github.com/arraypress/wp-register-row-actions/issues)

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance48

Moderate activity, may be stable

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity13

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/cd6eb8aff0903d87eb674d1ba3c5f3653899c0d7661504eb0deb7798ed86b643?d=identicon)[arraypress](/maintainers/arraypress)

---

Top Contributors

[![arraypress](https://avatars.githubusercontent.com/u/22668877?v=4)](https://github.com/arraypress "arraypress (11 commits)")

### Embed Badge

![Health badge](/badges/arraypress-wp-register-row-actions/health.svg)

```
[![Health](https://phpackages.com/badges/arraypress-wp-register-row-actions/health.svg)](https://phpackages.com/packages/arraypress-wp-register-row-actions)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
