WordPress 7.0 ships a built-in AI Client: a PHP API that lets plugins send prompts to AI providers and receive their results. What better way to understand how this new API works than by building something real with it? In this article, you will learn how to use the AI Client by building a plugin that generates images directly in the Media Library.
The plugin is intentionally small. It can send a text prompt to an AI provider, receive a generated image, and you can then save it to the WordPress Media Library if you like. Along the way, you will see the patterns that make AI-powered features work reliably in WordPress — all using the new built-in AI Client.
Table of Contents
How the AI client works
Provider and model-agnostic
The most important concept behind the WordPress AI Client is that your plugin never talks to an AI provider directly. You describe what you need — text, an image, a specific output format — and how you need it — aspect ratio for images, temperature for text generation, and so on. With that information, WordPress routes the request to a suitable model from a provider the site owner has configured.
This is relevant for several reasons:
- Site owners stay in control. They choose their provider (Anthropic, Google, OpenAI, or any other supported service) and manage API keys through the Settings > Connectors screen — a centralized admin interface for managing service connections. Your plugin does not need its own settings page for API credentials.
- Plugins are portable. A plugin that works with one provider works with all of them. There are no provider-specific code paths to maintain.
- The ecosystem scales. As new providers and models become available, existing plugins gain support automatically — no plugin updates required.
This means you should never write code that assumes a particular provider or model. Instead, you describe your requirements, and the AI Client finds a suitable match.
The entry point: wp_ai_client_prompt()
Every interaction with the AI Client starts with a single function call:
$builder = wp_ai_client_prompt();
This returns a WP_AI_Client_Prompt_Builder instance — a fluent builder that follows WordPress naming conventions (snake_case methods) while wrapping the underlying php-ai-client library.
From there, you chain methods to describe your prompt. For example, to generate text:
$builder = wp_ai_client_prompt()
->with_text( 'Summarize the benefits of caching in WordPress' )
->using_temperature( 0.7 );
with_text() provides the prompt. using_temperature() is an optional configuration parameter that controls randomness in the output. When you are ready, call a generation method:
$result = $builder->generate_text_result();
The $result is a GenerativeAiResult object — a serializable data transfer object that contains the generated content, along with metadata about which provider and model handled the request. You can pass it directly to rest_ensure_response(), which makes it straightforward to expose AI features through the REST API.
The same builder pattern works for image generation, which is the focus of this article. You will see how to configure a builder for images — with output file type, aspect ratio, and more — in the sections below.
Model preferences, not requirements
Since your plugin does not control which provider or model is available on each individual WordPress site, the AI Client uses a preference system rather than hard requirements.
You can call using_model_preference() with one or more model slugs to indicate which models would be ideal for your use case. The AI Client tries them in order — if the first is available, it uses that; otherwise it falls back to the next, and so on. If none of the preferred models are available, it falls back to any model that supports the requested capability. Your plugin continues to work either way.
$builder = wp_ai_client_prompt()
->with_text( 'Summarize the benefits of caching in WordPress' )
->using_temperature( 0.7 )
->using_model_preference( 'gpt-5.4', 'gemini-3.1-pro-preview', 'claude-opus-4.6' );
This is a preference, not a requirement. Building around a specific model would force vendor lock-in upon your plugin’s users or prevent them from using your plugin — it would defeat the purpose of the abstraction. Use model preferences when a particular model handles your use case especially well, but design your plugin to function without them.
Support checks: gate your AI features
Not every WordPress site will have an AI provider configured, and not every provider supports every capability or configuration option. Before exposing an AI feature to users, check whether it can actually work. For example:
$is_available = wp_ai_client_prompt()
->with_text( 'test' )
->is_supported_for_image_generation();
is_supported_for_image_generation() returns true only when the site has a configured provider with a model that supports image generation. There are equivalent methods for other capabilities like text generation.
This check does not make an API call or perform any inference. The prompt text itself is not analyzed for the support check. The method uses purely deterministic logic, matching the builder’s configuration (requested capability, output options, etc.) against the capabilities the available models declare. There is no cost incurred by calling it.
This check is essential. Use it to conditionally load your UI, show a helpful notice when the feature is unavailable, or skip registering UI items entirely. Never assume that AI features will be available just because WordPress 7.0 is installed — the site owner still needs to set up a provider.
Implementing the plugin
The plugin adds a “Generate Image File” button to the Media Library. Clicking it opens a dialog where the user enters a text prompt. The plugin sends the prompt to the AI Client, displays the generated image as a preview, and lets the user save it as a new media attachment.

