Subscribe to Our Mailing List and Stay Up-to-Date! Subscribe

ACF Tutorial: Building Custom Post Types with Advanced Custom Fields

Advanced Custom Fields transforms WordPress custom post types from basic content containers into feature-rich data structures through intuitive field group interfaces. From text fields and image uploads to relationship fields and repeaters, ACF enables complex content management without custom database tables. This comprehensive tutorial teaches ACF installation, field group creation, custom post type registration, field display techniques, and query integration building professional WordPress applications with ACF.

Installing Advanced Custom Fields

Install ACF Plugin:

  1. Plugins → Add New
  2. Search “Advanced Custom Fields”
  3. Install and activate ACF (free) or ACF PRO

ACF Free vs PRO:

Free includes:

  • Basic field types
  • Field groups
  • Location rules

PRO adds:

  • Repeater fields
  • Flexible content
  • Gallery fields
  • Clone fields
  • Options pages

Registering Custom Post Type

Register Portfolio CPT (functions.php):

function mytheme_register_portfolio() {
    register_post_type('portfolio', array(
        'labels' => array(
            'name'          => __('Portfolio', 'mytheme'),
            'singular_name' => __('Portfolio Item', 'mytheme'),
            'add_new'       => __('Add New', 'mytheme'),
            'add_new_item'  => __('Add New Portfolio Item', 'mytheme'),
            'edit_item'     => __('Edit Portfolio Item', 'mytheme'),
            'new_item'      => __('New Portfolio Item', 'mytheme'),
            'view_item'     => __('View Portfolio Item', 'mytheme'),
            'search_items'  => __('Search Portfolio', 'mytheme'),
        ),
        'public'       => true,
        'has_archive'  => true,
        'rewrite'      => array('slug' => 'portfolio'),
        'supports'     => array('title', 'editor', 'thumbnail', 'excerpt'),
        'menu_icon'    => 'dashicons-portfolio',
        'show_in_rest' => true,
    ));
}
add_action('init', 'mytheme_register_portfolio');

Creating Field Groups

Add Field Group via ACF UI:

  1. Custom Fields → Add New
  2. Title: “Portfolio Fields”
  3. Add fields (see below)
  4. Location Rules: Post Type = Portfolio
  5. Publish

Field Configuration Example:

Client Name (Text):

  • Field Label: Client Name
  • Field Name: client_name
  • Field Type: Text
  • Required: Yes

Project URL (URL):

  • Field Label: Project URL
  • Field Name: project_url
  • Field Type: URL

Project Date (Date Picker):

  • Field Label: Project Date
  • Field Name: project_date
  • Field Type: Date Picker
  • Display Format: d/m/Y
  • Return Format: Y-m-d

Project Images (Gallery – PRO):

  • Field Label: Project Images
  • Field Name: project_images
  • Field Type: Gallery
  • Min/Max: Min 1, Max 10

Technologies Used (Checkbox):

  • Field Label: Technologies
  • Field Name: technologies
  • Field Type: Checkbox
  • Choices:
    • wordpress : WordPress
    • php : PHP
    • javascript : JavaScript
    • react : React

Common Field Types

Text Field:

Field Type: Text
Used for: Short text (names, titles)
Returns: String

Textarea:

Field Type: Textarea
Used for: Long text (descriptions)
Returns: String

Number:

Field Type: Number
Used for: Numeric values
Returns: Integer/Float

Email:

Field Type: Email
Used for: Email addresses
Returns: String (email)

URL:

Field Type: URL
Used for: Web addresses
Returns: String (URL)

Image:

Field Type: Image
Return Format: Array/URL/ID
Returns: Image data

File:

Field Type: File
Return Format: Array/URL/ID
Returns: File data

Select:

Field Type: Select
Used for: Dropdown selection
Returns: Selected value(s)

Checkbox:

Field Type: Checkbox
Used for: Multiple selections
Returns: Array of values

Radio Button:

Field Type: Radio Button
Used for: Single selection
Returns: Selected value

True/False:

