If you have a current Drupal site (built in Drupal 8 or later) you no longer need to entirely rebuild your site -- ever again. That doesn't mean it couldn't use a freshening up now and then.
We have several clients revamping their home pages, along with key landing pages. As part of this refresh, they are getting new designs in place, new messaging, new content. And they would like to launch the new design, and content, all at once.
While it's easy to deploy code and configuration changes from a development site to production, getting content up there is not so simple. Even posting a new block has long been a challenge -- if you add it on a dev site and push your configuration up, you get a "Broken or missing block" text on production, leading to deployment gyrations to get new content deployed smoothly.
For the past couple years, we've been using Fixed Block Content to get new blocks deployed, but that's proven to be finicky and inconsistent -- you have a lot of clicking to get actual content into an actual new block. And on one site if you make any changes, it blanks out the content when we deploy (largely due to a conflict from a custom module).
Two new modules to deploy content
So these may not be entirely new, but they are new to us: Structure Sync and Default Content. Both of these modules allow you to export content to code, and import it on other site environments. And they work entirely differently.
Both of these are very easy to use, and solve the basic problem of deploying content quickly and easily -- with a couple gotchas.
Keep content in sync with code - Structure Sync
First of all, Structure Sync allows you to export any custom blocks, taxonomy terms, or menu links on your site. This module uses its own configuration entities to store the content. If you want to sync all custom blocks, it's really easy:
> drush export:blocks Exporting blocks... [notice] Custom blocks export started [notice] Exported "Homepage Hero" [notice] Exported "Social block" [notice] Exported "Explore Life & Legacy Feature" [notice] Exported "From Our Archives" [notice] Exported "Museum Exhibitions" [notice] Exported "Archive & Exhibit Feature" [notice] Exported "PYV Hero_L1" [notice] Exported "Library Location_Google Map" [notice] Exported "Library Hours" [notice] Exported "PYV_L1_Lead" ... [notice] Custom blocks exported [notice] Message: The custom blocks have been successfully exported. > drush config:export [notice] Differences of the active config to the export directory: +------------+---------------------+-----------+ | Collection | Config | Operation | +------------+---------------------+-----------+ | | structure_sync.data | Create | +------------+---------------------+-----------+ The .yml files in your export directory (../config/sync) will be deleted and replaced with the active config. (yes/no) [yes]: > [success] Configuration successfully exported to ../config/drupal/sync.
... that's all there is to saving all your custom blocks. This process can handle taxonomy terms and menu items as well -- with an important caveat: it only saves the item itself, and not anything related to it -- images, media, other content referenced by an entity reference field.
Once you've exported your content, it gets stored in the Structured Sync data configuration -- but this does not automatically deploy your content. After you've gotten the configuration to your other environment, you still need to import the data.
> drush import:blocks Importing blocks... What import style would you like?:  Full  Safe  Force >
... will import all blocks on the new environment, with 3 choices of how. Read the project page to decide which you want -- this module can update content of existing blocks, delete blocks that are not exported, or only import blocks that don't already exist.
The admin page for structure sync has even more options -- you can select individual blocks, menus, or vocabularies to export or import.
This is a great option for sites that need to deploy blocks or a new menu along with a fresh design!
Deploy rich content using Default Content
Default Content is entirely different -- it can deploy any content entity, not just blocks, menus and taxonomy. It's far more granular -- you export individual items, one at a time. This can include nodes, paragraphs, users, any kind of content entity.
This module was originally meant to create demo content for testing new websites, but it's useful for deploying new content as well, especially pages with paragraphs, images, and the like -- with the "Export References" option, when you point it at a single node, it will export all related entities at the same time!
For a simple, one-shot deployment of several pages, this is how we've done it:
- Set content to be owned by user 1. (If you don't, it will export the author's user account, which currently throws an error on import if it already exists).
drush default-content:export-references node <nid> --folder=contentto export a particular node, to a new directory called "content".
- Add any other nodes/items you want to deploy.
- Create a new custom module. e.g.
drush generate my_new_content. You only need the .info.yml file.
- Edit the info.yml file, adding a default_content block listing the entities to import grouped by entity type. Each entity is exported to a file named with its UUID -- this is what you need to add to the info file.
- Move the content folder into the new module.
Example info.yml file:
name: New Pages type: module description: Default content for homepage, about, and leadership pages package: Custom core_version_requirement: ^9 || ^10 dependencies: - default_content:default_content default_content: node: - 0ab700ab-89ff-48b3-af0b-786659fe7e0a - 2e4598da-23f2-411f-ada1-f4571b6fdbaa - fa4d9814-c290-431e-8fad-090b6c58f310
Now, when you deploy this module to the new environment, all you need to do is enable it, and your new content will get imported!
This successfully deploys paragraphs embedded in the content, as well as attached images. We have not (yet) tried this with images embedded in rich content, or layout builder, but I do see issues with patches available or fixes.
So far we've only used this to deploy new content, and as is, if you enable this module when any of the entities in it already exist, you will get SQL errors. However, it does look like there are patches that might make this a more robust solution for rolling out changes to existing content in addition to just deploying new content.
Which to use?
Both of these modules seem extremely useful, with different gotchas and quirks. One big benefit of Default Content is deploying content to entirely different sites. If the two sites have the same field structure, this makes it easy to move content around.
Some other miscellaneous notes:
- Issue with a patch to prevent erroring out if an entity already exists.
- Issue with a patch to manually import content without a module.
- Add layout builder support.
- Default Content Deploy module -- this looks like an alternative built around deployment, but looks related to an earlier version of default content. I'd love to hear more about whether this is a better fit, please comment below if you have hands on experience with these!