Using #weight to control element orderfor Drupal 11 , 10 , 9 , and 8

Last updated :  

Form elements render in the order they're defined in the array — usually. #weight is the escape hatch for when "usually" isn't good enough: reordering elements added by different code (your module plus another module's hook_form_alter()) without needing to control the literal array insertion order at all.

What you'll learn in this lesson

  • Why array order alone isn't always reliable for final rendering order
  • The weight scale, and why smaller numbers sort first
  • Why #weight matters most when multiple modules alter the same form

Basic reordering

$form['email'] = [
  '#type' => 'email',
  '#title' => $this->t('Email'),
  '#weight' => 10,
];
$form['name'] = [
  '#type' => 'textfield',
  '#title' => $this->t('Name'),
  '#weight' => 5,
];
// Renders name first (weight 5), then email (weight 10) —
// regardless of the order they were added to $form above.
Lighter (smaller/more negative) weights sort first — a physical scale metaphor. Think of it as literal weight: lighter elements "float" toward the top, heavier ones sink toward the bottom. Elements without an explicit #weight default to 0, and elements sharing the same weight fall back to their original array order as a tiebreaker.

Where #weight actually earns its keep

// hook_form_alter() adding a field from an entirely different module —
// array order here is whatever order hooks happened to fire in,
// not something you control directly.
function othermodule_form_mymodule_contact_form_alter(&$form, $form_state) {
  $form['newsletter_opt_in'] = [
    '#type' => 'checkbox',
    '#title' => t('Subscribe to our newsletter'),
    '#weight' => 15, // Explicitly placed after the original fields.
  ];
}

This is the real reason #weight exists: when a form gets assembled from multiple sources (your own buildForm() plus any number of other modules' alter hooks), literal array insertion order becomes unpredictable and fragile. Weight gives every element an explicit, portable position regardless of which code added it or in what order hooks fired.

Weighting the actions container too

$form['actions'] = [
  '#type' => 'actions',
  '#weight' => 100, // Keeps submit button reliably at the very bottom,
                     // even as other altered fields get added above it.
];

Quick check: your form's fields are added in your own buildForm() with no explicit weights, and another module's hook_form_alter() adds a field with #weight => -50. Where does that altered field render? If you said before all your own fields (since they default to weight 0, and -50 is lighter/smaller), you've understood exactly why an unweighted form is vulnerable to being reordered unexpectedly by an alter hook elsewhere.

Key takeaways

  • Elements without an explicit #weight default to 0; smaller/more negative weights render first.
  • Array insertion order is a fallback tiebreaker for equal weights, not the primary ordering mechanism.
  • Weight matters most when a form is assembled from multiple sources (your code plus other modules' alter hooks) where array order isn't something any single piece of code fully controls.

Coming up next

You've controlled ordering. Next, back to validation basics: making a field genuinely mandatory with #required.