Customizing and Previewing Menu Item Fields in WordPress – The Right Way

WordPress provides a robust menu system, but sometimes you need to extend it with custom fields for menu items. Here’s how to do it properly:

1. Adding Custom Fields to Menu Items

Using the wp_nav_menu_item_custom_fields hook (WordPress 5.4+)

add_action('wp_nav_menu_item_custom_fields', 'add_custom_menu_fields', 10, 4);
function add_custom_menu_fields($item_id, $item, $depth, $args) {
    ?>
    <div class="field-custom_field description description-wide">
        <label for="edit-menu-item-custom-<?php echo $item_id; ?>">
            <?php _e('Custom Field'); ?><br />
            <input type="text" id="edit-menu-item-custom-<?php echo $item_id; ?>" 
                   class="widefat code edit-menu-item-custom" 
                   name="menu-item-custom[<?php echo $item_id; ?>]" 
                   value="<?php echo esc_attr($item->custom); ?>" />
        </label>
    </div>
    <?php
}

For older WordPress versions (pre-5.4)

add_filter('wp_edit_nav_menu_walker', 'custom_menu_walker', 10, 2);
function custom_menu_walker($walker, $menu_id) {
    return 'Walker_Nav_Menu_Edit_Custom';
}

// Create a custom walker
require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
class Walker_Nav_Menu_Edit_Custom extends Walker_Nav_Menu_Edit {
    function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
        parent::start_el($output, $item, $depth, $args, $id);
        
        // Add your custom fields here
        $custom_field = get_post_meta($item->ID, '_menu_item_custom', true);
        
        $output = str_replace(
            '<fieldset class="field-move hide-if-no-js">',
            '<div class="field-custom description description-wide">
                <label for="edit-menu-item-custom-' . $item->ID . '">
                    ' . __('Custom Field') . '<br />
                    <input type="text" id="edit-menu-item-custom-' . $item->ID . '" 
                           class="widefat code edit-menu-item-custom" 
                           name="menu-item-custom[' . $item->ID . ']" 
                           value="' . esc_attr($custom_field) . '" />
                </label>
            </div>
            <fieldset class="field-move hide-if-no-js">',
            $output
        );
    }
}

2. Saving Custom Fields

 add_action('wp_update_nav_menu_item', 'save_custom_menu_fields', 10, 3);
function save_custom_menu_fields($menu_id, $menu_item_db_id, $args) {
    if (isset($_REQUEST['menu-item-custom'][$menu_item_db_id])) {
        $custom_value = sanitize_text_field($_REQUEST['menu-item-custom'][$menu_item_db_id]);
        update_post_meta($menu_item_db_id, '_menu_item_custom', $custom_value);
    }
}

3. Previewing Custom Fields in the Menu
To see your custom fields in the menu preview

 add_filter('wp_setup_nav_menu_item', 'setup_custom_nav_fields');
function setup_custom_nav_fields($menu_item) {
    $menu_item->custom = get_post_meta($menu_item->ID, '_menu_item_custom', true);
    return $menu_item;
}

4. Displaying Custom Fields in Frontend

 class Custom_Walker_Nav_Menu extends Walker_Nav_Menu {
    function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
        $attributes = '';
        $description = '';
        
        !empty($item->custom) && $attributes .= ' data-custom="' . esc_attr($item->custom) . '"';
        
        $item_output = $args->before;
        $item_output .= '<a' . $attributes . ' href="' . esc_attr($item->url) . '">';
        $item_output .= $args->link_before . apply_filters('the_title', $item->title, $item->ID) . $args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;
        
        $output .= apply_filters('walker_nav_menu_start_el', $item_output, $item, $depth, $args);
    }
}

Then use your custom walker when calling wp_nav_menu():

 wp_nav_menu(array(
    'theme_location' => 'primary',
    'walker' => new Custom_Walker_Nav_Menu()
));

5. Adding JavaScript for Real-time Preview

 jQuery(document).ready(function($) {
    // Watch for changes in custom fields and update preview
    $(document).on('change', '.edit-menu-item-custom', function() {
        var input = $(this),
            menuItem = input.closest('.menu-item'),
            itemId = menuItem.find('.menu-item-data-db-id').val(),
            preview = $('#menu-item-preview-' + itemId);
            
        // Update preview element
        preview.find('.menu-item-title').attr('data-custom', input.val());
    });
});

Best Practices

  1. Always sanitize input when saving
  2. Use proper escaping when outputting values
  3. Prefix your custom field names to avoid conflicts
  4. Consider using WordPress’s built-in REST API for more complex previews
  5. Test with accessibility in mind

This approach gives you a solid foundation for extending WordPress menu items while maintaining compatibility with core functionality.

Related Posts


How to detect YouTube iframe tag in a specific WordPress post

Detecting YouTube Iframes in WordPress Posts This document outlines two primary methods for detectin...

How to Change the Active Link Color in Navigation Menu Using CSS: The Ultimate Guide

Having proper visual feedback in your website’s navigation is crucial for both user experience...

How to Fix CSP Inline Script & Style Issues in WordPress

Content Security Policy (CSP) is a crucial security layer that helps protect your WordPress site fro...

How to Fix Empty href Tags When Displaying Custom Tag Cloud as a List in WordPress

Fixing empty href tags in a custom tag cloud displayed as a list in WordPress usually involves ensur...

Recent Posts