The group field in ACF (SCF) bundles related fields into a single box. The basic way to pull values out is “receive it as an array and name the key”—the get_field('company')['tel'] shape.
<?php $company = get_field('company'); ?>
<p>Phone: <?php echo esc_html($company['tel']); ?></p>TipsACF or SCF—which one should I install?Still deciding between ACF and SCF? Start hereA group field is a box that bundles input fields
A group exists to turn fields that would otherwise scatter into a meaningful cluster. Using company details as an example, it looks like this.
Company details (group: company)
├── tel +1 555-0123
├── address 123 Main Street…
└── hours 10:00–19:00The visible benefit is that the admin screen groups them under a heading. The other practical win is that field names stop colliding. If you want to use the name tel for both shop details and a contact address, splitting them into groups lets tel inside shop and tel inside contact coexist.
Approach 1: receive it as an array (recommended)
get_field() returns a group as an associative array. The keys are the sub field names.
<?php $company = get_field('company'); ?>
<?php if ($company) : ?>
<table class="company">
<tr><th>Phone</th><td><?php echo esc_html($company['tel']); ?></td></tr>
<tr><th>Address</th><td><?php echo esc_html($company['address']); ?></td></tr>
<tr><th>Hours</th><td><?php echo esc_html($company['hours']); ?></td></tr>
</table>
<?php endif; ?>Because you receive it into a variable once and reuse it, this is the shortest approach on a page that outputs several values from the same group.
When a sub field might be empty, add an individual if.
<?php if (!empty($company['tel'])) : ?>
<p>Phone: <?php echo esc_html($company['tel']); ?></p>
<?php endif; ?>Approach 2: enter a loop with have_rows
A group can also be seen as “a repeater with a single row,” so have_rows() handles it too. With this shape you pull the contents out with get_sub_field().
<?php if (have_rows('company')) : ?>
<?php while (have_rows('company')) : the_row(); ?>
<p>Phone: <?php the_sub_field('tel'); ?></p>
<p>Address: <?php the_sub_field('address'); ?></p>
<?php endwhile; ?>
<?php endif; ?>The advantage is that it matches how you write repeater fields. It also cuts down on typo-an-array-key-and-never-notice accidents. Either is fine—but pick one style and stick to it within a page and it reads better.
Images inside a group
When you put an image field inside a group, you pull it out the same way. Match the approach to the return value setting.
<?php $hero = get_field('hero'); ?>
<?php if ($hero) : ?>
<section class="hero">
<?php echo wp_get_attachment_image($hero['image'], 'full'); ?> <!-- return value = image ID -->
<h1><?php echo esc_html($hero['catch']); ?></h1>
</section>
<?php endif; ?>If the return value is an image array, walk one more level down, like $hero['image']['url'].
Nested groups
When you put a group inside a group, you just keep walking the array.
<?php $s = get_field('shop'); ?>
<p><?php echo esc_html($s['sns']['twitter']); ?></p>With the have_rows() style, you enter have_rows('sns') once more on the inside. Deep hierarchies make templates hard to read, so keeping nesting to two levels is a good rule of thumb in real projects.
A group inside a repeater field
When a repeater row contains a group, what get_sub_field() gives you back is an array.
<?php while (have_rows('staff')) : the_row(); ?>
<?php $profile = get_sub_field('profile'); ?>
<p><?php echo esc_html($profile['name']); ?> (<?php echo esc_html($profile['role']); ?>)</p>
<?php endwhile; ?>Summary
- A group field is a box that bundles related input fields—and it stops field names from colliding
- The basic way to pull values is
get_field('box')['contents'].have_rows()plusget_sub_field()works too - One set means a group; many means a repeater—choose by count
- Passing a sub field name straight to
get_field()won’t work. Open the box, then the contents
A tidy admin screen makes the site kinder to whoever you’re asking to keep it updated.