how to create an AJAX field widget I'm trying to create a field widget which is a collection of multiple fields. I'm using hook_field_widget_info() and hook_field_widget_form(), something like this:

function module_field_widget_form(&amp;$form, &amp;$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $element += array(
    '#type' =&gt; 'container',
    '#element_validate' =&gt; array('custom_validation_function'),
  );

  $options = array('some values here');
  $element['existing'] = array(
    '#type' =&gt; 'select',
    '#title' =&gt; 'Recipient &amp; delivery address',
    '#options' =&gt; $options,
    '#ajax' =&gt; array(
      'wrapper' =&gt; $wrapper_id,
      'callback' =&gt; '_my_custom_ajax_callback'
    )
  );
  $element['new'] = array(
    '#prefix' =&gt; '&lt;div id="' . $wrapper_id . '"&gt;',
    '#suffix' =&gt; '&lt;/div&gt;',
  );

  $new_entity = entity_create('entity_type', array('type' =&gt; 'bundle')); 

  field_attach_form('entity_type', $new_entity, $element['new'], $form_state);

  return $element;
}

function _my_custom_ajax_callback($form, &amp;$form_state) {
  return $form['new'];
}


The problem is that in the ajax callback the $form array is different than the one in the hook_field_widget_form(). In the ajax callback the form array is the entire form structure, not just the one defining that widget...

Any ideas?
I've tried to do this a few times and never really got very far (for the reasons you've already mentioned).

The way I found to do it instead was to install the Field Collection module (and Field Collection Table for a better UX), and use that to group the fields I need into a single field, which is represented as a single widget.

Then I just hook into the particular form that's showing the field and add the AJAX that way. As coincidence would have it I've just done exactly this a few hours ago. I know this is out of context but here's a (slightly anonymised) dump of the code I used; hopefully it'll give you a place to start if you choose to go down this route:

function MYMODULE_form_directory_listing_node_form_alter(&amp;$form, &amp;$form_state, $form_id) {
  $selected_country_tid = $form['field_address'][LANGUAGE_NONE][0]['field_country'][LANGUAGE_NONE]['#default_value'][0];

  $form['field_address'][LANGUAGE_NONE][0]['field_country'][LANGUAGE_NONE]['#ajax'] = array(
    'wrapper' =&gt; 'directory-form-county-wrapper',
    'callback' =&gt; 'MYMODULE_form_directory_listing_country_ajax'
  );

  $form['field_address'][LANGUAGE_NONE][0]['field_county'][LANGUAGE_NONE]['#prefix'] = '&lt;div id="directory-form-county-wrapper"&gt;';
  $form['field_address'][LANGUAGE_NONE][0]['field_county'][LANGUAGE_NONE]['#suffix'] = '&lt;/div&gt;';

  $selected_country_term = taxonomy_term_load($selected_country_tid);

  $options = array();
  foreach (_MYMODULE_get_country_counties($selected_country_term) as $county) {
    $options[$county-&gt;tid] = $county-&gt;name;
  }

  $form['field_address'][LANGUAGE_NONE][0]['field_county'][LANGUAGE_NONE]['#options'] = $options;
}


function MYMODULE_form_directory_listing_country_ajax(&amp;$form, &amp;$form_state) {
  return $form['field_address'][LANGUAGE_NONE][0]['field_county'][LANGUAGE_NONE];
}


field_address in the above example is the Field Collection field.