Eleventy / Netlify CMS Modular List Widget

Hey There,

I have just started using Eleventy and Netlify together to build some static sites. I wanted to emulate a “page builder” for the pages, so users will be able to choose elements from a list and populate those fields. With the lists widget I also notice you can drag and drop different sections for order.

What is the best way to set this up or is it even possible with the list widget currently. Can you use components for these sections and still have the drag and drop working? Is there a better way to setup the structure?

Below you will find the pages section for config.yml

- name: "pages"
    label: "Page"
    folder: "pages"
    create: false # Change to true to allow editors to create new pages
    slug: "{{slug}}"
    fields:
      - { label: "Title", name: "title", widget: "string" }
      - { label: "Publish Date", name: "date", widget: "datetime" }
      - { label: "Permalink", name: "permalink", widget: "string" }
      - label: "Navigation" # https://www.11ty.dev/docs/plugins/navigation/
        name: "eleventyNavigation"
        widget: "object"
        fields:
          - { label: "Key", name: "key", widget: "string" }
          - { label: "Order", name: "order", widget: "number", default: 0 }
      - label: "Page Layout"
        name: "page_layout"
        widget: "list"
        types:
         - label: "Body Section"
           name: "body_section"
           widget: "object"
           fields:
             - { label: "Body Updated", name: "body_updated", widget: "string" }
         - label: "Left Align"
           name: "left_align"
           widget: "object"
           fields:
             - { label: "Left Align Image", name: "left_align_image", widget: "image" }
             - { label: "Left Align Title", name: "left_align_title", widget: "string" }
             - { label: "Left Align Body", name: "left_align_body", widget: "markdown" }
             - { label: "Left Align Link", name: "left_align_link", widget: "string" }

Hi @dana-blackbean :wave: Welcome to the community!

The list widget variable types is the way to go there I think.

If you want to get really fancy you can create a custom widget:

Is there something specific you’re trying to accomplish that is not working?

Hey @erez thanks for the reply!!

Yea, so what I am trying to accomplish is to be able to create say 5 different modular blocks for a website. From the backend you would be able to pick and choose those blocks from a list and have them populate on page. Backend users would essentially be able to create custom pages using those blocks.

I found this thread and this is basically what I am looking for https://github.com/netlify/netlify-cms/issues/1066#issuecomment-366101956

Now I just need to figure out on how best to implement this! Any Tips??

hi @dana-blackbean - we don’t necessarily have the bandwidth to give too much guidance on implementation - maybe one of our volunteers such as @tomrutgers has ideas - but if you experiment with the widgets and run into specific problems, feel free to post again!

Hey @perry no problem! I have already started playing around with getting this to work correctly. Any
11ty or Netlify advice on this would be awesome.

Thanks again!

Here’s an example collection with some basic widgets:

collections:
  - name: pages
    label: Pages
    folder: data/pages/
    create: true
    slug: '{{title}}'
    editor:
      preview: false

    fields:
      - {name: title, widget: string}
      - {name: layout, widget: select, options: ['grid', 'full-width', 'about', 'contact']}

      - name: sections
        widget: list
        types:
          - name: text_section
            label: Text
            widget: object
            fields: 
              - {name: text, widget: markdown}

          - name: image_section
            label: Image
            widget: object
            fields: 
              - {name: image, widget: image}
              - {name: background-color, widget: select, options: [Dark, Medium, Light], required: false}


          - name: slider_section
            label: Slider
            widget: object
            fields:
              - name: images  
                widget: list
                field: {label: Image, name: image, widget: image}
              - {name: background-color, widget: select, options: [Dark, Medium, Light], required: false}

          - name: video_section
            label: Video
            widget: object
            fields:
              - {name: embed, label: Embed video, hint: 'YouTube or Vimeo url'}
              - {name: background-color, widget: select, options: [Dark, Medium, Light], required: false}

1 Like

Hey @tomrutgers,

Thanks for the example! I have a simple layout called page.njk

---

layout: layouts/base.njk

section: page

---

<h1>{{ title }}</h1>

{{ layoutContent | safe }}

How would I use the above collection on the page in order to call the different “sections”. My main concerns are keeping the ability to drag and drop the order. I am planning on using widgets to create all the “sections” then calling them on page but, not 100% on the logic.

The CMS will output its sections data something like this:

