Problem/Motivation

We have a stub for it in the README, and touch on it in https://git.drupalcode.org/project/distributions_recipes/-/blob/1.0.x/do...

But we should provide more in depth documentation, and a roadmap.

We can use https://kanopi.com/blog/default-content-in-drupal/ as an example.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

thejimbirch created an issue. See original summary.

peaton’s picture

I'm going to work on this at DrupalCon Atlanta 2025.

peaton’s picture

Assigned: Unassigned » peaton
thejimbirch’s picture

Assigned: peaton » Unassigned
nkarhoff’s picture

Assigned: Unassigned » nkarhoff

nkarhoff’s picture

Status: Active » Needs review

I've added more detail about using the Default Content module. Ready for review: https://git.drupalcode.org/project/distributions_recipes/-/merge_request...

thejimbirch’s picture

Thanks for working on this.

This is a good start, but I would like to have the content from the blog post to be put at the top level page here:

https://project.pages.drupalcode.org/distributions_recipes/default_conte...

And then in the author guide where you worked, add a link to the top level default content page.

# Default Content in Recipes

In Drupal 10.3, the DefaultContent API was added to Drupal core as part of the experimental Recipes APIs. These APIs allow Drupal to create content from files that are part of a recipe.

This content that we programmatically create isn’t intended for deploying or migrating content, we have the Workspaces and other modules for that.

Instead, it can be a great tool in our toolkit for creating demos and test content, while freeing developers and testers from the drudgery of repetitive content creation.

## The Default Content contrib module

While we added a way in core to consume content config files from recipes, we are still at the start of what core can do, and mid-journey of what the contrib module aims to do.

Until the recent interest from the Recipes Initiative team, the Default Content module has always been used for creating default content configuration files for custom modules and install profiles. You could create content on a local site, export it to your module or a module in an install profile, and when that module was enabled (or a site was started from the install profile), and you installed the default content module as a dependency, you would get your content imported. This all still works exactly as it always has, and now it can also be used with recipes. 



## Using the Default Content module

The export process is the same for modules and recipes. Let’s review that now.

## Getting set up

Install the module as you normally would. Since I am using the module for creating content for recipes, I install it in the dev section of my composer requirements as I do not need it in production.

```bash
composer require --dev drupal/default_content
```

And enable the module:

```bash
drush en -y default_content
```

The module doesn’t have a user interface. Instead, it provides Drush commands that can be run to export content.

## What can we export?

Out of the box, we can export the following entity types:

- **Content type nodes**: `node <node id>`
- **Taxonomy Terms**: `taxonomy_term <taxonomy term id>`
- **Files**: `file <file id>`
- **Media**: `media <media id>`
- **Menu Links**: `menu_link_content <menu link id>`
- **Content blocks**: `block_content <block id>`
- **Shortcuts**: `shortcut <shortcut id>`

At this time, there isn’t a way to export by bundle type (e.g., all nodes of type Article, or all menu links from the main menu), but there is a patch for it.

Let’s take a look at the three commands that the module provides to export content.

## Default content Drush commands

### `default-content-export-references`

Exports an entity and all its referenced entities. This is my go-to command as it helps you export referenced media, files, taxonomy terms, and users from the parent node.

**Alias**:  
`dcer`

**Arguments**:

- `entity_type` – The entity type to export.
- `entity_id` – The ID of the entity to export.

*One of these arguments is required.*

**Options**:

- `folder` – The module or recipe’s content folder to export to. Entities are automatically grouped by entity type into directories.

**Examples**:

```bash
# Exports node 123 and all its dependencies.
# To a module:
drush dcer node 123 module_name 

# To a recipe:
drush dcer node 123 --folder=recipes/recipe_name/content

# Export all nodes and all their dependencies.
drush dcer node --folder=modules/custom/my_custom_module/content
```

---

### `default-content-export`

Exports a single entity. Use this if you want to manage your own dependencies.

**Alias**:  
`dce`

**Arguments**:

- `entity_type` – The entity type to export.  
- `entity_id` – The ID of the entity to export.  

*Both arguments are required.*

**Options**:

- `file` – Write out the exported content to a file instead of stdout.

**Example**:

```bash
# Exports node 123 only. You are responsible to export dependencies.
drush dce node 123 module_name
```

---

### `default-content-export-module`

Exports all the content defined in a module info file. This is most helpful for distribution and install profile maintainers who want to maintain and update default content from a canonical source.

**Alias**:  
`dcem`

**Arguments**:

- `module` – The machine name of the module (required).

**Options**:  
None

**Example**:

```bash
# Exports all the content defined in a module info file.
drush dcem module_name
```

The module's info file would need to have a section like this:

```yaml
default_content:
  node:
    - c9a89616-7057-4971-8337-555e425ed782
    - b6d6d9fd-4f28-4918-b100-ffcfb15c9374
  file:
    - 59674274-f1f5-4d6a-be00-fecedfde6534
    - 0fab901d-36ba-4bfd-9b00-d6617ffc2f1f
  media:
    - ee63912a-6276-4081-93af-63ca66285594
    - bcb3c719-e266-45c1-8b90-8f630f86dcc7
  menu_link_content:
    - 9fbb684c-156d-49d6-b24b-755501b434e6
    - 19f38567-4051-4682-bf00-a4f19de48a01
  block_content:
    - af171e09-fcb2-4d93-a94d-77dc61aab213
    - a608987c-1b74-442b-b900-a54f40cda661
```

---

# Creating a Drupal recipe with Default content

So let’s create a recipe that can store default content we can send to a test server so our QA engineer can test. We’ve created a node that has our new feature on our local environment. Rather than having to rebuild that page manually, we will export the content and apply the recipe after it has been deployed.

## Create the recipe structure

We keep our recipes in the recommended folder above the webroot.

You can manually create a recipe folder, the content folder, and a `recipe.yml` file or use the command line:

```bash
mkdir recipes/recipe-name
mkdir recipes/recipe-name/content
touch recipes/recipe-name/recipe.yml
```

In our `recipe.yml` file, we only need a name, description, and type:

```yaml
name: 'Recipe name'
description: 'Demo content for ticket xyz'
type: 'Content'
```

## Export your content

Use the Drush commands above, usually `dcer`, to export the content you need. Verify in your `/content` folder that you now have the entities you expect. It should look something like this:

(Directory structure omitted for brevity)

## Uninstall the module

We can now uninstall the Default Content module from our local environment.

Normally, we just use recipes in our local development environments and deploy the resulting config. But when leveraging the Default Content module in your recipes, you will deploy them to your server.

Once deployed and applied on your site, you’ll probably want to remove your recipe in a subsequent pull request as you don’t need it in production.

Commit and deploy your new recipe along with your code changes as you normally would.

thejimbirch’s picture

Status: Needs review » Fixed

Thanks!

thejimbirch’s picture

Assigned: nkarhoff » Unassigned

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.