Getting Started with Paragraphs and Bootstrap in Drupal

Paragraphs is a popular module that allows you to create components which can be used on an article or basic page for example. It’s a module which I’ve used on all of my projects for the last half-decade.

Instead of an editor writing all the page content in a text editor, a site builder can create a set of paragraph types that the editor can use to create pages.

An example of a paragraph type could be an accordion, slideshow, or any type of complex component.

The Drupal landscape in the last couple of years has changed thanks to Layout Builder, which is a core Drupal module that lets you control and create layouts on entities. Hence the name Layout Builder. If you want to learn more about the module then look at our Getting Started with Layout Builder in Drupal 8 tutorial.

Now, with Layout Builder on the scene. Does it mean that Paragraphs is dead? NO!

The Paragraphs module is great for implementing complex data models, i.e., grouping fields together. I have worked on projects where they have gone a little overboard with the use of Paragraphs and things got pretty messy. I’m talking about 4 levels of nested paragraphs. But if you control the complexity of the paragraph types and don’t have so many nested levels then you should be fine.

I’ve talked about Paragraphs on this site many times in the past. If you’re looking for a basic introduction then look at our Introduction to the Paragraphs Module in Drupal 8 tutorial.

And we even have a free course on using Paragraphs with Bootstrap 3, using the Bootstrap theme. This course is called Build Edge-to-edge Sites using Paragraphs in Drupal 8.

Seeing as it’s been a while since I’ve last written about Paragraphs, I thought it’ll be great to write a step-by-step tutorial on implementing a Bootstrap 4 component using the Radix theme. This tutorial will step you through each process of, creating the paragraph type, creating a Radix sub-theme, overriding paragraph templates, customizing the widget and using the Paragraphs Library sub-module.

So in this tutorial, we’re going to use Paragraphs and Radix (Bootstrap theme) to allow editors to create card decks in Drupal.

To implement the above design, we’ll need to create two paragraph types: Card Deck and Card.

Card Deck will be used to simply group the individual Card paragraphs.

The Card paragraph will have the following fields:

  • Image (Media field)
  • Title (plain text field)
  • Description (plain text long field)
  • Link (Link field)

Below is a visual representation of how things will work.

Getting Started

Start things off by downloading Paragraphs and Radix.

composer require drupal/radix drupal/paragraphs

With everything downloaded let’s start with the paragraphs. So go ahead and install Paragraphs and Media Library (we’ll need this for the image field).

Run the following if you use Drush to install the modules:

drush en paragraphs media_library

Paragraph Types

The first bit of work we need to do is create the two paragraph types: Card Deck and Card.

Create Card Paragraph Type

1. Go to Structure, “Paragraph Types” and click on “Add paragraph type”.

2. Enter Card into Label and a description if you like, then click on “Save and manage fields”.

3. The Card paragraph type will need four fields: Image, Title, Description and Link.

4. Let’s first create the Image field. Click on “Add field”, select Media from “Add a new field” and add Image into the Label field.

If you can’t see the Media field then make sure you have installed Media Library.

5. On the field settings page for Image, select the Image as the media type from the Reference type section.

This means only the Image media type will be allowed in the field.

6. Now create the Title field, so click on “Add field”. Select “Text (plain)” and enter Title into Label.

Also, make Title a required field.

7. Again click on “Add field”. Select “Text (plain, long)” and enter Description into Label.

8. Last but not least, create the Link field. Select Link from the “Add a new field” and Link as the Label.

On the Link settings page, make the link text required.

At this point, we’ve created the paragraph type and attached fields to it. Let’s now configure the widgets and formatters.

Field Widgets on Card

Click on “Manage form display” and just make sure you’re happy with the order of the widgets.

There’s no right or wrong way to order the widgets. You can change it anytime in the future.

Field Formatters on Card

Next, click on “Manage display” and remove the Label. Just change the drop-down to “- Hidden -” on all the fields.

Create Card Deck Paragraph Type

Now we need to create the second paragraph type called Card Deck. It’ll be used to group the Card paragraphs.

This paragraph type will be very simple. It’ll have a single paragraph field on it.

1. Go to Structure, “Paragraph Types” and click on “Add paragraph type”.

2. Enter Card Deck into Label and click on “Save and manage fields”.

3. Click on “Add field”, Select Paragraph from the “Add a new field” drop-down and enter Cards as the Label.

4. On the “Field settings” page you can limit the number of cards by changing “Allowed number of values”. I’ll set it to 3.

5. Then on the edit page scroll down to the “Reference type” section and only select Card.

6. Next, click on the “Manage display” tab and set the Label to “- Hidden -” on the Cards formatter.

Add Paragraph Field to Content Type

So far we’ve sorted out all the paragraph types and attached fields to them. Now let’s add a Paragraph to an actual content type so we can see it all working.

We’ll add it to the “Basic page” content type but you can add it to any you like.

1. Go to Structure, “Content types” and click on “Manage fields” on the “Basic page” row.

2. Click on “Add field”, select Paragraph and Add Components to the Label field.

3. On the edit page in the “Reference type” section select “Card Deck” and click on “Save settings”.

