Creating a WordPress child theme is straightforward, but building one that performs well, stays maintainable, and survives parent theme updates requires discipline. This checklist covers the technical details that separate a quick override from a production-ready child theme—directory structure, enqueue order, performance patterns, and the hooks that matter.
Pre-Development Planning
Choose the Right Parent Theme
Before writing a single line of code, verify your parent theme is worth extending. Check that the parent theme uses standard WordPress hooks, follows coding standards, and receives regular updates. Themes built with page builders or proprietary frameworks often fight child theme customizations. Look for themes that use get_template_directory() and get_template_directory_uri() correctly—these functions ensure child theme overrides work as expected.
Document Your Customization Scope
List exactly what you plan to override: templates, styles, functions, or all three. This determines your file structure and helps you avoid scope creep. A child theme that only modifies colors needs different architecture than one that rebuilds the entire header.
Set Up Version Control
Initialize a Git repository before creating any files. Track your child theme separately from WordPress core and the parent theme. Tag releases that correspond to production deployments. This practice saves hours when a customization breaks and you need to bisect commits.
Directory Structure and Required Files
Create the Minimum Viable Structure
Your child theme needs exactly two files to function:
wp-content/themes/parent-theme-child/
├── style.css
└── functions.php
The style.css header must include these exact fields:
/*
Theme Name: Parent Theme Child
Template: parent-theme
Version: 1.0.0
*/
The Template value must match the parent theme's directory name exactly—case sensitive, hyphens and all.
Organize Assets by Type
As your child theme grows, adopt a clear directory structure:
parent-theme-child/
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
├── inc/
│ ├── custom-functions.php
│ └── template-tags.php
├── template-parts/
├── templates/
├── functions.php
└── style.css
This structure separates concerns and makes it obvious where new files belong. The inc/ directory holds PHP logic, template-parts/ stores reusable template fragments, and templates/ contains full page templates.
Enqueue Styles and Scripts Correctly
Never Import Parent Styles in style.css
The old @import method is slow and makes it impossible to control load order. Always enqueue parent and child styles in functions.php:
function child_theme_enqueue_styles() {
$parent_style = 'parent-theme-style';
wp_enqueue_style(
$parent_style,
get_template_directory_uri() . '/style.css'
);
wp_enqueue_style(
'child-theme-style',
get_stylesheet_directory_uri() . '/style.css',
array( $parent_style ),
wp_get_theme()->get('Version')
);
}
add_action( 'wp_enqueue_scripts', 'child_theme_enqueue_styles' );
Note the dependency array that ensures parent styles load first, and the version parameter tied to your theme version for cache busting.
Set Enqueue Priorities Deliberately
When your child theme styles conflict with parent styles despite correct dependencies, check enqueue priorities. The default priority is 10; if the parent theme enqueues at a different priority, match or exceed it:
add_action( 'wp_enqueue_scripts', 'child_theme_enqueue_styles', 15 );
Enqueue Conditional Assets
Load scripts and styles only where needed. Enqueuing assets globally wastes bandwidth and slows page rendering:
function child_theme_conditional_assets() {
if ( is_front_page() ) {
wp_enqueue_script(
'homepage-slider',
get_stylesheet_directory_uri() . '/assets/js/slider.js',
array('jquery'),
'1.0.0',
true
);
}
}
add_action( 'wp_enqueue_scripts', 'child_theme_conditional_assets' );
The final true parameter loads the script in the footer, improving page load performance.
Template Hierarchy and Overrides
Copy Only What You Modify
Do not copy the entire parent theme into your child theme "just in case." Every copied template is a maintenance burden. When the parent theme updates its structure, your child theme becomes a liability.
Copy a parent template to your child theme only when you need to modify it. Place it in the same relative path—if the parent has template-parts/content-post.php, create template-parts/content-post.php in your child theme.
Use get_template_part() for Reusable Fragments
When you modify a template, extract reusable sections into template parts:
get_template_part( 'template-parts/content', get_post_type() );
This call looks first in the child theme, then falls back to the parent. It keeps templates DRY and makes future updates easier.
Override Page Templates Selectively
Page templates (files with Template Name: headers) in your child theme appear in the WordPress admin template selector. Use them for genuinely different layouts, not minor variations:
<?php
/*
Template Name: Landing Page
Template Post Type: page
*/
get_header();
// Custom layout
get_footer();
Functions.php Best Practices
Never Copy Parent functions.php
Unlike templates, functions.php in a child theme does not replace the parent's version—WordPress loads both. Copying parent functions causes fatal "function already defined" errors.
Use Hooks, Not Direct Overrides
When the parent theme defines a function you want to modify, check if it's wrapped in a conditional:
if ( ! function_exists( 'parent_function' ) ) {
function parent_function() {
// Code
}
}
If so, define your version in the child theme's functions.php and it takes precedence. For functions without conditionals, use remove_action() or remove_filter() to detach the parent's hook, then attach your own:
function child_theme_modify_excerpt_length( $length ) {
return 30;
}
remove_filter( 'excerpt_length', 'parent_excerpt_length' );
add_filter( 'excerpt_length', 'child_theme_modify_excerpt_length' );
Namespace Your Functions
Prefix every function, class, and constant with your child theme's slug to avoid collisions:
function mytheme_custom_widget() {
// Code
}
This discipline prevents conflicts with plugins and other themes.
Load Modular Includes
Keep functions.php lean by loading functionality from the inc/ directory:
require get_stylesheet_directory() . '/inc/custom-functions.php';
require get_stylesheet_directory() . '/inc/template-tags.php';
This organization makes code easier to navigate and test.
Performance and Optimization
Minimize CSS Specificity
Child theme styles cascade after parent styles, so you can usually override without !important. When you find yourself reaching for !important, the parent selector is likely too specific. Inspect the element and match or slightly exceed the parent's specificity:
/* Parent */
.site-header .nav-menu li a { }
/* Child - match specificity */
.site-header .nav-menu li a { }
Defer or Async Non-Critical Scripts
For scripts that don't affect above-the-fold content, add async or defer attributes:
function child_theme_add_async_attribute( $tag, $handle ) {
if ( 'analytics-script' !== $handle ) {
return $tag;
}
return str_replace( ' src', ' async src', $tag );
}
add_filter( 'script_loader_tag', 'child_theme_add_async_attribute', 10, 2 );
Use async for independent scripts and defer when execution order matters.
Remove Unused Parent Assets
If your child theme replaces parent functionality, dequeue the parent's now-unused assets:
function child_theme_dequeue_parent_assets() {
wp_dequeue_style( 'parent-unused-style' );
wp_dequeue_script( 'parent-unused-script' );
}
add_action( 'wp_enqueue_scripts', 'child_theme_dequeue_parent_assets', 20 );
Run this action at a later priority (higher number) than the parent's enqueue action.
Optimize Template Queries
When adding custom queries to templates, always reset post data and avoid nested loops when possible:
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
// Template code
}
}
wp_reset_postdata();
Skipping wp_reset_postdata() causes template tags to display wrong content later in the template.
Hooks and Filters
Use Core Hooks Before Custom Ones
WordPress provides dozens of action and filter hooks. Before creating a custom solution, check if a core hook exists. Common hooks for child themes include:
wp_enqueue_scripts- Add styles and scriptsafter_setup_theme- Register theme supports and menuswidgets_init- Register sidebarsexcerpt_lengthandexcerpt_more- Modify excerptsbody_classandpost_class- Add custom classes
Document Hook Priorities
When attaching to parent theme hooks, document the priority and why you chose it:
// Priority 15 runs after parent theme's priority 10 setup
add_action( 'after_setup_theme', 'child_theme_setup', 15 );
This comment prevents confusion when debugging hook execution order.
Remove Parent Hooks Carefully
Before removing a parent theme hook, verify it won't break functionality:
function child_theme_remove_parent_hook() {
remove_action( 'wp_footer', 'parent_footer_function' );
}
add_action( 'after_setup_theme', 'child_theme_remove_parent_hook' );
Run removals in after_setup_theme or init to ensure the parent's hooks are already registered.
Translation and Internationalization
Set a Child Theme Text Domain
If your child theme adds translatable strings, define a text domain in style.css:
/*
Text Domain: parent-theme-child
*/
Then use it in all translatable strings:
__( 'Read More', 'parent-theme-child' );
Load Child Theme Translations
If you provide translation files, load them in functions.php:
function child_theme_load_textdomain() {
load_child_theme_textdomain(
'parent-theme-child',
get_stylesheet_directory() . '/languages'
);
}
add_action( 'after_setup_theme', 'child_theme_load_textdomain' );
Testing and Quality Assurance
Test With Theme Check Plugin
Before deployment, run the Theme Check plugin against your child theme. It catches common mistakes like missing text domains, incorrect template tags, and deprecated functions.
Verify Template Hierarchy
Enable SCRIPT_DEBUG in wp-config.php and use the Query Monitor plugin to confirm which templates load for each page type. This reveals when child theme overrides fail to take effect.
Test Parent Theme Updates
Before applying parent theme updates in production, test them in staging with your child theme active. Check for broken layouts, PHP errors, and missing functionality. Parent theme updates can change hook names, function signatures, and CSS classes your child theme depends on.
Check Mobile Responsiveness
Your child theme's CSS changes must respect the parent's responsive breakpoints. Test on actual devices or use browser dev tools to verify mobile, tablet, and desktop layouts.
Deployment Checklist
Remove Development Code
Before deploying, remove or comment out debugging statements:
// Remove these
error_log();
var_dump();
print_r();
Minify Production Assets
Minify CSS and JavaScript files for production. Keep unminified versions in version control and enqueue the minified versions:
$suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
wp_enqueue_style(
'child-theme-style',
get_stylesheet_directory_uri() . "/assets/css/style{$suffix}.css"
);
Version Your Release
Update the version number in style.css before each deployment. This forces browsers to download updated assets and helps track which version runs in production.
Document Customizations
Maintain a CHANGELOG.md or README.md that lists what the child theme overrides and why. When someone else (or future you) touches the code, this documentation explains the customization rationale.
Conclusion
A well-structured child theme extends a parent theme's functionality without creating a maintenance nightmare. Follow this checklist: plan your scope, organize files logically, enqueue assets correctly, override only what you modify, use hooks instead of copying functions, optimize for performance, and test thoroughly. Document your work and version your releases. These practices turn a simple child theme into a reliable, maintainable foundation that survives parent theme updates and team handoffs. The discipline you bring to child theme creation directly determines how much time you spend fixing it later.