Field Type: True/False
Used for: On/off toggle
Returns: Boolean (1/0)

Relationship (PRO):

Field Type: Relationship
Used for: Link to other posts
Returns: Array of post objects

Repeater (PRO):

Field Type: Repeater
Used for: Repeating field sets
Returns: Array of field groups

Displaying ACF Fields in Templates

Single Portfolio Template (single-portfolio.php):

<?php get_header(); ?>

<div class="portfolio-single">
    <?php while (have_posts()) : the_post(); ?>

        <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
            <header class="entry-header">
                <h1 class="entry-title"><?php the_title(); ?></h1>

                <?php
                // Get ACF fields
                $client_name = get_field('client_name');
                $project_url = get_field('project_url');
                $project_date = get_field('project_date');
                $technologies = get_field('technologies');
                ?>

                <?php if ($client_name) : ?>
                    <p class="client-name">
                        <strong>Client:</strong> <?php echo esc_html($client_name); ?>
                    </p>
                <?php endif; ?>

                <?php if ($project_date) : ?>
                    <p class="project-date">
                        <strong>Date:</strong> <?php echo esc_html(date('F Y', strtotime($project_date))); ?>
                    </p>
                <?php endif; ?>

                <?php if ($project_url) : ?>
                    <p class="project-url">
                        <a href="<?php echo esc_url($project_url); ?>" target="_blank">
                            View Live Project
                        </a>
                    </p>
                <?php endif; ?>

                <?php if ($technologies) : ?>
                    <div class="technologies">
                        <strong>Technologies:</strong>
                        <ul>
                            <?php foreach ($technologies as $tech) : ?>
                                <li><?php echo esc_html($tech); ?></li>
                            <?php endforeach; ?>
                        </ul>
                    </div>
                <?php endif; ?>
            </header>

            <div class="entry-content">
                <?php the_content(); ?>
            </div>

            <?php
            // Display gallery (ACF PRO)
            $images = get_field('project_images');
            if ($images) : ?>
                <div class="project-gallery">
                    <?php foreach ($images as $image) : ?>
                        <div class="gallery-item">
                            <a href="<?php echo esc_url($image['url']); ?>" data-lightbox="portfolio">
                                <img src="<?php echo esc_url($image['sizes']['medium']); ?>"
                                     alt="<?php echo esc_attr($image['alt']); ?>" />
                            </a>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </article>

    <?php endwhile; ?>
</div>

<?php get_footer(); ?>

Archive Template

Portfolio Archive (archive-portfolio.php):

<?php get_header(); ?>

<div class="portfolio-archive">
    <header class="page-header">
        <h1>Portfolio</h1>
    </header>

    <?php if (have_posts()) : ?>
        <div class="portfolio-grid">
            <?php while (have_posts()) : the_post(); ?>

                <article id="post-<?php the_ID(); ?>" <?php post_class('portfolio-item'); ?>>
                    <?php if (has_post_thumbnail()) : ?>
                        <div class="portfolio-thumbnail">
                            <a href="<?php the_permalink(); ?>">
                                <?php the_post_thumbnail('medium'); ?>
                            </a>
                        </div>
                    <?php endif; ?>

                    <div class="portfolio-content">
                        <h2 class="entry-title">
                            <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
                        </h2>

                        <?php
                        $client_name = get_field('client_name');
                        if ($client_name) :
                            ?>
                            <p class="client">Client: <?php echo esc_html($client_name); ?></p>
                        <?php endif; ?>

                        <div class="entry-excerpt">
                            <?php the_excerpt(); ?>
                        </div>

                        <a href="<?php the_permalink(); ?>" class="read-more">
                            View Project
                        </a>
                    </div>
                </article>

            <?php endwhile; ?>
        </div>

        <?php the_posts_pagination(); ?>

    <?php else : ?>
        <p>No portfolio items found.</p>
    <?php endif; ?>
</div>

<?php get_footer(); ?>

Querying Posts with ACF Meta

Query by ACF Field Value:

<?php
// Query portfolio items by client name
$args = array(
    'post_type'  => 'portfolio',
    'meta_key'   => 'client_name',
    'meta_value' => 'Acme Corporation',
);

$portfolio_query = new WP_Query($args);

if ($portfolio_query->have_posts()) :
    while ($portfolio_query->have_posts()) :
        $portfolio_query->the_post();
        the_title('<h2>', '</h2>');
    endwhile;
    wp_reset_postdata();
endif;
?>

Query with Meta Comparison:

<?php
// Get portfolio items from 2024 or later
$args = array(
    'post_type'  => 'portfolio',
    'meta_query' => array(
        array(
            'key'     => 'project_date',
            'value'   => '2024-01-01',
            'compare' => '>=',
            'type'    => 'DATE',
        ),
    ),
);

$recent_portfolio = new WP_Query($args);
?>

Multiple Meta Queries:

<?php
$args = array(
    'post_type'  => 'portfolio',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'key'     => 'client_name',
            'value'   => 'Acme',
            'compare' => 'LIKE',
        ),
        array(
            'key'     => 'project_date',
            'value'   => '2024-01-01',
            'compare' => '>=',
            'type'    => 'DATE',
        ),
    ),
);

$filtered_portfolio = new WP_Query($args);
?>

Programmatic Field Registration

Register Fields via PHP (functions.php):

function mytheme_register_acf_fields() {
    if (function_exists('acf_add_local_field_group')) {
        acf_add_local_field_group(array(
            'key'    => 'group_portfolio',
            'title'  => 'Portfolio Fields',
            'fields' => array(
                array(
                    'key'   => 'field_client_name',
                    'label' => 'Client Name',
                    'name'  => 'client_name',
                    'type'  => 'text',
                    'required' => 1,
                ),
                array(
                    'key'   => 'field_project_url',
                    'label' => 'Project URL',
                    'name'  => 'project_url',
                    'type'  => 'url',
                ),
                array(
                    'key'   => 'field_project_date',
                    'label' => 'Project Date',
                    'name'  => 'project_date',
                    'type'  => 'date_picker',
                    'display_format' => 'd/m/Y',
                    'return_format'  => 'Y-m-d',
                ),
                array(
                    'key'   => 'field_technologies',
                    'label' => 'Technologies Used',
                    'name'  => 'technologies',
                    'type'  => 'checkbox',
                    'choices' => array(
                        'wordpress'  => 'WordPress',
                        'php'        => 'PHP',
                        'javascript' => 'JavaScript',
                        'react'      => 'React',
                    ),
                ),
            ),
            'location' => array(
                array(
                    array(
                        'param'    => 'post_type',
                        'operator' => '==',
                        'value'    => 'portfolio',
                    ),
                ),
            ),
        ));
    }
}
add_action('acf/init', 'mytheme_register_acf_fields');

Helper Functions

Check if Field Has Value:

<?php
if (get_field('client_name')) {
    echo 'Client: ' . esc_html(get_field('client_name'));
}
?>

Field Shorthand:

<?php
// Get and echo in one function
the_field('client_name');

// Equivalent to:
echo get_field('client_name');
?>

Get Field from Specific Post:

<?php
$client = get_field('client_name', 123); // Post ID 123
?>

Conclusion

Advanced Custom Fields transforms WordPress custom post types into robust content management systems through intuitive field group interfaces eliminating custom database development. Register custom post types with register_post_type(), create field groups via ACF admin interface, display fields in templates using get_field() and the_field(), query posts by meta values with WP_Query meta_query parameters, and optionally register fields programmatically with acf_add_local_field_group(). ACF enables complex data structures maintaining WordPress simplicity and flexibility.

  1. ACF Documentation
  2. ACF Field Types
  3. get_field() Reference
  4. WP_Query Meta Parameters
  5. Register Post Type

Call to Action

Custom fields need reliable backups. Backup Copilot Pro protects your WordPress ACF configurations and custom post type data automatically. Safeguard your content structures—start your free 30-day trial today!