sections:
  - text: >-
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus aliquet
      tortor massa, in pretium sem tempor non. Donec interdum porttitor sapien
      vitae blandit. Phasellus a orci a lorem maximus lobortis ac ac mi. Sed
      commodo ut risus quis vulputate. Aenean pellentesque, erat non volutpat
      porttitor, massa erat blandit massa.
    type: text_section
  - embed: 'https://vimeo.com/123456789'
    type: 'video_section'
  - images:
      - /img/uploads/image-01.jpg
      - /img/uploads/image-02.jpg
      - /img/uploads/image-03.jpg
    type: slider_section

Unless you manipulate the data yourself, it will be in the right order when you iterate over it in your layout. I haven’t used eleventy with .njk layouts but it should be something like: {% for section in sections %}

1 Like

Okay I have the data passed through like so

---
title: About
date: 2017-01-01T00:00:00.000Z
permalink: /about/index.html
eleventyNavigation:
  key: About
  order: 1
page_layout:
  - type: left_align
    left_align_image: /static/img/logo.png
    left_align_title: LEFT ALIGN TITLE
    left_align_body: LEFT ALIGN BODY
    left_align_link: https://testestes.com
  - type: body_section
    body_updated: This is body text from the body section
---

So it would be something like {% for page_layout in pages %}? Once I get here I am a little stuck - how can I setup the HTML structure for these fields now?

hi @dana-blackbean, have you been continuing to experiment?

Hey @perry,

Yea, so I have now figured out the looping

{% for type in page_layout %}
  <li>{{ title }}</li>
{% else %}
  <li>This would display if the 'item' collection were empty</li>
{% endfor %}

That will pull in the 2 sections. Now, I am a little confused of how to access the fields in the list. Say I wanted to use the left_align_image, how would I call that with .njk?

Something like this?

{{ left_align.left_align_image }}

You’ll have to check the list for conditionals using the if statement within the for loop. To access every individual list item, you’ll have to use the range global function. Step by step:

  • You need to loop over all the items in the list using the for loop
  • You need to access every individual item by combining with the range function
  • Once you can target the data you need, you can check the type of the list item
  • Once you know the type, you can display the right data

Be aware, I still haven’t used the njk templating language so this example might by a bit off syntax-wise but it might give you some leads nonetheless:

{% for i in range(0, page_layout) -%}
  {% if page_layout[i].type == 'left_align' %}
    {{ page_layout[i].leftalign_image}}

  {% elif page_layout[i}.type == 'body_section' %}
   ...
  {% endif %}
{%- endfor %}

My advice would be to get a better understanding of the .njk templating language before you try to process data with it. Luckily the documentation is quite extensive.

Thanks for the pointers @tomrutgers!! Yes ,Nunjucks is pretty new to me but, I am really close!

{% for type in page_layout %}

  {% if type.page_layout == left_align %}
    <section class="left-align">
    {{ type.left_align_image }}
    {{ type.left_align_title }}
   {{ type.left_align_body }}
   {{ type.left_align_link }}
    </section>
{% endif %}


  {% if type.page_layout == body_section %}
    <section class="body-section">
    {{ type.body_updated }}
    </section>
  {% endif %}

{% endfor %}

Right now this loops through both blocks every time when the “FOR” is used. When I add the “ELIF” to the second block (where IF is) it does not pull that data correctly from it. It only pulls in the first block. What am I missing here?

That might be because you closed the first if statement and then opened the second with an “ELIF”. Maybe something like this?

{% for type in page_layout %}

  {% if type.page_layout == left_align %}
    <section class="left-align">
      {{ type.left_align_image }}
      {{ type.left_align_title }}
      {{ type.left_align_body }}
      {{ type.left_align_link }}
    </section>

  {% elif type.page_layout == body_section %}
    <section class="body-section">
      {{ type.body_updated }}
    </section>
  {% endif %}

{% endfor %}

I have to add that this thread is becoming rather out of scope of the level of support that this forum can provide. I’m glad that you have figured out the Netlify CMS side of things, but you might be better off reaching out to other Eleventy users to shed some more light on your case. Good luck, you’ve made great progress so far! :muscle:

1 Like

ooops, that was left in when it was copy’d over. :frowning: That closing “if statement” is not there in the full build. Still having a couple problems but on the right track. Thanks for the assistance @tomrutgers

2 Likes