Drupal 8: Writing a Hello World Module

It's been a while since I've sat down and tried to write a module from scratch in Drupal 8. I've dabbled here and there in the various already existing modules but there's always something interesting about just trying to write that simple "Hello World" module. Sure, starting from scratch isn't really something we do all that often, but it's nice to to know how it works.

So, as part of our Learning Drupal 8 by Trial and Error blog posts, I set out to write what is probably one of the simplest tasks you could want to do with a custom module. Displaying the text "Hello World" as the primary content of the page. In the process of doing so I learned that while the general concepts are much the same as Drupal 7 — you're simply finding a way to map a URL to some PHP code that gets executed — the mechanics of it are much different in Drupal 8.

Most of the information I used while doing this was found by searching through the change records on drupal.org which provide a list and some basic documentation for most of the big changes between Drupal 7 & 8.

.info files and YAML

First up was the .info file. Which in Drupal 8 is now really a modulename.info.yml YAML file. Asside from the sytax differences between the old .ini style files and the new YAML files things are mostly the same here. Give your module a name, a description, and some other basic metadata that Drupal needs. I mostly read through these examples and looked at the .info.yml files of a few core modules for reference.

Symfony 2 routing

The other big thing for me that I knew existed, but hadn't encountered up until this point, is the new Symfony 2 based routing system. It's basically Drupal 8's version of the routing portion of what hook_menu() in prior versions did. The hook_menu() hook still exists, but it's now exclusively used for defining menu items, not menu items and their routes. You can read more about modulename.routing.yml files and how they work. At the end of the day though you're still just mapping a URL to some PHP that gets executed.


Also worth mentioning is the PSR-0 naming standard which is being used by Drupal 8 to perform autoloading of code on an as-needed basis. In Drupal 7 we have the registry which reads in a list of files[] from your .info file and then parses them to figure out what classes, if any, are defined in those files. Then, when someone instantiates a new object from a given class the file containing that code will be automatically loaded on an as-needed basis. The PSR-0 standard is essentially a different way of doing this same thing but following a practice that a larger portion of the PHP community uses, rather than one that we made up for Drupal.

There's more in the screen cast as well, so have a look at what it takes to write the simplest of Drupal 8 modules.

Note: As of the recording of this video some changes have been made to Drupal 8 that make this code not work. Most notably the ControllerInterface class which I extend in the video has been renamed to ContainerInjectionInterface. More details https://drupal.org/node/2079895.

More Changes: Changes where made in the .routing.yml file recently so that the key 'pattern' is now 'path'. See the change notice here: https://drupal.org/node/2089727

Even more changes: Drupal 8 now uses PSR-4. You can read all about it in this blog post: Preparing for Drupal 8: PSR-4 Autoloading


To read and see other tours of Drupal 8, check out Kyle's original post, Learning by Trial and Error - Installing and Touring Drupal 8 and Addi's post on Drupal 8: New Multilingual Features.


We are trying to make this easier developing a code generator scaffolding tool for Drupal8 based on Symfony Console component.

The idea of this project is to provide similar functionality as the Symfony console, providing a CLI scaffolding tool to automate the creation of modules using the terminal to automatically generate the directory structure for controllers, forms, services and required files.

Github repository

Watch a video here

I think what you are doing is fantastic, thanks. It would be great for me if you could present all of your tutorial in a written format. Video takes four+ times longer to digest than a written tutorial, so it makes following your tutorials too time consuming to follow. This is unfortunate as I really like what you are doing.

Hi, I really appreciate your idea. I started to learn more about Drupal in general. The differences you have indicated here between Drupal 7 and 8 make it easier for me to understand it more. This is a very informative module for me.

when I enter this "http://www.mydomain/agenda_subscription" then I getting
" Page not found
The requested page could not be found. "

My Directory structure :-
D8 > modules > custom > customModule > lib > Drupal > customModule > Controller > ExampleController.php

/***** Start customModule **************/


namespace Drupal\customModule\Controller;

use Drupal\Core\Controller\ControllerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class ExampleController implements ControllerInterface {

public static function create(ContainerInterface $container) {
return new static($container->get('module_handler'));

public function myPage() {
$element = array(
'#type' => 'markup',
'#markup' => t('Hello world!'),
return $element;

/******** End customModule ******************/

And Root level customModule.routing.yml

/************** start customModule.routing.yml **************/
path: '/agenda_subscription'
_content: '\Drupal\customModule\Controller\ExampleController::myPage'
_title: 'Agenda Subscription'
_permission: 'view content'

/************** End customModule.routing.yml **************/

So help me....

Hi arpit.awasthi,

It looks like in addition to the change notice (https://www.drupal.org/node/2079895) mentioned in the article there have been other changes that need to be made to the chad.routing.yml file.

I've got the module working again, and uploaded the chad.zip file attached to this blog post. Take a look at that compared to your custom code and you'll see the changes. Hopefully this helps get your module working.


It's bad enough PHP 7.0 was written for people who can't type. Now we have YAML for people who can't use text editors.

This is the end of Drupal for me, I'm moving over to a real CMS using a real language. (so much for a lot of D7 custom code I wasted my time on).

Thanks Drupal.

Add new comment

Filtered HTML

  • Web page addresses and email addresses turn into links automatically.
  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <code class> <ul type> <ol start type> <li> <dl> <dt> <dd><h3 id> <p>
  • Lines and paragraphs break automatically.

About us

Drupalize.Me is the best resource for learning Drupal online. We have an extensive library covering multiple versions of Drupal and we are the most accurate and up-to-date Drupal resource. Learn more