The Decoupled Pages module provides a quick and simple way to define new Drupal routes which can be overtaken by a single page application, often written in Javascript using React, Vue.js, or Ember (to name a few common frameworks).

How to create a decoupled page

1. Defining a route

To define a new decoupled page, you define a new route in your module's routing file. The basic structure of your route definition remains unchanged. You can add route requirements, route defaults and route options pretty much as normal. The only caveat is that you must not define a _controller or _form route requirement.

Instead, you'll define a _decoupled_page_main route requirement. The value of this key is the name of an asset library containing your single page application and its dependencies. It should take the form your_module/name. You can define this asset library in a your_module.libraries.yml file, but any module can provide an asset library.

Here is an example of a decoupled page route definition:

your_module_name.foo:
  path: /some/path/of/your-choosing
  defaults:
    _decoupled_page_main: decoupled_pages/route_test
  requirements:
    _access: 'TRUE'

Once your route definition has been created, a new route will be available after you've rebuilt Drupal's cache (make sure to enable your module too 😉).

The decoupled_pages/route_test asset library is pre-defined by this module. You can use it to test your route without first registering your own library. If your route works, you should be able to visit the path you defined. Check the browser console for a message saying that your route worked.

Warning: _access: 'TRUE' will make your route accessible to everyone.

2. Adding your single page application

This module will place an empty <div> into the main content region of your active theme. It will have the HTML ID decoupled-page-root, like so:

<div id="decoupled-page-root"></div>

Your JavaScript can attach to that element.

Once you've defined an asset library of your own. Go ahead and point your route's _decoupled_page_main to that library.

3. Customize for your needs

Paths routed in JavaScript

If your single page application defines its own routes, you'll want to make sure that your users can link directly to them. In order to do that, Drupal needs to know about those paths. You can add additional paths for Drupal to serve your application code by setting the _decoupled_page_paths route option, like so:

  options:
    _decoupled_page_paths:
      foo: /some/path/of/your-choosing/foo
      bar: /some/path/of/your-choosing/bar
      foobaz: /some/path/of/your-choosing/foo/baz

Adding additional CSS and JavaScript libraries

You may also need the ability to attach additional JavaScript or CSS to your decoupled pages that can't be included in your main asset library. You can tell Drupal about those assets by defining the _decoupled_page_assets route option, like so:

  options:
    _decoupled_page_assets:
      - your_module/some_extra_css

Passing configuration to your JavaScript application

In order to keep your JavaScript application from hardcoding certain assumptions (e.g. API URLs), you may want to pass configuration to your JavaScript via the root data element. Custom data attributes are an excellent way to pass information from the backend to your JavaScript application. To do that, you can specify a _decoupled_page_data route default, like so:

  defaults:
    _decoupled_page_data:
      api-base-path: /my/api/v1

Your data attribute names must be strings containing only the lower-case letters a-z and dashes and must not begin or end with a dash. You data attribute values must also be strings. You can access these data attributes from JavaScript, like so:

const root = document.getElementById('decoupled-page-root');
root.dataset.apiBasePath === '/my/api/v1'; // true

Notice that the dasherized key name that you defined in YAML is lower-camel-cased in JavaScript. This is a browser behavior, not a feature of this module.

Defining data attributes dynamically

If you need to be able to define data attributes described above dynamically, you may do so. There are three steps. First, create a class which implements Drupal\decoupled_pages\DataProviderInterface (see the decoupled_pages_test module for an example implementation). Second, define a service in your_module.services.php tagged with decoupled_pages_data_provider:

services:
  your_module.your_custom_data_provider:
    class: Drupal\your_module\YourCustomDataProvider
    tags:
      - { name: decoupled_pages_data_provider }

Finally, register your data provider on your decoupled route definition by its service ID:

  defaults:
    _decoupled_page_data_provider: your_module.your_custom_data_provider

The rules for data attribute names and values described above still apply.

Supporting organizations: 
Research & development

Project information

  • caution Minimally maintained
    Maintainers monitor issues, but fast responses are not guaranteed.
  • caution Maintenance fixes only
    Considered feature-complete by its maintainers.
  • Module categories: Decoupled
  • chart icon86 sites report using this module
  • Created by gabesullice on , updated
  • shieldStable releases for this project are covered by the security advisory policy.
    Look for the shield icon below.

Releases