This means you’ll only be able to select “Card Deck” from the “Basic page” paragraph field.

4. Click on “Manage display” and set the Label drop-down for Components to “- Hidden -“, and reorder it so it’s under Body.

Now go ahead and create a page with a few card components.

Paragraphs Widget

If you’ve ever used the Paragraphs module then you know that you’ll spend a lot of time creating the paragraph content and reordering them.

So I’d like to spend some time talking about the different widgets that come with the module. This is where a lot of the magic happens.

If you go to the “Manage form display” on an entity that has a Paragraph field you’ll notice that there are two widgets: Paragraphs Classic and Paragraphs EXPERIMENTAL.

The classic widget is pretty basic. It’ll display the paragraph fields all expanded and visible.

This is fine when you have just a few paragraphs but the page becomes long and unmanageable once you start adding lots of paragraph items.

The experimental widget allows you to collapse paragraphs so it’s easy to reorder and you can duplicate existing paragraph items. This will allow you to create pages very quickly. But beware it is experimental so make sure you test it out.

1. Go to the “Manage form display” for “Basic page”.

2. Select Paragraphs EXPERIMENTAL, then click on the cog-wheel.

3. From the “Edit mode” drop-down, select “Closed, show nested” then click on Update.

4. Also, don’t forget to switch the widget on the Card Deck paragraph type and configure it the same way.

There’s a lot you can configure on the widget so I’d recommend you spend some time playing around with the settings.

Once everything is configured, your paragraphs should look like:

You can remove and duplicate paragraphs by clicking on the drop-down button:

You can also reorder all the paragraphs in the field by switching to the “drag & drop” view.

Reorder the paragraphs then click on “Complete drag & drop” to save it.

Using Radix (Bootstrap 4)

So far at this point, we’ve done most of the Drupal site building. We created the paragraph types and attached fields to it. Now we need to customize the look-and-feel of the paragraphs so they look like the Cards component in Bootstrap.

Because we’re implementing a Bootstrap 4 component we’ll need to use a Drupal theme that supports it. So we’ll be using Radix.

I like this theme because it supports Bootstrap 4 and comes out-of-the-box with everything configured so you can compile Bootstrap locally. It’s an advanced theme so you need to have npm or Yarn installed and you need to be comfortable running command line tools.

If you’re looking for a detailed tutorial on Radix, then check out Getting Started with Bootstrap 4 using Radix in Drupal 8.

But let’s quickly go through the steps required to create a Radix sub-theme.

Create Radix Sub-theme

1. Make sure you download Radix.

composer require drupal/radix

The theme also depends on the Components module. However, Composer will handle downloading the dependency.

2. Install the Components module and then run the radix:create Drush command to create the sub-theme.

drush en components
drush --include="themes/contrib/radix" radix:create radix_paragraphs

If you see the error “There are no commands defined in the “radix” namespace.”, then make sure the --include path is correct. The include path could be --include="web/themes/contrib/radix" if you’re running the command outside of the Drupal root directory.

3. Go into the sub-theme which you just created and run npm install.

4. Then to compile Bootstrap run npm run dev.

5. Open your Drupal site then go to Appearance and click on “Install and set as default” on the sub-theme.

6. You should see a “working” site but the block placement needs to be fixed up.

7. Go to Structure, “Block layout” and reorder the blocks. I’ve already written about this in the detailed tutorial on Radix. So head over there to see the correct order, then come back here.

You should now have a working Radix sub-theme let’s now implement the cards.

Implement Card Component

We’ll start on the card deck paragraph type.

The only customization we need to do is add the .card-deck class to the paragraph.

We can do this by implementing the hook_preprocess_paragraph__card_deck hook in the sub-theme.

1. Open radix_paragraphs.theme and paste the following:

 * Implements hook_preprocess_paragraph().
