Adding images to anchors in the list created and fetched with wp_nav_menu

Menu items are saved as posts with the post_type “nav_menu_item”. This means you can assign postmeta to them just like you can with posts and create custom fields in the menu edit screen to enter an img url or assign a media ID.

You can follow this tutorial about adding custom meta to menu items to suit your needs. If you need help, I can throw together some code bits to get you rolling.

UPDATE
I put together this bit that allows you to drop into functions.php. Untested, but should work. It’s a different approach then the link I originally shared but it’s a method I’ve used in the past.

//Adds custom data field form
function wpse_259995_menu_custom_fields_fields( $id, $item, $depth, $args ) {

  $_key = 'img_url';
  $label = "Image URL";
  $key   = sprintf( 'menu-item-%s', $_key );
  $id    = sprintf( 'edit-%s-%s', $key, $item->ID );
  $name  = sprintf( '%s[%s]', $key, $item->ID );
  $value = get_post_meta( $item->ID, $key, true );
  $class = sprintf( 'field-%s', $_key );

  printf(
    '<p class="%5$s"><label for="%1$s">%2$s<br />
    <input type="text" id="%1$s" class="%5$s" name="%3$s" value="%4$s" /></label></p>',
    esc_attr( $id ),
    esc_html( $label ),
    esc_attr( $name ),
    esc_url( $value ),
    esc_attr( $class )
  );
}
add_action( 'wp_nav_menu_item_custom_fields', 'wpse_259995_menu_custom_fields_fields', 10, 4 );

//Save Custom Menu data
function wpse_259995_menu_custom_fields_save( $menu_id, $menu_item_db_id, $menu_item_args ) {
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
    return;
  }

  check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
  $_key = 'img_url';
  $label = "Image URL";

  $key = sprintf( 'menu-item-%s', $_key );

  // Sanitize
  if ( ! empty( $_POST[ $key ][ $menu_item_db_id ] ) ) {
    // Do some checks here...
    $value = $_POST[ $key ][ $menu_item_db_id ];
  }
  else {
    $value = null;
  }

  // Update
  if ( ! is_null( $value ) ) {
    update_post_meta( $menu_item_db_id, $key, $value );
  }
  else {
    delete_post_meta( $menu_item_db_id, $key );
  }
}
add_action( 'wp_update_nav_menu_item', 'wpse_259995_menu_custom_fields_save', 10, 3 );

//Adds toggle to show field in screen options
function wpse_259995_menu_custom_fields_columns( $columns ) {
  $fields = array('img_url' => "Imae URL");
  $columns = array_merge( $columns, $fields );
  return $columns;
}
add_filter( 'manage_nav-menus_columns', 'wpse_259995_menu_custom_fields_columns', 99 );

//Modify the element of the WP walker
function wpse_259995_walker_nav_menu_start_el_menu_with_images ($item_output, $item, $depth, $args ){
  $attr_title  = ! empty( $item->attr_title ) ? $item->attr_title : $item->title;

  $img_url = get_post_meta( $item->ID, 'menu-item-img_url', true );

  $item_output = $args->before;
  $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . '<img src="'.$img_url.'" />' . $args->link_after;
  $item_output .= $args->after;
  return $item_output;
}

Then, in your header.php (or wherever you are calling your menu):

add_filter( 'walker_nav_menu_start_el', 'wpse_259995_walker_nav_menu_start_el_menu_with_images', 10, 4 );
wp_nav_menu( /*Call your menu*/ );
remove_filter( 'walker_nav_menu_start_el', 'wpse_259995_walker_nav_menu_start_el_menu_with_images', 10, 4 );

UPDATE
I forgot that when I used this solution a few years ago it was in conjunction with this plugin that provided the hook “wp_nav_menu_item_custom_fields”
https://wordpress.org/plugins/menu-item-custom-fields/installation/

You’ll need to install that plugin (or reuse the code) to get this to work.