Client-Side Abilities API in WordPress 7.0

WordPress 6.9 introduced the Abilities API. The APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. provides a common interface that AI agents, workflow automation tools, and plugins can use to interact with WordPress. In WordPress 7.0 we continued that work and now provide a counterpart JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com API that can be used to implement client-side abilities like navigating, or inserting blocks. This work is fundamental to integrate with browser agents/extensions and WebMCP.

Two packages

The client-side Abilities API is split into two packages:

  • @wordpress/abilities: A pure state management package with no WordPress server dependencies. It provides the store, registration functions, querying, and execution logic. Use this when you only need the abilities store without loading server-registered abilities. This package could also be used in non-WordPress projects.
  • @wordpress/core-abilities :The WordPress integration layer. When loaded, it automatically fetches all abilities and categories registered on the server via the REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/ (/wp-abilities/v1/) and registers them in the @wordpress/abilities store with appropriate callbacks.

Getting started

To use the Abilities API in your pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party., you need to enqueue the appropriate script module.

When your plugin needs server-registered abilities

If your plugin needs access to abilities registered on the server (e.g., coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. abilities), enqueue @wordpress/core-abilities. This is the most common case:

add_action( 'admin_enqueue_scripts', 'my_plugin_enqueue_abilities' );
function my_plugin_enqueue_abilities() {
    wp_enqueue_script_module( '@wordpress/core-abilities' );
}

This will load both @wordpress/core-abilities and its dependency @wordpress/abilities, and automatically fetch and register all server-side abilities.

When your plugin only registers client-side abilities

If your plugin only needs to register and work with its own client-side abilities on a specific page, without needing server-registered abilities, you can enqueue just @wordpress/abilities:

add_action( 'admin_enqueue_scripts', 'my_plugin_enqueue_abilities' );
function my_plugin_enqueue_abilities( $hook_suffix ) {
    if ( 'my-plugin-page' !== $hook_suffix ) {
        return;
    }
    wp_enqueue_script_module( '@wordpress/abilities' );
}

Importing in JavaScript

Abilities API should be imported as a dynamic import, for example:

const {
    registerAbility,
    registerAbilityCategory,
    getAbilities,
    executeAbility,
} = await import( '@wordpress/abilities' );

If your client code is also a script module relying on @wordpress/scripts, you can just use the following code like any other import:

import {
    registerAbility,
    registerAbilityCategory,
    getAbilities,
    executeAbility,
} from '@wordpress/abilities';

Registering abilities

Register a categoryCategory The 'category' taxonomy lets you group posts / content together that share a common bond. Categories are pre-defined and broad ranging. first

Abilities are organized into categories. Before registering an ability, its category must exist. Server-side categories are loaded automatically when @wordpress/core-abilities is enqueued. To register a client-side category:

const { registerAbilityCategory } = await import( '@wordpress/abilities' );

registerAbilityCategory( 'my-plugin-actions', {
    label: 'My Plugin Actions',
    description: 'Actions provided by My Plugin',
} );

Category slugs must be lowercase alphanumeric with dashes only (e.g., data-retrieval, user-management).

Register an ability

const { registerAbility } = await import( '@wordpress/abilities' );

registerAbility( {
    name: 'my-plugin/navigate-to-settings',
    label: 'Navigate to Settings',
    description: 'Navigates to the plugin settings page',
    category: 'my-plugin-actions',
    callback: async () => {
        window.location.href = '/wp-admin/options-general.php?page=my-plugin';
        return { success: true };
    },
} );

Input and output schemas

Abilities should define JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. Schema (draft-04) for input validation and output validation:

registerAbility( {
    name: 'my-plugin/create-item',
    label: 'Create Item',
    description: 'Creates a new item with the given title and content',
    category: 'my-plugin-actions',
    input_schema: {
        type: 'object',
        properties: {
            title: { type: 'string', description: 'The title of the item', minLength: 1 },
            content: { type: 'string', description: 'The content of the item' },
            status: { type: 'string', description: 'The publish status of the item', enum: [ 'draft', 'publish' ] },
        },
        required: [ 'title' ],
    },
    output_schema: {
        type: 'object',
        properties: {
            id: { type: 'number', description: 'The unique identifier of the created item' },
            title: { type: 'string', description: 'The title of the created item' },
        },
        required: [ 'id' ],
    },
    callback: async ( { title, content, status = 'draft' } ) => {
        // Create the item...
        return { id: 123, title };
    },
} );

