Drupal 11 Custom Form Workflows

Drupal 11 Custom Form Workflows

Drupal 11 Custom Form Workflows
Anandaakrishnan G A
20 April, 2025

In Drupal 11, forms can do much more than just collect a few fields. At Arudhra IT Techs, we build multi-step workflows, dynamic field changes, and secure file uploads — all without a single page reload. Here's our blueprint for modern, AJAX-powered form systems.

Drupal 11 Custom Form Workflows: Multi-Step, AJAX Validation, and File Upload Logic

Want to build a multi-step application form? A survey with conditional logic? Or a secure file upload wizard? With Drupal 11’s Form API + AJAX + FormState workflows — we can do it all.

📦 Step 1: Basic Form Structure

Let’s create a form class that extends FormBase or ConfirmFormBase:


// src/Form/MultiStepForm.php
class MultiStepForm extends FormBase {
  protected $step = 1;
  public function getFormId() {
    return 'multi_step_form';
  }
  public function buildForm(array $form, FormStateInterface $form_state) {
    $step = $form_state->get('step') ?? 1;
    $form_state->set('step', $step);
    if ($step === 1) {
      $form['first_name'] = [
        '#type' => 'textfield',
        '#title' => 'First Name',
        '#required' => TRUE,
      ];
      $form['next'] = [
        '#type' => 'submit',
        '#value' => 'Next',
      ];
    }
    elseif ($step === 2) {
      $form['upload_cv'] = [
        '#type' => 'managed_file',
        '#title' => 'Upload CV',
        '#upload_location' => 'public://uploads/',
        '#required' => TRUE,
      ];
      $form['prev'] = [
        '#type' => 'submit',
        '#value' => 'Back',
        '#submit' => ['::prevStep'],
      ];
      $form['submit'] = [
        '#type' => 'submit',
        '#value' => 'Submit',
      ];
    }
    return $form;
  }
  public function submitForm(array &$form, FormStateInterface $form_state) {
    if ($form_state->get('step') === 1) {
      $form_state->set('step', 2);
      $form_state->setRebuild();
    } else {
      // Final submit
      \Drupal::messenger()->addMessage('Form submitted ✅');
    }
  }
  public function prevStep(array &$form, FormStateInterface $form_state) {
    $form_state->set('step', 1);
    $form_state->setRebuild();
  }
}
    

🔁 Step 2: Enable AJAX for Instant Interaction

Add AJAX to your buttons:


$form['next'] = [
  '#type' => 'submit',
  '#value' => 'Next',
  '#ajax' => [
    'callback' => '::ajaxRebuildForm',
    'wrapper' => 'multi-step-form-wrapper',
  ],
];
$form['#prefix'] = '<div id="multi-step-form-wrapper">';
$form['#suffix'] = '</div>';
    

This lets us change steps without reloading the page!

📁 Step 3: Handle File Uploads Properly

  • Use managed_file
  • Set file_usage_add() if needed
  • Restrict extensions + size

$form['upload_cv'] = [
  '#type' => 'managed_file',
  '#title' => $this->t('Upload CV'),
  '#upload_location' => 'public://uploads',
  '#upload_validators' => [
    'file_validate_extensions' => ['pdf doc docx'],
    'file_validate_size' => [25600000], // 25MB
  ],
];
    

🧠 Real-World Use Cases

  • Vendor onboarding with dynamic step-by-step logic
  • Loan application with file upload and real-time field validation
  • Multi-language registration with conditional fields

✅ Form Validation + Conditional Logic

Use validateForm() to dynamically throw errors:


public function validateForm(array &$form, FormStateInterface $form_state) {
  if ($form_state->get('step') === 2) {
    $file = $form_state->getValue('upload_cv');
    if (empty($file)) {
      $form_state->setErrorByName('upload_cv', 'CV is required.');
    }
  }
}
    

Need a Complex Form Flow Built in Drupal 10 or 11?

We specialize in multi-step, AJAX-driven, file-uploading Drupal forms for enterprise clients — fast, secure, and fully dynamic.

Let’s architect your next smart form 🔥

Talk to Our Drupal Devs