How to Register a Custom Post Type in WordPress (Step-by-Step)
The complete register_post_type() walkthrough, including the rewrite-rules gotcha that causes a confusing 404 every time.
The functions.php file is where WordPress theme customization actually happens — but it's also the easiest place to make a mistake that takes down the entire site. Here's how to write custom functions safely and correctly.
If you're using a third-party theme, put your custom code in a child theme's functions.php, or better, in a small site-specific plugin. A theme update will silently wipe out anything you added directly to the parent theme.
<?php
// Always start with the opening tag, never close it at the end of the file
// (a trailing whitespace after ?> is a classic cause of "headers already sent" errors)
function mytheme_enqueue_styles() {
wp_enqueue_style('mytheme-style', get_stylesheet_uri());
}
add_action('wp_enqueue_scripts', 'mytheme_enqueue_styles');
This is the single most important WordPress concept to understand. An action lets you run code AT a specific point (nothing is returned):
add_action('wp_footer', function () {
echo '<!-- Custom footer script -->';
});
A filter lets you modify a value as it passes through (you must return something):
add_filter('the_title', function ($title) {
return $title . ' — ' . get_bloginfo('name');
});
// Add a custom excerpt length
add_filter('excerpt_length', fn() => 20);
// Add a custom image size
add_action('after_setup_theme', function () {
add_image_size('card-thumb', 400, 250, true);
});
// Disable Gutenberg block editor for a specific post type
add_filter('use_block_editor_for_post_type', function ($use, $post_type) {
return $post_type === 'testimonial' ? false : $use;
}, 10, 2);
function_exists() before redeclaring anything that might already exist.mytheme_, myplugin_) to avoid collisions with plugins.functions.php can white-screen the entire site instantly.Once actions and filters click, most of what looks like "WordPress magic" in plugins turns out to be exactly this pattern, used a few hundred times.
The complete register_post_type() walkthrough, including the rewrite-rules gotcha that causes a confusing 404 every time.
Field groups, repeaters, displaying ACF data in templates, and registering fields in code so they ship with your theme.
The mental model behind every WordPress plugin and theme — and how to create your own hooks so others can extend your code too.