Creating high-quality content consistently is one of the biggest challenges for website owners and content managers. The Google Gemini API offers a powerful solution to this problem, and in this guide, we’ll walk you through creating a WordPress Content Generation Plugin that leverages this technology for automated content generation.

What You’ll Learn
- Setting up a WordPress Content Generation Plugin structure for Gemini integration
- Implementing the Google Gemini API for content generation
- Creating an intuitive admin interface
- Managing API responses and content formatting
- Adding draft post creation functionality
Prerequisites
Before we begin, ensure you have:
- A WordPress development environment
- Basic knowledge of PHP and WordPress plugin development
- A Google Gemini API key from Google AI Studio
- Understanding of JavaScript and jQuery
WordPress Content Generation Plugin Structure Overview
Our WordPress Content Generation Plugin follows a well-organized structure to maintain code clarity and separation of concerns. The structure includes:
wp-content/plugins/gemini-content-generator/
├── gemini-content-generator.php
├── assets/
│ ├── css/
│ │ └── admin-style.css
│ └── js/
│ └── admin-script.js
├── includes/
│ ├── class-gemini-api-handler.php
│ └── class-gemini-content-generator.php
└── templates/
├── admin-main-page.php
└── admin-settings-page.php
Step 1: Setting Up the Plugin Base (gemini-content-generator.php)
The main plugin file initializes the plugin, defines constants, and sets up the basic plugin structure. This file acts as the entry point for our plugin.
<?php
/**
* Plugin Name: WordPress Content Generation Plugin
* Description: A WordPress plugin that generates content using Google's Gemini AI
* Version: 1.0.0
* Author: Your Name
* License: GPL v2 or later
*/
if (!defined('ABSPATH')) exit;
// Define plugin constants
define('GEMINI_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('GEMINI_PLUGIN_URL', plugin_dir_url(__FILE__));
// Require necessary files
require_once GEMINI_PLUGIN_PATH . 'includes/class-gemini-content-generator.php';
class Gemini_Plugin {
private static $instance = null;
private $content_generator;
public static function getInstance() {
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// Initialize components
$this->content_generator = new Gemini_Content_Generator();
// Add menu items
add_action('admin_menu', array($this, 'add_admin_menu'));
// Add settings
add_action('admin_init', array($this, 'register_settings'));
}
public function add_admin_menu() {
add_menu_page(
'Gemini Content Generator',
'Gemini AI',
'manage_options',
'gemini-content-generator',
array($this, 'display_main_page'),
'dashicons-text-page',
30
);
add_submenu_page(
'gemini-content-generator',
'Settings',
'Settings',
'manage_options',
'gemini-content-settings',
array($this, 'display_settings_page')
);
}
public function register_settings() {
register_setting('gemini_content_settings', 'gemini_api_key');
}
public function display_main_page() {
require_once GEMINI_PLUGIN_PATH . 'templates/admin-main-page.php';
}
public function display_settings_page() {
require_once GEMINI_PLUGIN_PATH . 'templates/admin-settings-page.php';
}
}
// Initialize the plugin
function init_gemini_plugin() {
Gemini_Plugin::getInstance();
}
add_action('plugins_loaded', 'init_gemini_plugin');
Key components:
- Defines plugin constants for paths and URLs
- Implements singleton pattern for plugin instance
- Sets up admin menu and submenu pages
- Registers plugin settings
Step 2: Styling the Admin Interface (admin-style.css)
The CSS file provides styling for the admin interface, including form layouts and loading states.
/* assets/css/admin-style.css */
.gemini-form-container {
max-width: 800px;
margin: 20px 0;
}
.gemini-form-field {
margin-bottom: 20px;
}
.gemini-form-field label {
display: block;
font-weight: 600;
margin-bottom: 5px;
}
.gemini-form-field textarea {
width: 100%;
min-height: 100px;
}
.gemini-form-field select {
min-width: 200px;
}
.gemini-loading {
display: none;
padding: 20px;
text-align: center;
}
.gemini-loading .spinner {
float: none;
margin: 0 10px 0 0;
}
.gemini-generated-content {
margin-top: 20px;
padding: 15px;
background: #fff;
border: 1px solid #ddd;
border-radius: 4px;
}
.gemini-error {
color: #dc3232;
padding: 10px;
border-left: 4px solid #dc3232;
margin: 20px 0;
}
.dynamic-content {
font-family: Arial, sans-serif;
line-height: 1.6;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.section-heading {
font-size: 1.8em;
color: #1a73e8;
margin-bottom: 15px;
border-bottom: 2px solid #ddd;
padding-bottom: 5px;
}
.subsection-heading {
font-size: 1.5em;
color: #333;
margin-top: 20px;
margin-bottom: 10px;
}
.custom-list {
margin: 10px 0;
padding-left: 20px;
}
.custom-list li {
margin-bottom: 5px;
}
.custom-ordered-list {
margin: 10px 0;
padding-left: 20px;
}
Key styling features:
- Responsive form container with proper spacing
- Loading animation styles
- Error and success message formatting
- Dynamic content display formatting
Step 3: Adding Interactive Features (admin-script.js)
The JavaScript file handles:
- Form submissions via AJAX
- Loading state management
- Response handling
- Draft post creation
// assets/js/admin-script.js
jQuery(document).ready(function($) {
$('#gemini-generate-form').on('submit', function(e) {
e.preventDefault();
const $form = $(this);
const $submitButton = $form.find('button[type="submit"]');
const $loading = $('.gemini-loading');
const $result = $('.gemini-generated-content');
// Show loading
$loading.show();
$submitButton.prop('disabled', true);
// Collect form data
const formData = {
action: 'generate_content',
prompt: $('#content-prompt').val(),
tone: $('#content-tone').val(),
length: $('#content-length').val(),
nonce: geminiAjax.nonce
};
// Make AJAX request
$.post(geminiAjax.ajaxurl, formData, function(response) {
if (response.success) {
$result.html(response.data.content);
// Add Create Draft button
const createDraftButton = $('<button>', {
class: 'button button-secondary',
text: 'Create Draft Post',
style: 'margin-top: 15px;'
}).on('click', function(e) {
e.preventDefault();
const $button = $(this);
$button.prop('disabled', true).text('Creating draft...');
$.ajax({
url: geminiAjax.ajaxurl,
type: 'POST',
data: {
action: 'create_draft_post',
content: $result.html(),
title: $('#content-prompt').val(),
nonce: geminiAjax.nonce
},
success: function(response) {
$button.prop('disabled', false).text('Create Draft Post');
if (response.success) {
const successMessage = $('<div>', {
class: 'notice notice-success',
html: `<p>Draft created successfully!
<a href="${response.data.edit_url}" target="_blank">Edit Post</a> |
<a href="${response.data.preview_url}" target="_blank">Preview Post</a></p>`
});
$('button.button-secondary').after(successMessage);
} else {
const errorMessage = $('<div>', {
class: 'notice notice-error',
html: `<p>Error: ${response.data.message}</p>`
});
$('button.button-secondary').after(errorMessage);
}
},
error: function(xhr, status, error) {
$button.prop('disabled', false).text('Create Draft Post');
const errorMessage = $('<div>', {
class: 'notice notice-error',
html: `<p>Error creating draft: ${error}</p>`
});
$result.prepend(errorMessage);
}
});
});
$result.append(createDraftButton);
} else {
$result.html('<div class="notice notice-error"><p>' + response.data.message + '</p></div>');
}
})
.fail(function() {
$result.html('<div class="notice notice-error"><p>Error connecting to server</p></div>');
})
.always(function() {
$loading.hide();
$submitButton.prop('disabled', false);
});
});
});
Key functionalities:
- AJAX form submission with loading states
- Dynamic content updates
- Error handling and display
- Draft post creation with success/error notifications
Step 4: API Handler Implementation (class-gemini-api-handler.php)
This class manages all communications with the Google Gemini API.
<?php
// includes/class-gemini-api-handler.php
if (!defined('ABSPATH')) exit;
class Gemini_API_Handler {
private $api_key;
private $api_url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent';
public function __construct() {
$this->api_key = get_option('gemini_api_key');
}
public function generate_content($prompt, $tone, $length) {
if (empty($this->api_key)) {
return new WP_Error('no_api_key', 'Gemini API key is not configured');
}
// Enhance prompt based on tone and length
$enhanced_prompt = $this->enhance_prompt($prompt, $tone, $length);
// Prepare the request
$response = wp_remote_post(
$this->api_url . '?key=' . $this->api_key,
array(
'headers' => array(
'Content-Type' => 'application/json',
),
'body' => json_encode(array(
'contents' => array(
array(
'parts' => array(
array(
'text' => $enhanced_prompt
)
)
)
),
'generationConfig' => array(
'temperature' => 0.7,
'topK' => 40,
'topP' => 0.95,
'maxOutputTokens' => $this->get_max_tokens($length),
),
)),
'timeout' => 30,
)
);
if (is_wp_error($response)) {
return $response;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (empty($body) || isset($body['error'])) {
return new WP_Error(
'api_error',
isset($body['error']['message']) ? $body['error']['message'] : 'Unknown API error'
);
}
try {
return $this->format_content($this->parse_response($body));
} catch (Exception $e) {
return new WP_Error('parse_error', $e->getMessage());
}
}
private function enhance_prompt($prompt, $tone, $length) {
$tone_instructions = array(
'professional' => 'Write in a professional and business-appropriate tone.',
'casual' => 'Write in a casual, conversational tone.',
'friendly' => 'Write in a warm and approachable tone.',
'formal' => 'Write in a formal and academic tone.'
);
return sprintf(
"Please write content with the following requirements:\n" .
"Topic/Brief: %s\n" .
"Tone: %s\n" .
"Additional Instructions: Write well-structured content suitable for a website or blog. " .
"Use markdown formatting with ## for main headings and ### for subheadings. " .
"Use proper spacing between sections.",
$prompt,
$tone_instructions[$tone]
);
}
private function get_max_tokens($length) {
$token_limits = array(
'short' => 250,
'medium' => 500,
'long' => 800
);
return $token_limits[$length] ?? 500;
}
private function parse_response($response) {
if (!isset($response['candidates'][0]['content']['parts'][0]['text'])) {
throw new Exception('Unexpected API response format');
}
return $response['candidates'][0]['content']['parts'][0]['text'];
}
private function format_content($content) {
// Basic sanitization while preserving markdown
$content = wp_kses_post($content);
$content = preg_replace('/\n#\n/', "\n", $content);
$content = preg_replace('/^#\s*$/m', '', $content);
if (function_exists('wpautop')) {
$content = preg_replace('/^## (.*?)$/m', '<h2>$1</h2>', $content);
$content = preg_replace('/^### (.*?)$/m', '<h3>$1</h3>', $content);
$content = wpautop($content);
$content = preg_replace('/\*\*(.*?)\*\*/', '<strong>$1</strong>', $content);
$content = preg_replace('/- (.*?)\n/', '<ul><li>$1</li></ul>', $content);
$content = preg_replace('/<\/ul>\s*<ul>/', '', $content);
$content = preg_replace('/<p>\s*<h([23])/', '<h$1', $content);
$content = preg_replace('/<\/h([23])>\s*<\/p>/', '</h$1>', $content);
}
return $content;
}
}
Key features:
- API request formatting and sending
- Response parsing and error handling
- Content enhancement based on tone and length
- Output formatting and sanitization
Step 5: Core Plugin Class (class-gemini-content-generator.php)
The main class handling plugin functionality.
<?php
if (!defined('ABSPATH')) exit;
class Gemini_Content_Generator {
private $api_handler;
public function __construct() {
require_once GEMINI_PLUGIN_PATH . 'includes/class-gemini-api-handler.php';
$this->api_handler = new Gemini_API_Handler();
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
add_action('wp_ajax_generate_content', array($this, 'handle_generate_content'));
add_action('wp_ajax_create_draft_post', array($this, 'handle_create_draft_post'));
}
public function enqueue_admin_assets($hook) {
if (strpos($hook, 'gemini-content') === false) {
return;
}
wp_enqueue_style(
'gemini-admin-style',
GEMINI_PLUGIN_URL . 'assets/css/admin-style.css',
array(),
'1.0.0'
);
wp_enqueue_script(
'gemini-admin-script',
GEMINI_PLUGIN_URL . 'assets/js/admin-script.js',
array('jquery'),
'1.0.0',
true
);
wp_localize_script('gemini-admin-script', 'geminiAjax', array(
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('gemini_generate_nonce') // Updated nonce name
));
}
public function handle_generate_content() {
check_ajax_referer('gemini_generate_nonce', 'nonce');
if (!current_user_can('edit_posts')) {
wp_send_json_error(array('message' => 'Permission denied'));
}
$prompt = sanitize_textarea_field($_POST['prompt'] ?? '');
$tone = sanitize_text_field($_POST['tone'] ?? 'professional');
$length = sanitize_text_field($_POST['length'] ?? 'medium');
if (empty($prompt)) {
wp_send_json_error(array('message' => 'Please provide a content prompt.'));
}
// Generate content using API
$content = $this->api_handler->generate_content($prompt, $tone, $length);
if (is_wp_error($content)) {
wp_send_json_error(array(
'message' => $content->get_error_message()
));
}
wp_send_json_success(array('content' => $content));
}
public function handle_create_draft_post() {
check_ajax_referer('gemini_generate_nonce', 'nonce');
if (!current_user_can('edit_posts')) {
wp_send_json_error(['message' => 'Permission denied']);
return;
}
// Get and sanitize the content
$content = isset($_POST['content']) ? wp_kses_post(stripslashes($_POST['content'])) : '';
$title = isset($_POST['title']) ? sanitize_text_field(stripslashes($_POST['title'])) : 'Generated Content ' . date('Y-m-d H:i:s');
if (empty($content)) {
wp_send_json_error(['message' => 'No content provided']);
return;
}
// Create post object
$post_data = array(
'post_title' => $title,
'post_content' => $content,
'post_status' => 'draft',
'post_author' => get_current_user_id(),
'post_type' => 'post'
);
// Insert the post into the database
$post_id = wp_insert_post($post_data);
if ($post_id && !is_wp_error($post_id)) {
$edit_url = admin_url('post.php?post=' . $post_id . '&action=edit');
$preview_url = get_preview_post_link($post_id);
wp_send_json_success([
'message' => 'Draft created successfully',
'post_id' => $post_id,
'edit_url' => $edit_url,
'preview_url' => $preview_url
]);
} else {
wp_send_json_error([
'message' => is_wp_error($post_id) ? $post_id->get_error_message() : 'Failed to create draft'
]);
}
}
}
Important functions:
- Asset enqueuing
- AJAX handlers for content generation
- Draft post creation
- Security implementations
Step 6: Admin Interface Templates
Main Page Template (admin-main-page.php)
Creates the content generation interface.
<?php
// templates/admin-main-page.php
if (!defined('ABSPATH')) exit;
?>
<div class="wrap">
<h1>Gemini Content Generator</h1>
<?php if (!get_option('gemini_api_key')): ?>
<div class="notice notice-warning">
<p>Please configure your Gemini API key in the <a href="<?php echo admin_url('admin.php?page=gemini-content-settings'); ?>">settings page</a> first.</p>
</div>
<?php else: ?>
<div class="gemini-form-container">
<form id="gemini-generate-form" method="post">
<div class="gemini-form-field">
<label for="content-prompt">What would you like to write about?</label>
<textarea
id="content-prompt"
name="prompt"
class="large-text"
rows="4"
required
placeholder="Enter your content brief here. Be specific about what you want to generate."
></textarea>
</div>
<div class="gemini-form-field">
<label for="content-tone">Content Tone</label>
<select id="content-tone" name="tone" class="regular-text">
<option value="professional">Professional</option>
<option value="casual">Casual</option>
<option value="friendly">Friendly</option>
<option value="formal">Formal</option>
</select>
</div>
<div class="gemini-form-field">
<label for="content-length">Content Length</label>
<select id="content-length" name="length" class="regular-text">
<option value="short">Short (~150 words)</option>
<option value="medium">Medium (~300 words)</option>
<option value="long">Long (~500 words)</option>
</select>
</div>
<div class="gemini-form-field">
<?php wp_nonce_field('gemini_generate_nonce', 'gemini_nonce'); ?>
<button type="submit" class="button button-primary">Generate Content</button>
</div>
</form>
<div class="gemini-loading">
<span class="spinner is-active"></span> Generating content...
</div>
<div class="gemini-generated-content"></div>
</div>
<?php endif; ?>
</div>
Features:
- Content generation form
- Tone and length selection
- Loading indicators
- Generated content display
Settings Page Template (admin-settings-page.php)
Manages WordPress Content Generation Plugin configuration.
<?php
// templates/admin-settings-page.php
if (!defined('ABSPATH')) exit;
?>
<div class="wrap">
<h1>Gemini Content Generator Settings</h1>
<form method="post" action="options.php">
<?php
settings_fields('gemini_content_settings');
do_settings_sections('gemini_content_settings');
?>
<table class="form-table">
<tr>
<th scope="row">
<label for="gemini_api_key">Gemini API Key</label>
</th>
<td>
<input type="password"
id="gemini_api_key"
name="gemini_api_key"
value="<?php echo esc_attr(get_option('gemini_api_key')); ?>"
class="regular-text">
<p class="description">
Enter your Gemini API key here. You can get one from
<a href="https://makersuite.google.com/app/apikey" target="_blank">Google AI Studio</a>.
</p>
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
Includes:
- API key management
- Security nonce implementation
- Settings form with proper validation
How to use
- Go to plugins and activate the WordPress Content Generation Plugin
- Then go to Gemini Ai from the sidebar of the WordPress admin
- First, go to settings and save your API key (If not then create new one)
- Then start creating content
Security Implementations
The plugin implements several security measures:
- Nonce verification for all AJAX requests
- Capability checking for user permissions
- Input sanitization and validation
- Secure API key storage
Testing and Deployment
Before deploying the plugin:
- Test the API integration thoroughly
- Verify error handling
- Check compatibility with different WordPress versions
- Ensure proper sanitization of inputs and outputs
Future Enhancements
Consider adding these features in future updates:
- Content templates
- Bulk content generation
- Custom post type support
- Content scheduling
- SEO optimization suggestions
Conclusion
You now have a fully functional WordPress Content Generation Plugin that leverages the power of Google’s Gemini AI for content generation. This tool can significantly streamline your content creation workflow while maintaining quality and relevance.