When executeAbility is called, the input is validated against input_schema before execution and the output is validated against output_schema after execution. If validation fails, an error is thrown with the code ability_invalid_input or ability_invalid_output.

Permission callbacks

Abilities can include a permissionCallback that is checked before execution:

registerAbility( {
    name: 'my-plugin/admin-action',
    label: 'Admin Action',
    description: 'An action only available to administrators',
    category: 'my-plugin-actions',
    permissionCallback: () => {
        return currentUserCan( 'manage_options' );
    },
    callback: async () => {
        // Only runs if permissionCallback returns true
        return { success: true };
    },
} );

If the permission callback returns false, an error with code ability_permission_denied is thrown.

Querying abilities

Direct function calls

const {
    getAbilities,
    getAbility,
    getAbilityCategories,
    getAbilityCategory,
} = await import( '@wordpress/abilities' );

// Get all registered abilities
const abilities = getAbilities();

// Filter abilities by category
const dataAbilities = getAbilities( { category: 'data-retrieval' } );

// Get a specific ability by name
const ability = getAbility( 'my-plugin/create-item' );

// Get all categories
const categories = getAbilityCategories();

// Get a specific category
const category = getAbilityCategory( 'data-retrieval' );

Using with ReactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org and @wordpress/data

The abilities store (core/abilities) integrates with @wordpress/data, so you can use useSelect for reactive queries in React components:

import { useSelect } from '@wordpress/data';
import { store as abilitiesStore } from '@wordpress/abilities';

function AbilitiesList() {
	// Get all abilities reactively
	const abilities = useSelect(
		( select ) => select( abilitiesStore ).getAbilities(),
		[]
	);

	// Filter by category
	const dataAbilities = useSelect(
		( select ) =>
			select( abilitiesStore ).getAbilities( {
				category: 'data-retrieval',
			} ),
		[]
	);

	// abilities and dataAbilities update automatically when the store changes
}

Executing abilities

Use executeAbility to run any registered ability, whether client-side or server-side:

import { executeAbility } from '@wordpress/abilities';

try {
    const result = await executeAbility( 'my-plugin/create-item', {
        title: 'New Item',
        content: 'Item content',
        status: 'draft',
    } );
    console.log( 'Created item:', result.id );
} catch ( error ) {
    switch ( error.code ) {
        case 'ability_permission_denied':
            console.error( 'You do not have permission to run this ability.' );
            break;
        case 'ability_invalid_input':
            console.error( 'Invalid input:', error.message );
            break;
        case 'ability_invalid_output':
            console.error( 'Unexpected output:', error.message );
            break;
        default:
            console.error( 'Execution failed:', error.message );
    }
}

For server-side abilities (those registered via PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher and loaded by @wordpress/core-abilities), execution is handled automatically via the REST API. The HTTPHTTP HTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. method used depends on the ability’s annotations:

  • readonly: true: uses GET
  • destructive: true + idempotent: true: uses DELETE
  • All other cases: uses POST

Annotations

Abilities support metadata annotations that describe their behavior:

registerAbility( {
    name: 'my-plugin/get-stats',
    label: 'Get Stats',
    description: 'Returns plugin statistics',
    category: 'my-plugin-actions',
    callback: async () => {
        return { views: 100 };
    },
    meta: {
        annotations: {
            readonly: true,
        },
    },
} );

Available annotations:

AnnotationTypeDescription
readonlybooleanThe ability only reads data, does not modify state
destructivebooleanThe ability performs destructive operations
idempotentbooleanThe ability can be called multiple times with the same result

Unregistering

Client-registered abilities and categories can be removed:

const { unregisterAbility, unregisterAbilityCategory } = await import( '@wordpress/abilities' );

unregisterAbility( 'my-plugin/navigate-to-settings' );
unregisterAbilityCategory( 'my-plugin-actions' );

Server-side abilities

Abilities registered on the server via the PHP API (wp_register_ability(), wp_register_ability_category()) are automatically made available on the client when @wordpress/core-abilities is loaded. WordPress core enqueues @wordpress/core-abilities on all adminadmin (and super admin) pages, so server abilities are available by default in the admin.

Plugins that register server-side abilities do not need any additional client-side setup. The abilities will be fetched from the REST API and registered in the client store automatically.

#7-0, #dev-notes, #dev-notes-7-0