Displaying Drupal Forms via Routes or Blocksfor Drupal 11 , 10 , 9 , and 8

Last updated :  

A form class by itself doesn't appear anywhere — SimpleForm is just a PHP class sitting in your module until something tells Drupal where a visitor can actually reach it. This lesson covers the two standard ways to put a form in front of a user: give it its own route (a dedicated page), or place it as a block (reusable, droppable into any region).

What you'll learn in this lesson

  • How to give a form its own page via a route
  • How to wrap the same form in a block plugin so it can appear anywhere
  • Why the form class itself never needs to change between these two approaches

Giving a form its own route

The most direct way to display a form: a route whose controller callback is the form class itself, using Drupal's built-in _form key.

form_api_example.simple_form:
  path: '/examples/form-api-example/simple-form'
  defaults:
    _form: '\Drupal\form_api_example\Form\SimpleForm'
    _title: 'Simple Form Example'
  requirements:
    _permission: 'access content'

Notice this is _form, not _controller — Drupal's routing system has a dedicated shortcut specifically for forms, which handles instantiating the form class, calling \Drupal::formBuilder()->buildForm(), and rendering the result, all without you writing a controller at all.

Placing a form as a block

To let a site builder place the same form in a sidebar, a footer, or any other block region, wrap it in a block plugin whose build() method delegates to the form builder:

/**
 * @Block(
 *   id = "simple_form_block",
 *   admin_label = @Translation("Simple Form block")
 * )
 */
class SimpleFormBlock extends BlockBase {

  public function build() {
    return \Drupal::formBuilder()->getForm('Drupal\form_api_example\Form\SimpleForm');
  }

}

This is the exact same getForm() call from the previous lesson — the form class has no idea whether it's being rendered on its own page or embedded in a block. That's the whole point of separating "how the form works" (the class) from "where the form appears" (the route or block wrapping it).

One form, many places, zero duplication: because both approaches call into the exact same SimpleForm class, you never have two copies of your validation or submission logic to keep in sync. Add the form to a route AND a block simultaneously if you genuinely need both — they share every line of behavior.

Quick check: you need the exact same contact form to appear both on its own dedicated page and as a sidebar block on every other page. Do you need two separate form classes? (No — one class, referenced from a routing.yml entry for the page and from a block plugin's build() method for the sidebar. Same class, two presentation contexts.)

Key takeaways

  • The _form routing key is a dedicated shortcut for form routes — it handles instantiation and rendering without a separate controller.
  • A block plugin can display any form by calling \Drupal::formBuilder()->getForm() in its build() method, using the form class's fully-qualified name as a string.
  • The same form class works identically whether it's reached via a route or embedded in a block — presentation and form logic are fully decoupled.

Coming up next

You've now built and displayed a form. The next lesson zooms into the render-array properties that define each individual field inside it — starting with the three you'll use constantly: #type, #title, and #default_value.