Useful Drupal modules

Click on the module name for notes, comments, patches.
Recommendations (for and against) are my personal opinion only and may be out of date. Feel free to email with corrections/suggestions.

Why doesn't the table mention Drupal 9 or 10?

1. Nowadays, I try and use Drupal as little as possible. Specifically I do not recommend it for a new website. I'd also strongly caution you against choosing Drupal if you're at the beginning of your career and trying to learn web development (front / back / full stack) or just want to manage/maintain a website. More details why by email on request.

2. By February 2022 I'd updated all the sites I look after to Drupal 9 (experience: mixed). As of March 2023, a year later, none of them were fully ready for Drupal 10 (waiting on support for various modules). By 13 Nov 2023 (a week or two after D9 lost support) I had updated a couple of them.

Q: What's the Drupal 9 to 10 upgrade experience like? A: it's what I'd describe as "just bearable", slightly better than D8 to D9, though with plenty of things to still trip you up. There is still deprecated code that needs fixing in D10 modules, and policy changes mean odd things will break (be prepared to turn off Aggregate CSS/JS files if your themes stop working, and note that Drush launcher doesn't work with Drush 12...)

Admittedly the upgrade_status module is the best solution they could have come up with for tracking compatibility. Also watch out for a nasty session headers bug with redirect_after_login.

What about sites still running Drupal 7? In short I still recommend leaving them on Drupal 7 or moving to another platform entirely. My prediction was that Drupal will extend D7 support year after year (note that drupal.org itself is still running D7). It's now been confirmed as 5 Jan 2025. There will have to be some sort of third-party long term support, because of the sheer volume of sites that are not being upgraded.

Your decision is really what specifically you think you will gain from Drupal 8/9/10 - often the answer will be not enough, and one of the main hassles - apart from converting each content type one at a time, and setting up a new theme, will be recreating all your views by hand, as views can't be automatically upgraded.

(134 modules in list)
Module name or machine name
Any text in the notes
Name D8 Personally Tested Last Updated Sort ascending
Required by role (required_by_role)

d.o. page

"Essential"

Set required fields more precisely.

How to verify it it's in use on any fields (so you can remove it if desired)

I wanted to upgrade a site to D10 and there was a dependency from required_by_role on required_api, which was not yet D10 compatible. I wasn't sure if I was even using required_by_role on any fields - turns out I wasn't. There's no admin UI page overview which shows it's use, but you can just run a simple SQL query:

select * from config where data like "%required_by_role%";

(the config table is where all your field settings are stored).
Views PHP (views_php)

d.o. page

Allows you to insert a Global PHP field in a view in D7, so you can have conditionals etc.

Note there's a bug in 7.x-1.1 (Jan 2022) - you need this patch.

(Not available in D8, you should use Twig instead).

Google Authenticator login (ga_login)

d.o. page

"Essential"

Use in conjunction with tfa to support OATH based HOTP/TOTP systems.

Nov 2022: No longer needed (i.e. uninstall it) when you upgrade to tfa 2.x for Drupal 9 and 10.

Custom Add Another (custom_add_another)

d.o. page

"Essential"

The default button text for 'unlimited' fields where you can add multiple items is "Add another item".

You might want to change this.

This module lets you do it per field bundle - there are a couple of text options (for add and remove) added to the field edit screen.

NB: this is not the 'Save and add another' button for an entire content type, just the one that handles the javascript for individual fields with multiple entries.

Double Field (double_field)

d.o. page

"Essential"

Store a pair of fields together (numbers and text, boolean, email, telephone, Url).  As normal you can specify the quantity of double fields per entity, or allow an unlimited number.

Note: D7 version doesn't support 'date'.

Don't use it for:

  • links - you might have one field for the URL and one for the description, but the display widget won't let you merge them into a clickable link.  That's what the dedicated link field type is for.
Node access user reference (nodeaccess_userreference)

d.o. page

"Essential"