The full source code is available on GitHub at wptrainingteam/ai-client-imagegen.
Note: Some code snippets in this article were slightly reduced from the code in the plugin for better focus on the most relevant changes.
Prerequisites
To follow along, you will need:
- WordPress 7.0 (or later) installed and running.
- An AI provider configured with image generation support. Go to Settings > Connectors in the WordPress admin and add a connection for a service like OpenAI or Google AI. Make sure the configured provider offers one or more models that can generate images.
- Node.js (version 20 or later) for building the frontend assets.
- A basic understanding of WordPress plugin development, the REST API, and PHP.
Plugin file structure
The plugin uses a straightforward structure:
ai-client-imagegen/
├── plugin.php
├── includes/
│ ├── prompt.php
│ ├── rest-api.php
│ └── admin.php
├── src/
│ └── index.ts
├── build/
│ ├── index.js
│ └── index.asset.php
├── package.json
└── composer.json
plugin.php— Main entry point. Loads everything, registers hooks.includes/prompt.php— Reusable functions that build AI Client prompts.includes/rest-api.php— REST API endpoint for generating images and saving them to the Media Library.includes/admin.php— Script registration and enqueuing on the Media Library screen.src/index.ts— Frontend: the dialog UI and API calls.build/— Compiled JavaScript output (generated by wordpress/scripts).
The main plugin file
The plugin.php file is minimal. It declares the plugin header, gates on the AI Client being available, loads the include files, and registers hooks:
<?php
/**
* Plugin Name: AI Client ImageGen
* Plugin URI: https://github.com/wptrainingteam/ai-client-imagegen
* Description: Generates images in the WordPress Media Library using the built-in AI Client.
* Requires at least: 7.0
* Requires PHP: 7.4
* Version: 1.0.0
* Author: Your Name
* License: GPL-2.0-or-later
* Text Domain: ai-client-imagegen
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! function_exists( 'wp_ai_client_prompt' ) ) {
return;
}
require_once __DIR__ . '/includes/prompt.php';
require_once __DIR__ . '/includes/rest-api.php';
require_once __DIR__ . '/includes/admin.php';
add_action( 'rest_api_init', 'aicig_register_rest_routes' );
add_action( 'init', 'aicig_register_assets' );
add_action( 'admin_enqueue_scripts', 'aicig_enqueue_media_assets' );
Setting Requires at least: 7.0 in the plugin header ensures WordPress will not activate the plugin on older versions. The function_exists( 'wp_ai_client_prompt' ) check serves as a runtime safety net — if the AI Client function is not available for any reason, the plugin bails out early.
The three add_action()calls wire up the REST API routes, asset registration, and conditional script enqueuing. Each callback is defined in its own file.
Building the prompt
This is the core of the AI Client integration. The includes/prompt.php file defines a reusable function that configures a prompt builder for image generation:
<?php
use WordPress\AiClient\Files\Enums\FileTypeEnum;
use WordPress\AiClient\Files\Enums\MediaOrientationEnum;
function aicig_get_image_generation_prompt( string $prompt, string $orientation = '' ): WP_AI_Client_Prompt_Builder {
$builder = wp_ai_client_prompt()
->with_text( $prompt )
->as_output_file_type( FileTypeEnum::inline() );
if ( $orientation ) {
$builder->as_output_media_orientation( MediaOrientationEnum::from( $orientation ) );
}
return $builder;
}
A few things to note:
- The function returns the builder, not the result. This is a deliberate design choice. The caller decides when to execute the prompt (via
generate_image_result()) and can add further configuration. It also makes the builder available for support checks — the same function is reused to callis_supported_for_image_generation(), as you will see later in the admin integration. FileTypeEnum::inline()requests base64-encoded image data in the response. This is convenient for a plugin that needs to display the image in the browser before saving it.MediaOrientationEnumis optional. If provided, it hints at the desired aspect ratio —square,landscape, orportrait. The methodMediaOrientationEnum::from()converts the string to the enum value.- No provider or model is specified. The AI Client handles routing. The prompt describes what is needed; WordPress determines how to fulfill it. This function will work with any provider that supports image generation.
Wrapping the prompt configuration in a standalone function keeps it reusable. The REST API calls it to generate images, and the admin code calls it to check whether image generation is supported.
The REST API
The plugin exposes two REST endpoints under the ai-client-imagegen/v1 namespace: one for generating images and one for uploading a generated image to the Media Library. Alternatively, a single endpoint could generate the image and save it to the Media Library in one step. However, since users should ideally review generated images before committing to them, splitting the two concerns makes more sense.
Generating an image
The generation route takes a text prompt and an optional orientation. Its callback, which can be defined in the includes/rest-api.php file, uses the reusable prompt function from includes/prompt.php, calls generate_image_result(), and returns the result directly:
function aicig_register_rest_routes(): void {
register_rest_route(
'ai-client-imagegen/v1',
'/generate-image',
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => 'aicig_rest_generate_image',
'permission_callback' => static function () {
return current_user_can( 'upload_files' );
},
'args' => array(
'prompt' => array(
'type' => 'string',
'required' => true,
),
'orientation' => array(
'type' => 'string',
'required' => false,
'enum' => array( 'square', 'landscape', 'portrait' ),
),
),
)
);
}
function aicig_rest_generate_image( WP_REST_Request $request ) {
$prompt = $request->get_param( 'prompt' );
$orientation = $request->get_param( 'orientation' );
$builder = aicig_get_image_generation_prompt(
$prompt,
$orientation ?? ''
);
return rest_ensure_response(
$builder->generate_image_result()
);
}
Because the GenerativeAiResult object that generate_image_result() returns is serializable, rest_ensure_response() converts it directly into a JSON response. The response includes the generated image data, plus metadata about which provider and model handled the request — information the frontend can display as attribution.
Similarly, if an error occurs as part of this, generate_image_result() returns a WP_Error, which can also be processed as a REST response directly — no conditional handling necessary.
Uploading to the Media Library
Once the user is happy with the generated image, the frontend sends it to a second endpoint. This route takes the base64-encoded image data, a file name, and a MIME type. The callback decodes the data, writes it to disk, and creates a WordPress attachment:
function aicig_register_rest_routes(): void {
// Other REST route registration for generating an image.
register_rest_route(
'ai-client-imagegen/v1',
'/upload-image',
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => 'aicig_rest_upload_image',
'permission_callback' => static function () {
return current_user_can( 'upload_files' );
},
'args' => array(
'image_base64' => array(
'type' => 'string',
'required' => true,
),
'file_name' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_file_name',
),
'mime_type' => array(
'type' => 'string',
'required' => false,
'default' => 'image/png',
'sanitize_callback' => 'sanitize_mime_type',
),
),
)
);
}
function aicig_rest_upload_image( WP_REST_Request $request ) {
$image_base64 = $request->get_param( 'image_base64' );
$file_name = $request->get_param( 'file_name' );
$mime_type = $request->get_param( 'mime_type' );
$decoded = base64_decode( $image_base64, true );
if ( false === $decoded ) {
return new WP_Error(
'invalid_image_data',
__( 'The provided image data is not valid base64.', 'ai-client-imagegen' ),
array( 'status' => 400 )
);
}
$upload = wp_upload_bits( $file_name, null, $decoded );
if ( ! empty( $upload['error'] ) ) {
return new WP_Error(
'upload_failed',
$upload['error'],
array( 'status' => 500 )
);
}
$attachment_data = array(
'post_mime_type' => $mime_type,
'post_title' => sanitize_file_name(
pathinfo( $file_name, PATHINFO_FILENAME )
),
'post_status' => 'inherit',
);
$attachment_id = wp_insert_attachment(
$attachment_data,
$upload['file']
);
if ( is_wp_error( $attachment_id ) ) {
return $attachment_id;
}
require_once ABSPATH . 'wp-admin/includes/image.php';
$metadata = wp_generate_attachment_metadata(
$attachment_id,
$upload['file']
);
wp_update_attachment_metadata( $attachment_id, $metadata );
return rest_ensure_response(
array(
'id' => $attachment_id,
'url' => wp_get_attachment_url( $attachment_id ),
)
);
}
This is standard WordPress media handling — wp_upload_bits() to write the file, wp_insert_attachment() to create the database record, and wp_generate_attachment_metadata() to create thumbnails and image metadata. Nothing here is specific to AI.
With this, we’re done with the server-side foundation for the feature. Let’s focus on frontend and UI next.
Admin integration
The only thing we need PHP for in relation to the frontend is for managing the script that renders the dynamic UI and handles interactions. The includes/admin.php file handles three things: registering the script, conditionally enqueuing it on the Media Library screen, and showing a notice when image generation is not supported.
Registering the script
function aicig_register_assets(): void {
$asset_file = plugin_dir_path( __DIR__ ) . 'build/index.asset.php';
if ( file_exists( $asset_file ) ) {
$asset = require $asset_file;
} else {
$asset = array(
'dependencies' => array(),
'version' => '1.0.0',
);
}
wp_register_script(
'aicig-imagegen',
plugins_url( 'build/index.js', __DIR__ ),
$asset['dependencies'],
$asset['version'],
array( 'strategy' => 'defer' )
);
}
The build/index.asset.php file is auto-generated by @wordpress/scripts during the build step. It declares the script’s dependencies and a content-based version hash. This is the standard WordPress pattern for bundled scripts.
Conditional enqueuing with support check
In order for the script to actually load in the frontend, we’ll also need to enqueue it. This is where we need to apply support check first though, so that the script is only loaded if the AI based functionality is actually supported:
function aicig_enqueue_media_assets( string $hook_suffix ): void {
if ( 'upload.php' !== $hook_suffix ) {
return;
}
if ( ! current_user_can( 'upload_files' ) ) {
return;
}
if ( ! aicig_get_image_generation_prompt( 'test' )->is_supported_for_image_generation() ) {
// Show an admin notice, or handle the unsupported case in another way.
return;
}
wp_enqueue_script( 'aicig-imagegen' );
}
Three checks happen in sequence:
- Screen check — Only run on
upload.php(the Media Library). - Capability check — Only for users who can upload files. It’s important to use the same capability here that the REST API endpoints use, so we gate the UI based on the same criteria. Otherwise, you may render UI for something that doesn’t actually work.
- Support check — Call
is_supported_for_image_generation()on a prompt builder to verify that a configured provider can handle image generation. If not, bail out early — you might want to show a notice pointing the site owner to the Connectors settings.
Notice how the same aicig_get_image_generation_prompt() function from includes/prompt.php is reused here. Passing ‘test’ as the prompt text is sufficient — the method only checks provider and model capabilities, not the prompt content. That is why we didn’t have it return the prompt result, so it can also be used to check for support.
The script is only enqueued when all three conditions pass. This means the “Generate Image File” button only appears when it will actually work.
The frontend
The frontend TypeScript file is ~590 lines of vanilla DOM manipulation. It creates a modal dialog, handles form submission via @wordpress/api-fetch, displays the generated image preview with provider/model attribution, and handles saving to the Media Library.
Explaining that code in depth would go beyond the purpose of this article. It’s all pretty standard vanilla JavaScript functionality, so I’d encourage you to study it separately if you’re interested. So rather than walking through the frontend code in detail here, you can copy it from the GitHub repository: src/index.ts. Place the code in your local src/index.ts file. Once it is built, it will appear as build/index.js, i.e. the same script path we registered above in PHP.
Just a few brief notes on the client-side code: The code uses vanilla JavaScript (well, TypeScript compiled to JavaScript) with no framework dependency beyond @wordpress/api-fetch and @wordpress/i18n. This is a deliberate choice — for a self-contained dialog, vanilla DOM manipulation is lightweight and performant. Modern coding agents are also particularly good at writing this kind of straightforward imperative UI code. For more complex plugin interfaces, using WordPress’s bundled version of React would probably be the better fit.
Build and test
The plugin needs two configuration files in the root directory to build the frontend code and allow testing the plugin in a bundled WordPress development environment.
Note: You need Node.js installed on your system to use the tooling. To use the built-in development environment based on @wordpress/env, you also need to have Docker installed. Alternatively, feel free to use another development environment of your choice.
First, a package.json that declares the build tooling and runtime dependencies:
{
"name": "ai-client-imagegen",
"private": true,
"license": "GPL-2.0-or-later",
"dependencies": {
"@wordpress/api-fetch": "^7.41.0",
"@wordpress/i18n": "^6.14.0"
},
"devDependencies": {
"@wordpress/env": "^10.27.0",
"@wordpress/scripts": "^31.0.0",
"typescript": "^5.8.2"
},
"scripts": {
"build": "wp-scripts build",
"wp-env": "wp-env"
}
}
Second, a .wp-env.json file so you can spin up a local WordPress 7.0 environment with the plugin already active using wp-env:
{
"core": "https://wordpress.org/wordpress-7.0.zip",
"plugins": [ "." ]
}
With these files in place, install dependencies and build:
npm install
npm run build
This runs @wordpress/scripts, which compiles src/index.ts into build/index.js and generates the build/index.asset.php dependency manifest. The PHP side does not need a build step — the AI Client is part of WordPress Core.
To test the plugin, start the local environment:
npm run wp-env start
This launches a Docker-based WordPress site at http://localhost:8888 (default credentials: admin / password) with the plugin already active. From there:
- Go to Settings > Connectors and configure an AI provider that supports image generation.
- Navigate to Media > Library.
- Click the “Generate Image File” button, enter a prompt, and generate an image.
- Once you’re happy with an image, click the Save to Media Library button. Optionally, customize the file name to use for the image.
If you see a notice instead of the button, your configured provider does not support image generation — check the Connectors settings.
Wrapping up
Looking back, the AI integration is small
If you review the plugin code, you might be surprised by how little of it is actually about AI. The prompt builder function in includes/prompt.php is about 10 lines. The REST callback that calls generate_image_result() is a handful more. The rest is standard WordPress development — REST API registration, media handling, script enqueuing, permission checks.
That’s the point. The WordPress AI Client handles the complexity of provider communication, authentication, model selection, and response normalization. Your plugin focuses on its own purpose, and the AI part is just another API call.
Going further
This article covered image generation, but this is only a basic starting point. For example, you could also allow editing an existing image with AI, e.g. where the user asks for further changes to a generated image. The prompt function for that could look almost the same as the one for generating images, only that you would in addition pass the existing image file to it:
use WordPress\AiClient\Files\DTO\File;
use WordPress\AiClient\Files\Enums\FileTypeEnum;
use WordPress\AiClient\Files\Enums\MediaOrientationEnum;
function aicig_get_image_editing_prompt( string $prompt, File $image_file, string $orientation = '' ): WP_AI_Client_Prompt_Builder {
$builder = wp_ai_client_prompt()
->with_text( $prompt )
->with_file( $image_file )
->as_output_file_type( FileTypeEnum::inline() );
if ( $orientation ) {
$builder->as_output_media_orientation( MediaOrientationEnum::from( $orientation ) );
}
return $builder;
}
The with_file() call passes the source image as a File DTO. The AI Client takes care of routing this to a provider and model that supports image editing.
I encourage you to take this plugin further: Maybe add the above prompt function and integrate it into the image generation REST endpoint (you only need to add an optional parameter to pass an existing image and call the above aicig_get_image_editing_prompt() function conditionally). And then explore how you could adjust the frontend UI to make this use-case intuitive.
There are so many more ideas to explore even for this seemingly simple feature. Here are some to explore:
- You could consider expanding the image generation (or editing) behavior so that it generates multiple image variations for the user’s prompt, where the user could then select their favorite.
- You may have noticed there’s a file name input field before uploading an image to the media library. Currently, that file name field is programmatically populated, which doesn’t lead to very useful file names. You could use the AI Client’s text generation abilities to generate a more descriptive file name based on what the image actually displays.
- You could integrate with existing images from your media library, e.g. show a prompt text field and a button to ask AI to edit an existing image.
If you are curious about exploring a professional end-to-end plugin that offers AI driven features similar to this one based on the new WordPress AI client, I encourage you to take a look at the official WordPress AI plugin.
You see, the abilities with AI are endless. While a simple image generation example like the one we built here is cool, it only scratches the surface. We can use AI to really bring user experience to another level.
But with this article, you hopefully have a better idea now on how to get started with using everything the WordPress AI Client has to offer. You can explore the plugin source code on GitHub: wptrainingteam/ai-client-imagegen.
Props @psykro @juanmaguitar and @bph for review and proofreading.
Leave a Reply