Weesperstraat 61-105
1018 VN Amsterdam
The Netherlands

AI / Vibe Coding in Drupal: A Practical Example

AI as a programming partner
28 Sep, 2026 Drupal
drupal-vibe-coding-header-english.

A request from a product owner: their Drupal website has a content type called Event. The editorial team wants to display a block on the homepage showing the next three events.

The initial request and AI prompt

“Create a Drupal block that displays the next three events, sorted by start date. Give each event a title, date, and link. The events must also be published. Also take recurring events into account. Please do this 'programmatically', not in Views, because I expect more complexity and external integrations later in this development process.”

Instead of first working out all the code themselves, a Drupal developer can use AI as a programming partner. This is vibe coding: you mainly describe what you want to achieve, have AI generate an initial implementation, and continue developing based on testing and feedback.

Step 1 – The Drupal Task for AI

An initial prompt could be:

I am working with Drupal 11.

I have a content type called 'Event' with the fields:
- field_start_date
- field_location

Create a custom module called 'event_teaser' with a block plugin.

The block must:
- retrieve the 3 next upcoming published events;
- sort by field_start_date ascending;
- display the title, start date, and location;
- display a link to the node;
- display no events if there are no future events.

Use Drupal coding standards and dependency injection where relevant.
Provide the required files and code.

AI could then suggest a module structure such as:

modules/custom/event_teaser/
├── event_teaser.info.yml
├── event_teaser.module
└── src/
    └── Plugin/
        └── Block/
            └── UpcomingEventsBlock.php

Step 2 – The Initial Code

A simplified block plugin could initially look something like this, generated by AI:

<?php

namespace Drupal\event_teaser\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides an Upcoming Events block.
 *
 * @Block(
 *   id = "upcoming_events",
 *   admin_label = @Translation("Upcoming events")
 * )
 */
class UpcomingEventsBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected EntityTypeManagerInterface $entityTypeManager;

  /**
   * Constructs the block.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    EntityTypeManagerInterface $entity_type_manager,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_type.manager'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    $storage = $this->entityTypeManager
      ->getStorage('node');

    $query = $storage->getQuery()
      ->accessCheck(TRUE)
      ->condition('type', 'evenement')
      ->condition('status', 1)
      ->condition('field_start_date', date('Y-m-d\TH:i:s'), '>=')
      ->sort('field_start_date', 'ASC')
      ->range(0, 3);

    $nids = $query->execute();

    $nodes = $storage->loadMultiple($nids);

    return [
      '#theme' => 'item_list',
      '#items' => array_map(
        fn($node) => [
          '#markup' => $node->toLink()->toString(),
        ],
        $nodes
      ),
    ];
  }

}

The vibe coding, however, is not just about generating this code. The interesting part starts afterward.


Step 3 – Testing

The developer installs the module:

drush en event_teaser
drush cr

The block can then be placed in a Drupal region in the backend so that it becomes visible on the frontend.

The frontend result might look like this:

Upcoming events

Drupal Meetup Amsterdam
October 15, 2026

Web Accessibility Workshop
October 22, 2026

Open Source Conference
November 4, 2026

But during testing and code review, things immediately stand out:

  • Drupal's date formatter should be used instead of date() ;
  • the location is missing;
  • why is getQuery() being used here instead of an entityQuery?
  • and what the bleep are fn($node) and array_map() doing here?

But also:

  • is the dependency injection implemented correctly?
  • what about code security?
  • and much more (see below).

You can also run the code through the Drupal Coder module for a review.


Step 4 – Giving Feedback to AI

Instead of rewriting the entire implementation themselves, the Drupal developer provides the AI specific feedback:

The first version works, but I want three changes:

1. Use Drupal's date formatter instead of date().
2. Display field_location underneath the title.
3. Use a separate Twig template file instead of #markup.
4. Why are "fn($node)" and `array_map' here?

Update the module and provide only the files that need to be changed.

AI can then suggest a Twig template:

event_teaser/
├── templates/
│   └── event-teaser-item.html.twig
└── src/
    └── Plugin/
        └── Block/
            └── UpcomingEventsBlock.php

For example:

<article class="event-teaser">
  <h3>
    <a href="{{ url }}">{{ title }}</a>
  </h3>

  <time datetime="{{ datetime }}">
    {{ date }}
  </time>

  {% if location %}
    <div class="event-teaser__location">
      {{ location }}
    </div>
  {% endif %}
</article>

Step 5 – Discovering a Problem

During code review, the Drupal developer notices that the query:

->condition(
  'field_start_date',
  date('Y-m-d\TH:i:s'),
  '>='
)

may not correctly match the actual storage format of the date field.

This is an important part of vibe coding:

AI generates the code, but the Drupal developer remains responsible for its correctness.

The Drupal developer must be able to test and manually correct the code, because AI can still hallucinate quite significantly.

The developer can therefore ask AI:

Check this query specifically for Drupal 11 and a
datetime field.

Explain which format Drupal uses internally for
field_start_date and adjust the query.

Also indicate which assumptions you are making.

And, we need to define the twig file, and it's variables in hook_theme().

AI can then help correct the implementation, but the developer subsequently verifies the solution against the actual Drupal configuration and test data.


Step 6 – An Additional Improvement

After the functional version is complete, a new requirement comes in:

“Can we make the number of events configurable?”

The developer asks:

Make the block configurable.

The administrator must be able to choose in the block configuration
how many events are displayed, with 3 as the default.

Use Drupal Form API and save the configuration
as block configuration.

AI then generates the required file with functions blockForm(), blockSubmit(), and build().

As a result, the developer does not need to know every piece of Drupal API code by heart. Instead, they primarily direct the desired behaviour and evaluate the generated implementation.

Here again, the Drupal developer must be able to analyze whether the generated code in blockForm(), blockSubmit(), and build() is correct.

For example, by looking up 'manually written' Drupal documentation. For example this blog you can help verify the generated Drupal block code.


What Makes This 'Vibe Coding'?

In a traditional approach, the developer might:

  1. look up the Drupal Block API documentation;
  2. create the plugin structure;
  3. write the dependency injection;
  4. build the Entity Query;
  5. create a Twig template;
  6. write the configuration form;
  7. test and debug.

Drupal Console and Drush previously helped with this, also did a lot of documentation. But vibe coding shifts part of this work into a conversation with AI:

Developer
    │
    │  "I want this behavior"
    ▼
AI
    │
    │  code + proposal
    ▼
Developer
    │
    │  test
    ▼
Problem / feedback
    │
    └──────────────► AI
                       │
                       ▼
                   improved code

The developer therefore works more as a director and reviewer, and less as someone who types every line from scratch.


What Should You Pay Extra Attention to in Drupal?

Vibe coding is especially powerful when the developer continues to carefully follow Drupal best practices.

For example, pay attention to:

  • Security — check input, permissions, and output.
  • Drupal coding standards — do not automatically assume generated code is correct.
  • Upgradability — verify that the APIs being used actually match the Drupal version.
  • Dependency injection — avoid unnecessary use of global services.
  • Configuration management — make sure settings are exportable.
  • Cacheability — blocks and entity queries must be cached correctly.
  • Access checking — for all 'Routes' and 'Entity Queries'.
  • Render arrays — avoid unnecessary HTML through #markup.
  • Twig escaping — let Drupal handle escaping of output.
  • .. and many other Drupal best practices

The Core Idea

A good Drupal vibe coding workflow is therefore not:

“AI, build my Drupal website.”

But rather:

“I describe the desired behavior → AI creates an initial implementation → I test → AI helps me improve it → I review and accept the final code.”

The main advantage lies in the shorter feedback loop. The Drupal developer can move more quickly from an idea to working Drupal code, while knowledge of Drupal architecture, security, and maintainability remains very important.

Full disclosure: this blog was also vibe coded :)

AI Vibe coding
Written by Joris Snoek | Sep 28, 2026
Drupal project? Let's Talk

Please tell me all about your project!
Mail , or send a message:

Got some more time?

Related content

09 May 2023 Drupal

Save time, frustration, and potential content loss.

Read more →

07 Jun 2021 Drupal

Programmatically in multiple Views

Last month we implemented the&nbsp;'Resources' page on iias.asia, as you can see: you can filter the Resource nodes on this page in the right of the screen. This is a Drupal View with exposed filters,&nbsp;which&nbsp;are placed via a block via Twig Tweak module. There is a 'Region' Filter and a …

Read more →

02 Jun 2021 Drupal

If you need a temporary buffer in Drupal to save data to,&nbsp;Drupal's tempstore service might come in handy. We used it for example in a webshop module, where users didn't have a saved address yet and also had to option to never save their address permanently (because of gdpr).

Read more →