In the previous tutorial, you learnt how to create a modal in a custom module. If you missed it, you can check it out here.

There is just one problem with this: if a user doesn’t have Javascript enabled, the modal won’t work. While the vast majority of people do have JavaScript enabled, it is still important to gracefully work for those that don’t for accessibility reasons (for example, visually impaired people might access the site with a screen reader).

In this case, the best option is to display the contents of the modal on a page, rather than the modal, for those that don’t have JavaScript enabled.

Fortunately for us, Drupal handles this out of the box. All you need is a few tweaks to the code that we wrote in the previous tutorial.

The Steps

Start with the module from the previous tutorial - custom_modal. You can clone from github:

You are going to need to change three files in this modal:

  • custom_modal.routing.yml
  • CustomModalController.php
  • ModalBlock.php

1/ custom_modal.routing.yml

In the routing file, add a slug to the path which will change dynamically if Javascript is enabled or disabled:

path: 'modal-example/modal/{js}'

If Javascript is enabled, Drupal will change this to:


Otherwise, it will be:


This (ajax or nojs) will be passed to the controller, which you will see shortly.

You also need to add js: 'nojs|ajax' to the requirements.

The full code for the route is:

  path: 'modal-example/modal/{js}'
    _title: 'Modal'
    _controller: '\Drupal\custom_modal\Controller\CustomModalController::modal'
    _permission: 'access content'
    js: 'nojs|ajax'

2/ CustomModalController.php

In the modal method, add $js as an argument. This will be passed from the route and will be either ajax or nojs. This will allow you to change what is returned depending on whether or not Javascript is enabled. If nothing is passed at all, we’ll use nojs as the default.

public function modal($js = 'nojs') {

And add the following use statement:

 use Symfony\Component\HttpFoundation\Response;

The full code for CustomModalController.php is now:


 * @file
 * CustomModalController class.

namespace Drupal\custom_modal\Controller;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Response;

class CustomModalController extends ControllerBase {

  public function modal($js = 'nojs') {
    if ($js == 'ajax') {
      $options = [
        'dialogClass' => 'popup-dialog-class',
        'width' => '50%',
      $response = new AjaxResponse();
      $response->addCommand(new OpenModalDialogCommand(t('Modal title'), t('The modal text'), $options));
    else {
      $response = new Response();
      $response->setContent('The modal text (Javascript is not enabled)');

    return $response;

3/ ModalBlock.php

Change $link_url to:

$link_url = Url::fromRoute('custom_modal.modal', ['js' => 'nojs']);"

The full code for ModalBlock.php is now:

 * @file
 * Contains \Drupal\custom_modal\Plugin\Block\ModalBlock.

namespace Drupal\custom_modal\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Url;
use Drupal\Core\Link;
use Drupal\Component\Serialization\Json;

 * Provides a 'Modal' Block
 * @Block(
 *   id = "modal_block",
 *   admin_label = @Translation("Modal block"),
 * )
class ModalBlock extends BlockBase {
   * {@inheritdoc}
  public function build() {
    $link_url = Url::fromRoute('custom_modal.modal', ['js' => 'nojs']);
      'attributes' => [
        'class' => ['use-ajax', 'button', 'button--small'],
        'data-dialog-type' => 'modal',
        'data-dialog-options' => Json::encode(['width' => 400]),

    return array(
      '#type' => 'markup',
      '#markup' => Link::fromTextAndUrl(t('Open modal'), $link_url)->toString(),
      '#attached' => ['library' => ['core/drupal.dialog.ajax']]

Testing it without Javascript

You can test this by turning off Javascript in your browser. You can do this in Chrome with the following steps:

  • click on Settings
  • Advanced (right at the bottom of the settings page)
    • Content settings
    • Javascript
    • Toggle to blocked
    • Refresh the page and hit the “Open Modal” button in the block

If you click on the link for the modal, it should now display the non-JS version on the custom path.

Wrapping up

To see the complete code, have a look at the branch on GitHub. You can also check out the difference between this and the previous version here.