function radix_paragraphs_preprocess_paragraph__card_deck(&$variables) {
  // Add .card-deck class.
  $variables['attributes']['class'][] = 'card-deck';

2. Next we need to override the field.html.twig for the field_cards paragraph field.

Copy radix/templates/field/field.html.twig from the Radix theme into radix_paragraphs/templates/field/field--paragraph--field-cards.html.twig.

Make sure you change the name of the twig file to field--paragraph--field-cards.html.twig. This means that the Twig template will only be used on the field_cards field.

Replace the whole template with the following:

 * @file
 * Default template for a field.
{% for item in items %}
  {{ item.content }}
{% endfor %}

Essentially what we’re doing here is removing any wrapping DIV between .card-deck and .card.

3. Now we need to implement the markup for the actual card.

Copy paragraphs/templates/paragraph.html.twig from the Paragraphs module into radix_paragraphs/templates/paragraphs/paragraph--card.html.twig (you’ll need to create the paragraphs folder).

Again, make sure you rename paragraph.html.twig to paragraph--card.html.twig.

Replace the template with the following:

  set classes = [
    'paragraph--type--' ~ paragraph.bundle|clean_class,
    view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class,
    not paragraph.isPublished() ? 'paragraph--unpublished',
{% block paragraph %}
  <div{{ attributes.addClass(classes) }}>
    {% block content %}
      {{ content.field_image }}
      <div class="card-body">
        <h5 class="card-title">{{ content.field_title }}</h5>
        <p class="card-text">{{ content.field_description }}</p>
        {{ content|without('field_title', 'field_description', 'field_image') }}
    {% endblock %}
{% endblock paragraph %}

Once all the templates have been overridden do not forget to run drush cr.

Create Card Deck Paragraph

Now that the front-end has been sorted out we can go ahead and create the actual paragraph on the “Basic page” content type.

1. Go to Content, “Add content” and click on “Basic page”.

2. Scroll down to the Components field and you should see the Card form expanded. Fill in the edit form.

3. Then click on “Add Card” to create another card.

4. Once you’ve finished creating the cards go and save the page. Then view the page and you should see something like the screenshot below:

5. If you edit the page, you should see the paragraphs widget showing you the cards in a closed state.

If you need to change a card just click on the Edit button.

Create Reusable Paragraphs using Paragraphs Library

The Paragraphs module comes with a very useful sub-module called Paragraphs Library. This sub-module allows you to create reusable paragraphs which can be created once and then added on many pages.

The sub-module requires the Entity Usage module to track which pages it’s used on. So go ahead and download the module.

composer require drupal/entity_usage

Then go and install Paragraphs Library.

Once everything is installed we need to create the reusable paragraph.

1. Go to Content and click on the Paragraphs tab.

From this page you can create and manage all the reusable paragraphs.

2. Click on “Add library item”, enter in the name of the paragraph into Label, then click on “Add Card Deck”.

3. Go and create the paragraph like you would on any other piece of content.

4. Once you’ve created the paragraph you should see it on the Paragraphs page.

Add Reusable Paragraph to Page

Now that we’ve created the reusable paragraph, let’s add it to a page.

But, before we can do that we need to make a slight change to our widget on the “Basic page” content type.

1. Go to Structure, “Content types” and click on “Manage fields” on the “Basic page” row.

2. Edit the Components field and in the “Reference type” section, select “From library” from the Type checkbox list.

This will allow us to select the “From library” paragraph type on pages which is required if you want to reuse a paragraph.

3. Go edit an existing page and click on “Add From library”.

4. Then select the reusable paragraph by searching for it in the autocomplete field using its label.

5. Save the page and you should see the reusable paragraph.

If you go back to Content, Paragraphs, you can edit the paragraphs and see where they are used.

You can click on the number in the “Used” column to see where the paragraph is used.

One thing I do want to mention is how the usage is calculated. The amount in the “Used” column tells you how many revisions a paragraph is used on, not how many pages or articles it’s used on.

If I’m using a paragraph on a single page, but the single page has multiple revisions. Then those revisions will be counted. However, you can easily see which entity and field the paragraph is being used on by simply clicking on the usage amount and seeing the usage information.


If you’ve made it to the end of this tutorial then hats off.

It’s pretty amazing what you can do with just Paragraphs, implementing the component’s structure, and Bootstrap for the actual components. But if you combine all that by using the experimental widget and Paragraphs Library then you have a powerful content producing machine.


13 thoughts on “Getting Started with Paragraphs and Bootstrap in Drupal”

  1. Gonçalo Dumas

    Hi Ivan. Great tutorial, as usual! I followed every step and managed to successfully replicate this card deck. One question remains unanswered: {{ content.field_image }} does not output the bootstrap class ”card-img-top” and so the image does not fully adapt do the card outline and rounded corners. Is there a way to output the image field as a simple url? Or alternatively add the class to the img tag outputted by drupal?

    1. Hi Gonçalo,

      Yes, I’m aware I skipped the “card-img-top”. I didn’t want to spend another 15 minutes explaining how to add the class for just rounded edges. But good job for picking it up. 🙂

      This class can be added in a few ways:
      – Try and get the class into the image template
      – Add the img element in the card component. Just google “drupal 8 media image URL twig”, you’ll find a lot of examples.
      – Another way (haven’t tested) is using sass “@extend .card-img-top”


  2. Any know the best practices for enabling comments on individual paragraphs? I would like to let people add annotations.

  3. We’ve worked with nested Paragraphs a couple of years, have you ever tried Entity Reference Layouts instead of the default Paragraphs form widget(s)? This was a HUGE improvement for us, especialy for the content authors:
    Moving Paragraphs to other layout regions, without deleting and recreating them. All layout related stuff goes to Drupals layouts system.. layouts also can have settings, eg. with this module:

    Kind regards

  4. Ivan!

    Thanks from a Drupal newbee for the tutorial. Working as advertised. Then I turned on layout builder and it broke the card-deck. Not sure why. Once I disabled layout builder. everything worked again. Any ideas?

  5. Hi Ivan,
    I’m kind of late to the show but let me thank you for the tutorial. I’ve been away from Drupal for a few years and this was a perfect intro for my first D9 project.
    I did run into one issue trying to use the class .card-deck for Bootstrap. Maybe it’s been deprecated? But when I substituted in the class .card-group the cards lined up nicely .
    Thanks again,

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top