Skip to content

Options Page

The Options_Page class registers ACF Options Pages from config arrays. Each page is organized into tabs (groups), and each tab’s fields are composed with ACF_Builder islands β€” the same pattern used for Flexible Content modules.

Options pages store global site settings (header navigation, footer content, social media links, API keys, etc.) accessible via get_field('field_name', 'option').


  • Tabbed layout: Groups are rendered as ACF tabs on a single options page
  • Island-composed fields: Uses ACF_Builder for consistent field definitions
  • Deterministic keys: Generated with md5() β€” safe for migrations
  • show_when support: Conditional visibility between fields
  • Submenu pages: Registered under the Terra admin menu by default

The Options Page config is typically split into separate files per tab in functions/project/config/general-options/:

functions/project/config/general-options/
β”œβ”€β”€ index.php # Merges all tabs
β”œβ”€β”€ header.php # Header navigation fields
β”œβ”€β”€ footer.php # Footer content fields
β”œβ”€β”€ site_identity.php # Logo, favicon
β”œβ”€β”€ social_media.php # Social media URLs
β”œβ”€β”€ scripts.php # Header/footer scripts
β”œβ”€β”€ api_keys.php # API credentials
└── error_404.php # 404 page content
functions/project/config/general-options/social_media.php
<?php
return [
'title' => 'Social Media',
'fields' => array_merge(
ACF_Builder::url(['name' => 'linkedin_url', 'label' => 'LinkedIn']),
ACF_Builder::url(['name' => 'twitter_url', 'label' => 'Twitter / X']),
ACF_Builder::url(['name' => 'facebook_url', 'label' => 'Facebook']),
ACF_Builder::url(['name' => 'instagram_url', 'label' => 'Instagram']),
ACF_Builder::url(['name' => 'youtube_url', 'label' => 'YouTube']),
),
];
functions/project/config/general-options/index.php
<?php
$groups = [];
foreach (glob(__DIR__ . '/*.php') as $file) {
$name = basename($file, '.php');
if ($name !== 'index') {
$groups[$name] = require $file;
}
}
return [
'page_title' => 'General Options',
'menu_slug' => 'general-options',
'parent_slug' => 'terra_dashboard',
'groups' => $groups,
];

ParameterTypeDescription
page_titlestringTitle shown in the admin page
menu_titlestringMenu label (defaults to page_title)
menu_slugstringURL slug for the page
parent_slugstringParent menu slug (e.g., 'terra_dashboard')
capabilitystringRequired capability (default: 'edit_posts')
icon_urlstringMenu icon URL (optional)
positionintMenu position (optional)
redirectboolRedirect to first child (default: false)
groupsarrayKeyed array of tab configs, each with title and fields

Access options page fields with get_field() using the 'option' post ID:

<?php
// In any template file
$linkedin = get_field('linkedin_url', 'option');
$logo = get_field('site_logo', 'option');
$nav = get_field('header_nav', 'option');
// In a loop over a repeater
$nav_items = get_field('header_navigation', 'option');
if ($nav_items) {
foreach ($nav_items as $item) {
echo '<a href="' . esc_url($item['link']['url']) . '">';
echo esc_html($item['link']['title']);
echo '</a>';
}
}

functions/project/config/general-options/header.php
<?php
return [
'title' => 'Header',
'fields' => array_merge(
ACF_Builder::repeater([
'name' => 'header_navigation',
'label' => 'Navigation Items',
'layout' => 'block',
'fields' => array_merge(
ACF_Builder::link(['name' => 'link', 'label' => 'Link']),
ACF_Builder::boolean(['name' => 'has_dropdown', 'label' => 'Has Dropdown?']),
ACF_Builder::repeater([
'name' => 'dropdown_items',
'label' => 'Dropdown Items',
'fields' => array_merge(
ACF_Builder::link(['name' => 'dropdown_link']),
ACF_Builder::image(['name' => 'dropdown_icon']),
ACF_Builder::text(['name' => 'dropdown_description', 'rows' => 2]),
),
]),
),
]),
ACF_Builder::link(['name' => 'header_cta', 'label' => 'CTA Button']),
),
];

Knowledge Check

Test your understanding of this section

Loading questions...