Theme Settings and Options
Learning Objectives
- Understand WordPress Options API and Settings API
- Create custom theme options pages
- Build settings forms with proper validation
- Implement different field types for settings
- Store and retrieve theme options efficiently
- Create tabbed options interfaces
- Export and import theme settings
- Integrate with the Customizer API
Introduction to Theme Settings
Theme settings and options allow users to configure various aspects of their theme without editing code. While the Customizer provides a live preview interface, theme options pages offer more complex configuration capabilities for advanced settings.
Important Note
WordPress APIs for Theme Settings
Options API
- Simple key-value storage
- Direct database interaction
- No built-in UI
- Functions: get_option(), update_option()
- Best for: Simple data storage
Settings API
- Built on Options API
- Form handling & validation
- Security (nonces, capability checks)
- Admin page integration
- Best for: Complex settings pages
Settings API Workflow
1
Register Settings
2
Add Sections
3
Add Fields
4
Create Page
5
Render Form
Creating a Theme Options Page
// Step 1: Add admin menu
add_action('admin_menu', 'mytheme_add_admin_menu');
function mytheme_add_admin_menu() {
add_theme_page(
'Theme Options', // Page title
'Theme Options', // Menu title
'manage_options', // Capability
'mytheme-options', // Menu slug
'mytheme_options_page' // Callback function
);
}
// Step 2: Register settings
add_action('admin_init', 'mytheme_settings_init');
function mytheme_settings_init() {
// Register a setting
register_setting('mytheme_options_group', 'mytheme_options');
// Add a section
add_settings_section(
'mytheme_general_section',
'General Settings',
'mytheme_general_section_callback',
'mytheme-options'
);
// Add fields
add_settings_field(
'mytheme_logo_url',
'Logo URL',
'mytheme_logo_url_render',
'mytheme-options',
'mytheme_general_section'
);
add_settings_field(
'mytheme_footer_text',
'Footer Text',
'mytheme_footer_text_render',
'mytheme-options',
'mytheme_general_section'
);
}
// Section callback
function mytheme_general_section_callback() {
echo '<p>Configure general theme settings below:</p>';
}
// Field callbacks
function mytheme_logo_url_render() {
$options = get_option('mytheme_options');
$value = isset($options['logo_url']) ? $options['logo_url'] : '';
?>
<input type="url" name="mytheme_options[logo_url]"
value="<?php echo esc_attr($value); ?>"
class="regular-text" />
<p class="description">Enter your logo URL</p>
<?php
}
function mytheme_footer_text_render() {
$options = get_option('mytheme_options');
$value = isset($options['footer_text']) ? $options['footer_text'] : '';
?>
<textarea name="mytheme_options[footer_text]"
rows="4" cols="50"><?php echo esc_textarea($value); ?></textarea>
<p class="description">Text to display in the footer</p>
<?php
}
// Step 3: Create the options page
function mytheme_options_page() {
?>
<div class="wrap">
<h1>Theme Options</h1>
<form action="options.php" method="post">
<?php
settings_fields('mytheme_options_group');
do_settings_sections('mytheme-options');
submit_button();
?>
</form>
</div>
<?php
}
Creating a Tabbed Options Interface
Tabbed Interface Code
function mytheme_tabbed_options_page() {
$active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'general';
?>
<div class="wrap">
<h1>Theme Options</h1>
<h2 class="nav-tab-wrapper">
<a href="?page=mytheme-options&tab=general"
class="nav-tab <?php echo $active_tab == 'general' ? 'nav-tab-active' : ''; ?>">
General
</a>
<a href="?page=mytheme-options&tab=layout"
class="nav-tab <?php echo $active_tab == 'layout' ? 'nav-tab-active' : ''; ?>">
Layout
</a>
<a href="?page=mytheme-options&tab=typography"
class="nav-tab <?php echo $active_tab == 'typography' ? 'nav-tab-active' : ''; ?>">
Typography
</a>
</h2>
<form method="post" action="options.php">
<?php
if ($active_tab == 'general') {
settings_fields('mytheme_general_options');
do_settings_sections('mytheme-general');
} elseif ($active_tab == 'layout') {
settings_fields('mytheme_layout_options');
do_settings_sections('mytheme-layout');
} elseif ($active_tab == 'typography') {
settings_fields('mytheme_typography_options');
do_settings_sections('mytheme-typography');
}
submit_button();
?>
</form>
</div>
<?php
}
Common Field Types
Text Field
Textarea
Checkbox
Radio Button
Select Dropdown
Color Picker
Media Upload
Date Picker
Field Implementation Examples
// Checkbox field
function mytheme_checkbox_field_render() {
$options = get_option('mytheme_options');
$checked = isset($options['show_sidebar']) ? $options['show_sidebar'] : 0;
?>
<input type="checkbox" name="mytheme_options[show_sidebar]"
value="1" <?php checked(1, $checked); ?> />
<label>Show sidebar on all pages</label>
<?php
}
// Radio buttons
function mytheme_radio_field_render() {
$options = get_option('mytheme_options');
$layout = isset($options['layout']) ? $options['layout'] : 'right';
?>
<label>
<input type="radio" name="mytheme_options[layout]"
value="left" <?php checked('left', $layout); ?> />
Left Sidebar
</label><br>
<label>
<input type="radio" name="mytheme_options[layout]"
value="right" <?php checked('right', $layout); ?> />
Right Sidebar
</label><br>
<label>
<input type="radio" name="mytheme_options[layout]"
value="full" <?php checked('full', $layout); ?> />
Full Width
</label>
<?php
}
// Select dropdown
function mytheme_select_field_render() {
$options = get_option('mytheme_options');
$font = isset($options['font_family']) ? $options['font_family'] : 'arial';
?>
<select name="mytheme_options[font_family]">
<option value="arial" <?php selected('arial', $font); ?>>Arial</option>
<option value="georgia" <?php selected('georgia', $font); ?>>Georgia</option>
<option value="helvetica" <?php selected('helvetica', $font); ?>>Helvetica</option>
<option value="times" <?php selected('times', $font); ?>>Times New Roman</option>
</select>
<?php
}
// Media upload field
function mytheme_media_field_render() {
$options = get_option('mytheme_options');
$image = isset($options['header_image']) ? $options['header_image'] : '';
?>
<input type="text" name="mytheme_options[header_image]"
id="header_image" value="<?php echo esc_url($image); ?>"
class="regular-text" />
<input type="button" class="button button-secondary"
value="Upload Image" id="upload_image_button" />
<script>
jQuery(document).ready(function($) {
$('#upload_image_button').click(function() {
var mediaUploader = wp.media({
title: 'Select Image',
button: { text: 'Use This Image' },
multiple: false
});
mediaUploader.on('select', function() {
var attachment = mediaUploader.state().get('selection').first().toJSON();
$('#header_image').val(attachment.url);
});
mediaUploader.open();
});
});
</script>
<?php
}
⚠️ Validation and Sanitization
Always validate and sanitize user input before saving to the database:
// Register setting with sanitization callback
register_setting(
'mytheme_options_group',
'mytheme_options',
'mytheme_sanitize_options'
);
function mytheme_sanitize_options($input) {
$sanitized = array();
// Sanitize text field
if (isset($input['footer_text'])) {
$sanitized['footer_text'] = sanitize_text_field($input['footer_text']);
}
// Sanitize URL
if (isset($input['logo_url'])) {
$sanitized['logo_url'] = esc_url_raw($input['logo_url']);
}
// Sanitize checkbox
if (isset($input['show_sidebar'])) {
$sanitized['show_sidebar'] = ($input['show_sidebar'] == 1) ? 1 : 0;
}
// Sanitize select field
if (isset($input['layout'])) {
$valid_layouts = array('left', 'right', 'full');
if (in_array($input['layout'], $valid_layouts)) {
$sanitized['layout'] = $input['layout'];
} else {
$sanitized['layout'] = 'right'; // Default
}
}
// Sanitize color hex
if (isset($input['primary_color'])) {
if (preg_match('/^#[a-f0-9]{6}$/i', $input['primary_color'])) {
$sanitized['primary_color'] = $input['primary_color'];
} else {
$sanitized['primary_color'] = '#0073aa'; // Default
}
}
// Add error messages if needed
if (empty($input['footer_text'])) {
add_settings_error(
'mytheme_options',
'footer_text_error',
'Footer text cannot be empty',
'error'
);
}
return $sanitized;
}
Using Theme Options
// Get theme options
$options = get_option('mytheme_options');
// Use with defaults
$footer_text = isset($options['footer_text']) ? $options['footer_text'] : 'Copyright © 2024';
$logo_url = isset($options['logo_url']) ? $options['logo_url'] : get_template_directory_uri() . '/images/logo.png';
$show_sidebar = isset($options['show_sidebar']) ? $options['show_sidebar'] : true;
// Helper function for getting options
function mytheme_get_option($option_name, $default = '') {
$options = get_option('mytheme_options');
return isset($options[$option_name]) ? $options[$option_name] : $default;
}
// In your theme templates
<?php if (mytheme_get_option('show_sidebar', true)) : ?>
<aside class="sidebar">
<?php get_sidebar(); ?>
</aside>
<?php endif; ?>
// Output custom CSS based on options
add_action('wp_head', function() {
$primary_color = mytheme_get_option('primary_color', '#0073aa');
$font_family = mytheme_get_option('font_family', 'Arial, sans-serif');
?>
<style>
:root {
--primary-color: <?php echo esc_attr($primary_color); ?>;
--font-family: <?php echo esc_attr($font_family); ?>;
}
body {
font-family: var(--font-family);
}
a {
color: var(--primary-color);
}
.button-primary {
background-color: var(--primary-color);
}
</style>
<?php
});
Export and Import Settings
Allow users to backup and transfer theme settings:
// Export settings
function mytheme_export_settings() {
if (!isset($_GET['action']) || $_GET['action'] != 'export_settings') {
return;
}
if (!current_user_can('manage_options')) {
return;
}
$options = get_option('mytheme_options');
$json = json_encode($options);
header('Content-Type: application/json');
header('Content-Disposition: attachment; filename="theme-settings-' . date('Y-m-d') . '.json"');
echo $json;
exit;
}
add_action('admin_init', 'mytheme_export_settings');
// Import settings
function mytheme_import_settings() {
if (!isset($_POST['import_settings'])) {
return;
}
if (!current_user_can('manage_options')) {
return;
}
if (!isset($_FILES['import_file'])) {
return;
}
$file = $_FILES['import_file']['tmp_name'];
$json = file_get_contents($file);
$options = json_decode($json, true);
if ($options) {
update_option('mytheme_options', $options);
add_settings_error(
'mytheme_options',
'settings_imported',
'Settings imported successfully!',
'success'
);
}
}
add_action('admin_init', 'mytheme_import_settings');
// Add to options page
function mytheme_export_import_section() {
?>
<h2>Export/Import Settings</h2>
<h3>Export Settings</h3>
<p>Export your theme settings for backup or transfer.</p>
<a href="<?php echo admin_url('admin.php?page=mytheme-options&action=export_settings'); ?>"
class="button button-secondary">Export Settings</a>
<h3>Import Settings</h3>
<form method="post" enctype="multipart/form-data">
<input type="file" name="import_file" accept=".json" />
<?php wp_nonce_field('import_settings_nonce'); ?>
<input type="submit" name="import_settings"
value="Import Settings" class="button button-secondary" />
</form>
<?php
}
✅ Best Practices for Theme Settings
- Use Customizer when possible:For visual settings with live preview
- Group related settings:Organize into logical sections
- Provide defaults:Always have fallback values
- Validate and sanitize:Never trust user input
- Use descriptive names:Clear option keys and labels
- Cache expensive operations:Use transients for complex queries
- Document settings:Add help text and descriptions
- Version your options:Track schema changes
- Minimize database calls:Get all options at once
- Provide reset option:Allow users to restore defaults
Practice Exercise
Hands-On Practice