WordPress Child Theme Development
Child theme — only proper way to customize ready-made WordPress theme without risk losing changes on parent update. If edits go directly into bought theme files, first update wipes them completely. Child theme created in 1–2 working days, but requires understanding template inheritance and hooks.
Child Theme Structure
Minimal child theme consists of two files:
wp-content/themes/my-child/
├── style.css
└── functions.php
style.css must contain header with Template:
/*
Theme Name: My Site Child
Template: twentytwentyfour
Version: 1.0.0
*/
functions.php loads parent theme styles:
<?php
add_action('wp_enqueue_scripts', function () {
wp_enqueue_style(
'parent-style',
get_template_directory_uri() . '/style.css'
);
wp_enqueue_style(
'child-style',
get_stylesheet_directory_uri() . '/style.css',
['parent-style']
);
});
Important: get_template_directory_uri() points to parent, get_stylesheet_directory_uri() — to child. Confusion here — source of half bugs.
Template Overriding
WordPress searches for template file first in child, then parent. Means: to change archive template, enough to create archive.php in child folder.
WordPress template hierarchy (simplified):
single-{post-type}.php → single.php → singular.php → index.php
category-{slug}.php → category-{id}.php → category.php → archive.php → index.php
page-{slug}.php → page-{id}.php → page.php → singular.php → index.php
Practical example: need to change template for specific category. Create category-news.php in child, no need touch parent.
Overriding Functions via Pluggable Functions
Some parent functions declared via if (!function_exists(...)). Can replace completely in functions.php:
// Parent theme does:
if (!function_exists('mytheme_header_logo')) {
function mytheme_header_logo() {
echo '<img src="' . get_template_directory_uri() . '/logo.svg">';
}
}
// In child theme override:
function mytheme_header_logo() {
$custom_logo_id = get_theme_mod('custom_logo');
echo wp_get_attachment_image($custom_logo_id, 'full', false, ['class' => 'site-logo']);
}
If function declared without function_exists check — can't override via child — use hooks.
Hooks: add_action and add_filter
Hooks — main tool to modify behavior without replacing files. remove_action and remove_filter disable parent logic:
// Remove date output from parent theme
remove_action('mytheme_entry_meta', 'mytheme_posted_on', 10);
// Add custom output with different format
add_action('mytheme_entry_meta', function () {
echo '<time>' . get_the_date('d.m.Y') . '</time>';
}, 10);
For remove_action priority (third parameter) must match original registration.
Customizer Customization
In child theme convenient to add custom Customizer settings:
add_action('customize_register', function (WP_Customize_Manager $wp_customize) {
$wp_customize->add_section('child_theme_options', [
'title' => 'Site Settings',
'priority' => 30,
]);
$wp_customize->add_setting('hero_background_color', [
'default' => '#1a1a2e',
'transport' => 'postMessage',
'sanitize_callback' => 'sanitize_hex_color',
]);
$wp_customize->add_control(
new WP_Customize_Color_Control($wp_customize, 'hero_background_color', [
'label' => 'Hero Section Background',
'section' => 'child_theme_options',
])
);
});
transport => postMessage allows preview changes without reload.
Additional CSS and Scripts
Child styles load after parent — order matters. For scripts:
add_action('wp_enqueue_scripts', function () {
wp_enqueue_script(
'child-main',
get_stylesheet_directory_uri() . '/assets/js/main.js',
['jquery'],
wp_get_theme()->get('Version'),
true
);
// Pass data from PHP to JS
wp_localize_script('child-main', 'childTheme', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('child_theme_nonce'),
]);
}, 20); // Priority 20 — after parent scripts
Versioning via wp_get_theme()->get('Version') resets browser cache.
Typical Timeframes
Basic child theme with several template overrides and custom styles — 4–8 hours. Significant template replacement and Customizer settings — 1–2 working days. Deep customization with template structure rework — 3–5 days.







