Custom WordPress themes require a specific file structure and setup sequence. Build on a starter theme (GeneratePress, Kadence, or Underscores) rather than from blank when possible.
Required Files
my-theme/
├── style.css # Theme header + base styles
├── functions.php # Theme setup, enqueue, hooks
├── index.php # Fallback template
├── header.php # Site header
├── footer.php # Site footer
├── page.php # Default page template
├── single.php # Single post template
├── archive.php # Archive/taxonomy template
├── 404.php # 404 template
└── template-parts/ # Reusable partials
style.css Theme Header
/*
Theme Name: My Custom Theme
Theme URI: https://example.com/theme
Author: Your Name
Author URI: https://example.com
Description: Custom theme for [client]
Version: 1.0.0
Requires at least: 6.0
Tested up to: 6.5
Requires PHP: 8.1
License: GPL v2 or later
Text Domain: my-theme
*/
functions.php Essentials
<?php
function mytheme_setup() {
add_theme_support('title-tag');
add_theme_support('post-thumbnails');
add_theme_support('html5', ['search-form', 'comment-form', 'gallery', 'caption']);
add_theme_support('responsive-embeds');
add_theme_support('editor-styles');
register_nav_menus([
'primary' => __('Primary Menu', 'my-theme'),
'footer' => __('Footer Menu', 'my-theme'),
]);
add_image_size('hero', 1920, 800, true);
add_image_size('card', 400, 300, true);
}
add_action('after_setup_theme', 'mytheme_setup');
function mytheme_scripts() {
wp_enqueue_style('mytheme-style', get_stylesheet_uri(), [], '1.0.0');
wp_enqueue_style('mytheme-main', get_template_directory_uri() . '/assets/css/main.css', [], '1.0.0');
wp_enqueue_script('mytheme-main', get_template_directory_uri() . '/assets/js/main.js', ['jquery'], '1.0.0', true);
wp_localize_script('mytheme-main', 'mythemeAjax', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('mytheme_nonce'),
]);
}
add_action('wp_enqueue_scripts', 'mytheme_scripts');
function mytheme_widgets() {
register_sidebar([
'name' => __('Sidebar', 'my-theme'),
'id' => 'sidebar-1',
'before_widget' => '<div class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widget-title">',
'after_title' => '</h3>',
]);
}
add_action('widgets_init', 'mytheme_widgets');
Version Control
Track theme with Git. Initialize in the theme folder (wp-content/themes/my-theme/), not the WordPress root. Add node_modules/, *.log, and *.DS_Store to .gitignore.