Allows you to use an entity reference field on a node to allow (or deny) access to that node to the users you have selected.  Extremely useful for giving selected people access.

This isn't available in D8 or above (though it ought to be) - there's a note in the issue queue about it.  #2655426

Role Watchdog (role_watchdog)

d.o. page

"Essential"

Logs every time a role is changed. Users get a Role History tab and there's a notification feature. 

Should now work fine on D8, not personally tested it (earlier: 15 Sep 2018 - there's a dev branch, but when you install it complains about missing role entity, and drush updb does nothing) 

Redirect after login (redirect_after_login)

d.o. page

"Essential"

Sends users (depending on their role) to another page, rather than their profile, immediately after logging in.

There's a bug (patch available, fortunately) with this which will break session cookies in Drupal 9, and upgrade_status doesn't warn you about it. 

AudioField (audiofield)

d.o. page

"Essential"

This is a formatter, available on node display or in views.

It turns a uploaded file or an external URL into an HTML5 audio player. Also supports various other JS player libraries which you can download. You can turn lazy-loading of the file on or off.

Note: there is a default label added below the player, which is either the URL field description or actual address, if the former is missing. Override the audioplayer.html.twig template to remove this.

Conditional Fields (conditional_fields)

d.o. page

Adds a 'Manage dependencies' tab to the admin page for content types.

This lets you set a 'target field' whose visibility is set according to a 'control field' and whether the latter is full/empty/checked/unchecked/has a specific value.

Another useful feature is you can prefill/pre-tick etc. the target field (including with a custom text value, for example).

To detect quickly if it's in use anywhere (I needed to do this before being sure I could uninstall the module on a site), export your site config and grep the /config/sync directory for 'conditional_fields' - it's stored in the 'third_party_settings' key in core.entity_form_display.node.[content type].default.yml

Views Aggregator Plus (views_aggregator)

d.o. page

Provides extra options for aggregating data in tables - e.g. grouping sets of rows together and summing the values of particular fields.

Has a hook - hook_views_aggregation_functions_info() - which lets you add your own options to the table aggregation settings - e.g.  supposing you have a limit for something (number of tickets, nights of accommodation) and want to decrement it - you can write a method to sum the matching rows and then do a calculation on the total.

Also supports 'add column function' - adds a grand total (or other summary field) at the very bottom of the table.

Caution - this module breaks hook_views_pre_render() - you can write your own pre_render method to remove or shuffle entire rows, but any chances to values will be overwritten (discovered this by looking through the source and looking for the use of that hook).  The workaround is to try and do what you need to by using hook_views_aggregation_functions_info() instead.

Metatag (metatag)

d.o. page

"Essential"

This comes with a whole series of modules, including metatag_verification that will allow you to insert a google-site-verification tab. Instructions

How to add it to a content type/bundle:

  • You need to add the metatag field to the content type and position it on the form (it'll display as a disclosure triangle in the sidebar)
  • You also need to configure the level of available options at /admin/config/search/metatag/settings
Rules (rules)

d.o. page

Patch needed (the pages and roles tabs are missing on block editing form otherwise):

https://www.drupal.org/project/rules/issues/3160347

Views Contextual Filters OR (views_contextual_filters_or)

d.o. page

"Essential"

By default if you add multiple contextual filters to a view, they must all pass (AND).
This gives you a checkbox in Advanced > Query Settings for the view which switches it to OR.

Responsive and off-canvas menu (responsive_menu)

d.o. page

This uses a JS library and works without a second copy of the menu in the HTML, and you don't need to make any changes to your existing navigation menu.

Look at the README file for instructions.
You need to install mm_menu and superfish in your libraries folder.

Note you're forced with the mobile version of the menu to have it expand to full depth (although the hack/workaround, if you want a top-level menu only,  is to hide the lower elements in CSS)

For hand-crated CSS / minimal javascript solutions, see https://css-tricks.com/responsive-menu-concepts/

Twig Tweak (twig_tweak)

d.o. page

Adds Twig syntax to insert views, view results, regions, entities (e.g. nodes, blocks, webforms), menus, images, image styles, tokens, breadcrumbs, messages, contextual links and more.

Node Page Disable (node_page_disable)

d.o. page

"Essential"

Adds an checkbox labelled 'Retain /node as an active url?' to /admin/config/system/site-information

This lets you disable the /node page which would otherwise list all published content.

Note it doesn't let you individual disable /node/xxx access by content type - see restrict_node_page_view or rabbit_hole for that.

This is for D7 only - in D8 you go to /admin/structure/views and disable /node there.

Username policy (username_policy)

d.o. page

This gives you a single box where you can enter tokens to form the default username.

It seems to work, but in D7 the patterns section ("Only use the below pattern elements") was empty. This is supposedly because there's a dependency on the 'profile' module, except profile doesn't exist in D7 (I tried profile2 instead but it made no difference) and profile2 doesn't exist in D8.

auto_username displays tokens OK and is more powerful / has more settings including customise at what point names are truncated, etc.

Upgrade Status (for Drupal 9) (upgrade_status)

d.o. page

"Essential"

Very useful module for scanning installed modules (including your own custom code) for compatibility issues - gives a detailed report of necessary fixes.

NB: scanning using the UI (rather than Drush) won't work on a dev server without a valid SSL certificate.

Coffee (coffee)

d.o. page

"Essential"

Keyboard shortcuts. A bit like Spotlight/Alfred/Launchbar.

Alt+D to launch (memonic: alt + D = coffee!)

Handy as it doesn't clash with browser keyboard-shortcut extensions such as Vimium.

Tips:

  • April 2020: 8.x-.10 released, means you no longer need this patch to fix CSS colours
  • remember that you can/should add your own menus to the search index
Table trash (table_trash)

d.o. page

Add extra tables JS support for views (and many other pages) - e.g. search box, dynamic column sorting, rearranging columns, exporting to clipboard etc.

If you get "TypeError: tables.DataTable is not a function" when using datatables module, first, try going to /admin/config/content/table_trash/global_settings and saving the existing settings.

Resave nodes (resave_nodes)

d.o. page

"Essential"

If you want to manually refresh a series of nodes.
Mainly useful if you have a rule or a hook that relies on a node being saved, to update a computed field etc.

You can choose the node type.

Devel (devel)

d.o. page

"Essential"

Key features:

  • Kint (enable kint submodule, for pretty variable printing, way to view properties and methods)
  • DevelMailLog (redirect all mail to text files instead, specify location directory, specify filenames using tokens for recipient, subject, timestamp) - DevelMailLog requires that you also install the MailSystem module to activate it
  • Config Editor and State Editor (enable via /admin/config/development/devel/toolbar)
Honeypot (honeypot)

d.o. page

"Essential"

Adds spam prevention to forms (you can select which, including user registration and contact).

Recommended settings:

- turn logging on (look for entries of type = honeypot in /admin/reports/dblog
- if using time limit - i.e the minimum amount of time form is expected to be on screen before module things it's a real person, not a bot, suggest 5 seconds rather than 10 (too agressive when using browser autocomplete for, for example, email address on password reminder form)

Rabbit hole (rabbit_hole)

d.o. page

Lets you set display, access denied, redirect or programmatic accessing for direct access to nodes.

However, note that includes both /node/xxx and the node alias, i.e. you can't just use it to restrict numeric URLs, the human friendly ones will get blocked too - so restrict_node_page_view might be more appropriate.

There's a series of submodules (such as rh_node) and without enabling them you won't see any options in the UI.
(look for the Rabbit Hole tab when editing nodes, for example).

Twig Extensions (twig_extensions)

d.o. page

Text filters, array and date manipulation.

(haven't tried this yet)

reCAPTCHA (recaptcha)

d.o. page

"Essential"

Use in conjunction with the Captcha module to prevent spam on forms.
As of Jan 2020, still ONLY supports v2 of Google's reCAPTCHA API (there's an active issue for v3 support)
There's also an option for non-js fallback.

Tested with D7 and D8.

Patch needed for AJAX forms (e.g. Webforms with AJAX support turned on) to stop the ReCAPTCHA UI disappearing when page is reloaded.

Webform (webform)

d.o. page

"Essential"

One of the best maintained contrib modules.  Main benefits: nicely presented forms with powerful conditional logic, YAML configuration, flexible user permissions, very clean data storage, secure.  Codebase is also very much written "the Drupal way".

I'm currently using this patch to avoid AJAX errors - it's only necessary where you've both enabled Automatically save as draft when paging, previewing, and when there are validation errors, and are using a confirmation type like inline, that still uses AJAX to submit the form when there are no validation errors.

Troubleshooting install of webform external libraries: (Jan 2020)

Follow these instructions: https://www.drupal.org/node/3003140

If you get a composer SSL error, follow these instructions.

(specifically, you need to set openssl.cafile in php.ini)

Menu position (menu_position)

d.o. page

"Essential"

Assign various pages (by content type, URL, role etc.) to certain menu entries - so the 'active' status of the menu is set correctly (i.e. they appear within the sections you want).

- patch: Add enabled checkbox to edit form + enable new rules by default

As of alpha2 - views with contextual filters (e.g. myview/somefilter) will work correctly when a rule is added with pages / pages with wildcards specified.

 

Twig VarDumper (twig_vardumper)

d.o. page

"Essential"

Much faster than using kint, with colour coding, and still works nicely within narrow DOM elements.
Make sure you install using composer, as it has symfony dependencies.

Enabling twig debugging in the first place:

Also make sure you've activated twig debugging properly (including enabling the settings.local.php file in settings.php, and adding the debug setting to development.services.yml)
Potential gotcha - settings.local.php needs to be in /sites/default (or whichever), not just /sites/
If Twig debugging isn't enabled, the symptom is you don't see any output for the dump() commands.

Usage Tip:

- Hover over the keys and you get a tooltip that says 'Public method','Protected property' etc.

Field group (field_group)

d.o. page

"Essential"

Add group' button to /admin/config/people/accounts/form-display (or any other form), can then group fields inside fieldset/other HTML wrappers.  

 

Types of groups:

  • Fieldsets
  • Horizontal tabs
  • Vertical tabs
  • Details
  • Accordions
  • Divs
  • HTML elements

In theory can use this to add <div>s with css styles such as flexbox, like webform uses, but CSS to support that needs to be present on the page.
 

Ngrok for drupal (ngrok_drupal)

d.o. page

Sets the cookie domain correctly if you're using ngrok.

Use this if you have an Ngrok secure tunnel - e.g. if you are testing a Stripe Apple Pay integration (needs to run with a valid, publicly accessible SSH domain)

Save edit (save_edit)

d.o. page

"Essential"

Add a save and edit button

  • Needed(?) patchto ignore the ?destination=querystring on admin page. 
  • Integrated dropbutton doesn't seem to style correctly, turn this off.
CSS Editor (css_editor)

d.o. page

"Essential"

If you only need a small amount of CSS, or want users to be able to directly edit it through the admin UI rather than creating a theme or module.

Caveat: doesn't work for admin themes (at least on D7)

Login email or username (login_emailusername)

d.o. page

"Essential"

Self-explanatory.

Content access (content_access)

d.o. page

"Essential"

Set permissions by content type (with the option of overriding node by node)

Toolbar menu (toolbar_menu)

d.o. page

"Essential"

Add extra menus (which appear as cog icons with a title, which you can click and expand) to the admin toolbar at the top of your site.

Add another (addanother)

d.o. page

"Essential"

For this to work for any content type after you activate it, you have to manually edit and save that content type's settings, otherwise the buttons will be missing.

Views infinite scroll (views_infinite_scroll)

d.o. page

  • supports both a 'Load more' button and automated load
  • need to turn AJAX on for the view
  • patch needed to support back button (adds a preserve history option to settings, which then needs to be turned on)
Views taxonomy term name into ID (views_taxonomy_term_name_into_id)

d.o. page

Allows you to use /my-view/foo instead of /my-view/123 when adding a contextual filter based on taxonomy terms

Administration Views (admin_views)

d.o. page

"Essential"

D7 only (as this is in D8 core as standard).

Converts /admin/people into a fully-fledged configurable view (i.e. fields, filters, exposed filters, header/footer, pagination settings etc.) 

(Dependency: views_bulk_operations)

Spambot (spambot)

d.o. page

"Essential"

Scans email addresses/usernames/IP addresses against the stopforumspam.com database and then blocks or deletes the accounts.
You can set the threshold - i.e. how many times an email address needs to be listed in the spam database before action is taken.
You can also have it scan your entire existing database, not just new users. (It does this via a configurable cron run.)

Tested with D7.

User restrictions (user_restrictions)

d.o. page

"Essential"

Block registrations (wildcards on email address and username)

Useful if you're getting persistent spam registrations from certain domains.

Tested with D7.  This used to be in core in D6.​​​​​​​

Captcha (captcha)

d.o. page

"Essential"

Framework for using (many) different types of captchas on forms such as user_registration.
e.g. try 'reCAPTCHA' which is Google's "I am not a robot" captcha.  Personal experience is that's much better at preventing spam than the standard "image" one.

Tested with D7.

Mydropwizard (mydropwizard)

d.o. page

"Essential"

If you have a Drupal 6 site, this modifies your Available Updates page (/admin/reports/updates) with direct download and release note links to all the D6 LTS (Drupal 6 Long Term Support) releases. 

Automatic User Names (auto_username)

d.o. page

"Essential"

Removes the 'Username' field from registration form.  Use in conjunction with a module that allows users to login with their email address.
Generates a username in the background (Drupal still requires one) - adds 'Patterns' tab to Account settings for configuring the name.

Patches:

 

Comment Notify (comment_notify)

d.o. page

"Essential"

Allows users to receive emails for replies to their comments (or optionally for all comments on a node).  Can be toggled by content type (although not by comment type, if you have more than one comment entity).  In permissions, you can also control which roles can subscribe.  Can also set it so the author of a node also automatically gets emailed for every comment.

I'm using three patches:

I also have a site which uses two comment types; I wrote a patch to turn off the notification checkbox for one of them.

Configuration Read-only (config_readonly)

d.o. page

Lock all configuration changes via the admin UI - e.g. if you need to lockdown a production environment.

config_readonly now has a whitelist, added via an array in settings.php.

CKEditor Custom Config (ckeditor_config)

d.o. page

I've not actually used this but I adapted it's code to enforce a configuration setting (I wanted to remove the HTML dom elements that are shown in the status bar).

The module allows you to supply your configuration using the UI.

CKEditor Wordcount (ckwordcount)

d.o. page

Adds word/character/paragraph count to the CKEditor status bar (can optionally enforce a word/character limit)

Configured via Text formats and Editors (/admin/config/content/formats).

Requires the ckeditor wordcount library - example composer.json for that (put this in your 'repositories' section):

        "ckeditor.wordcount": {
            "type": "package",
            "package": {
                "name": "ckeditor/wordcount",
                "version": "1.17.4",
                "type": "drupal-library",
                "extra": {
                    "installer-name": "wordcount"
                },
                "dist": {
                    "url": "https://download.ckeditor.com/wordcount/releases/wordcount_1.17.4.zip",
                    "type": "zip"
                },
                "require": {
                    "composer/installers": "~1.0"
                }
            }
        },

and then require:

"ckeditor/wordcount": "1.17.4",

Patch needed:

https://www.drupal.org/files/issues/2018-12-06/ckwordcount_plugin_path_2850845-9.patch

(to ensure modules gives browser correct path of plugin.js - if your ckeditor input disappears, check console.log and it's probably that)

The notification module is a dependency.