Getting Started with Advanced Custom Fields (ACF) in WordPress
Field groups, repeaters, displaying ACF data in templates, and registering fields in code so they ship with your theme.
WordPress's built-in "Posts" are fine for a blog, but the moment you need "Products," "Testimonials," or "Team Members" with their own distinct fields and archive pages, you need a custom post type. Here's exactly how to register one properly.
Add this to your theme's functions.php (or, better, a dedicated plugin so it survives a theme switch):
function register_testimonial_post_type() {
register_post_type('testimonial', [
'labels' => [
'name' => 'Testimonials',
'singular_name' => 'Testimonial',
'add_new_item' => 'Add New Testimonial',
],
'public' => true,
'has_archive' => true,
'supports' => ['title', 'editor', 'thumbnail'],
'menu_icon' => 'dashicons-format-quote',
'rewrite' => ['slug' => 'testimonials'],
]);
}
add_action('init', 'register_testimonial_post_type');
Post types must be registered on every page load, on the init hook — not conditionally, not only in the admin. WordPress needs to know about the post type on every single request to correctly route URLs and build the admin menu.
After adding a new post type, its archive/single URLs return a 404 until WordPress regenerates its rewrite rules. Visit Settings → Permalinks and click Save once (this is a one-time step, not something to run on every page load — flushing rewrite rules on every request is a well-known performance killer).
'publicly_queryable' => true,
'show_in_nav_menus' => true,
'show_in_rest' => true, // required for Gutenberg editor + REST API access
$testimonials = new WP_Query([
'post_type' => 'testimonial',
'posts_per_page' => 6,
]);
while ($testimonials->have_posts()) : $testimonials->the_post();
the_title();
the_content();
endwhile;
wp_reset_postdata();
Custom post types are the foundation almost every real WordPress build sits on — the moment content stops being "just a blog post," this is the tool for the job.
Field groups, repeaters, displaying ACF data in templates, and registering fields in code so they ship with your theme.
Actions vs filters, safe functions.php structure, and the defensive habits that keep a single typo from taking down your site.
The mental model behind every WordPress plugin and theme — and how to create your own hooks so others can extend your code too.