- Home
- 2048 In Drupal - Integrate Whatever JS Build Using Single Directory Components
2048 in Drupal - Integrate whatever JS build using Single Directory Components
21 November, 2024
To play 2048, swipe the tiles on a 4x4 grid in any direction (up, down, left, or right) to combine matching numbers, doubling their value, and aim to create a tile with the number 2048.
During the development of a website in Drupal, there are instances where integrating external applications built in JavaScript becomes essential.
These integrations provide the flexibility needed to implement advanced features, interactive elements, or custom functionalities that go beyond Drupal’s core capabilities.
A typical example can be the integration of a dynamic map using “Leaflet” inside a page of your website.
Typical examples of components:
- Integrating a dynamic map using a React bundle with Leaflet.
- Integrating a HubSpot lead gen form.
- Integrating some specific calculator or app that depends on external API working at the customer level.
- Integrating some conversion tracking button or action
- Creating some card or dynamic navigation menu.
- Everything that should be modularized and require some JS
The main solution could be to integrate directly in the JS inside the template, using Twig to edit that specific page, but doesn’t satisfy the isolation requirements, and would it require copying the code in whatever Twig file we need it.
Drupal Single Directory Components
Components are made up of HTML, CSS, and JavaScript code, and can be easily customized and reused across multiple pages or applications. Examples of components include navigation menus, forms, sliders, and buttons, or React, Angular, or whatever Javascript Bundle.
Single-Directory Components ( SDC ) are Drupal core’s implementation of components. Within SDC, all files necessary to render the component are grouped together in a single directory (hence the name!).
This includes Twig, YAML, and optional CSS, JavaScript, etc. When the template is invoked, SDC will automatically generate a library to load CSS/JS.
How to create a Single Directory Component
In this tutorial, we will demonstrate how to create a Single Directory Component (SDC) in Drupal to integrate the popular 2048 game, built using Next.js, into a Drupal website.
The goal of 2048 is to move and combine equal tiles until obtain a 2048 tile.
Our goal
We will obtain a clean and modular way to encapsulate all related assets, such as templates, styles, and JavaScript logic, within a single directory. This approach aligns perfectly with modern web development practices using whatever JS component
Our example will include the following steps:
- Setting Up the 2048 Game in Next.js: We’ll start by ensuring the Next.js version of the 2048 game is ready for embedding.
- Creating the Single Directory Component in Drupal: We’ll create a directory to house the template, configuration, and style files for this integration.
- Integrating Next.js into Drupal: Using an iframe or server-side rendering approach, we’ll embed the game seamlessly into a Drupal block or page.
- Styling and Testing: We’ll add custom styles if necessary and ensure the component is responsive and fully functional.
Premises
All single-directory components in Drupal should have a machine name with letters, numbers, and underscores and they must stay inside the components folder inside your template.
Component name: game_2048
Template name: geonovation01
So, our final directory would be /themes/custom/<theme_name>/components/<component_name>/js/dist and specifically /themes/custom/geonovation01/components/game_2048/js/dist
Preparing the JS bundle
Starting from the already existing 2048 game implementation from
mateuszsokola ( https://github.com/mateuszsokola/2048-in-react ) we are going to prepare our bundle for our Drupal website.
We need to specify the output basePath for the build unless it would be able to find the relative JS chunks when is in production as by default it would try to load from the root path. To do so, we need to edit the Webpack configuration. In Nextjs we can do it from the next.config.js.
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
basePath: "/themes/custom/geonovation01/components/game_2048/js/dist",
reactStrictMode: true,
images: {
unoptimized: true, // Image optimization is not compatible with static builds
},
};
module.exports = nextConfig;
Once the build is ready, we will obtain an "out" folder that we should copy and rename in "dist" in order to obtain index.html inside this directory /themes/custom/geonovation01/components/game_2048/js/dist/index.html
└── game_2048
├── game_2048.component.yml
├── game_2048.twig
└── js
├── 2048-in-react
│ └── [ nextjs files ... ]
└── dist
├── index.html
└── [ ... ]
Creating the components
Having the folder <template>/components/game_2048 create the component definition file for Drupal.
Filename: game_2048.components.yml
name: Game 2048
props:
type: object
required:
- class
properties:
class:
type: string
title: Css Classes
description: "Append your CSS classes to the container of the app"
slots:
header:
title: Header
footer:
title: Footer
Interesting features
- The name will be the visible name
- You can add properties using the JSON schema types.
- You can add slots using the Twig block structure to embed in the components.
Thanks to these features you can modularize your components and make them dynamics according to your needs, as then you can inject those properties inside your HTML, and consequently into your JS application.
Filename: game_2048.twig
<div class={{ class }}>
{% block header %}{% endblock %}
<iframe src="/themes/custom/geonovation01/components/game_2048/js/dist/index.html"
width="550" height="650" frameborder="0"></iframe>
{% block footer %}{% endblock %}
</div>
In this case, we will load the components using the IFrame because would be easier as there are many bundles with different chunk hashes, and we don’t require a full 100% integration, but it is possible with other ReactJS applications to simplify in a single bundle and load it automatically.
In this case, Drupal will automatically generate a “libraries.yml” file to automatically load all the CSS and JS dependencies. Drupal just recognizes the CSS and JS files that start with the machine_name of the component, including the twig HTML.
Adding the component in an Article
In order to add the component to an article we will use the paragraph ( https://www.drupal.org/project/paragraphs ) module.
Create a new paragraph type called component
The paragraph contains two field, one list containing a “Select” for each Drupal components and a plain text to add custom classes.
The machine name must follow the pattern <template_name>:<component_name>
Add a paragraph field to your Article Content Type.
Add the component to an article
Open a new article and in the section paragraphs select the component paragraph and add the created component.
Adapt the paragraph to render the components.
Create a new file to override the default template of the paragraph.html.twig of your template.
The new file should override only the paragraph of type “component”, so the path will be in your template /templates/paragraphs/paragraph-component.html.twig
If it is not read correctly, make sure that the machine_name of your paragraph is “component”
{%
set classes = [
'paragraph',
'paragraph--type--' ~ paragraph.bundle|clean_class,
view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class,
not paragraph.isPublished() ? 'paragraph--unpublished'
]
%}
{% set component_classes = [
'component',
'component--' ~ paragraph.field_component.value|clean_class,
paragraph.field_classes.value|clean_class
]
%}
{% block paragraph %}
<div{{ attributes.addClass(classes) }}>
{% block content %}
<div class="{{ component_classes|join(' ') }}">
{% embed paragraph.field_component.value with { class: 'content'} %}{% endembed %}
</div>
{% endblock %}
</div>
{% endblock paragraph %}
How to embed a component in Drupal
To use a component you’ll need to know the component ID. This is the machine name of the module or theme that provides the component, plus the name of the component itself, separated by a colon. Examples: geonovation01:game_2048
Using Embed — the best option for using slot functionalities.
{% embed 'geonovation01:game_2048' with { class: "test"} %}
{% block header %}
YOUR HTML
{% endblock %}
{% endembed %}
Using Include
{% include 'geonovation01:game_2048' with { text: 'Click Me', iconType: 'external' } only %}
Final result
Integrating JavaScript applications like the 2048 game into a Drupal website using Single Directory Components (SDC) makes the developer's life a lot easier.
By encapsulating all related assets — HTML, CSS, JavaScript, and configuration — within a single directory, SDCs enable clean, maintainable, and scalable solutions that align with Drupal’s best practices.
By following this approach, you can unlock new possibilities for delivering interactive, high-quality, and efficient solutions within your Drupal projects.
You can find this working example directly on our website: www.geonovation.it/blog
If you find the article helpful you can leave a like or a comment on how should we improve in our medium page: https://medium.com/@andreacorda-geonovation/2048-in-drupal-integrate-whatever-js-build-using-single-directory-components-97e0db767794