Eleventy: Programmatically create pages from data files

Created on 26 May 2018  路  10Comments  路  Source: 11ty/eleventy

Maybe I am missing something in the docs but I have an array of data from contentful that needs to get turned into pages using the same template. I'd love to hear how you'd approach this currently, but I think it would be amazing to be able to do something like this:

// projects is an array of objects that each look like 
// { ... title: 'whatever', description: 'whatever' ...}

projects.forEach(project => {
    createPage({
        data: project,
        template: 'project.njk',
        dir: /projects
    })
}
duplicate enhancement

Most helpful comment

I was all set to vote for this until I discovered how pagination can do this for me. SO slick.

layout: layouts/base.njk
pagination:
  data: eventslist.events
  size: 1
  alias: event
permalink: events/{{ event.date }}/index.html
---
<h1>{{ event.title }}</h1>
<p>{{ event.date }}</p>
...

鉂わ笍

All 10 comments

We don鈥檛 support a programmatic API yet but this is the ground that the pagination module covers!

https://github.com/11ty/eleventy/blob/master/docs/pagination.md

Moving this to be a feature request. This will require upvotes to get traction.


This repository is now using lodash style issue management for enhancements. This means enhancement issues will now be closed instead of leaving them open.

The enhancement backlog can be found here: https://github.com/11ty/eleventy/issues?utf8=%E2%9C%93&q=label%3Aneeds-votes+sort%3Areactions-%2B1-desc+

I was all set to vote for this until I discovered how pagination can do this for me. SO slick.

layout: layouts/base.njk
pagination:
  data: eventslist.events
  size: 1
  alias: event
permalink: events/{{ event.date }}/index.html
---
<h1>{{ event.title }}</h1>
<p>{{ event.date }}</p>
...

鉂わ笍

i am using this approach to convert a big json file into a bunch of pages. there are a couple of pain points with this though

  1. because the pages are created via pagination, if i add a tag to the front-matter, eg posts, each item isnt properly added to collections.posts
  2. because of 1, if i want to generate a sitemap entry, i currently have to manually create a collection which i populate by loading the json file, which isnt ideal either
  3. because front-matter yaml doesnt process template vars, i have to use renderData to set things like title and description in the main layout, and have to use unwieldy things like title = (renderData && renderData.title ? renderData.title : title) in the main layout, instead of just being able to use title

theres a few other things that make using the pagination size:1 approach for converting a data file into a bunch of pages not quite ideal. its close enough to get it going but it would be nice if either this pagination approach was fine-tuned to allow for generated 'pages' to be treated like unique items if size == 1, or perhaps if there was another way to turn a data file into a bunch of pages

A _big_ use case for me when it comes to programmatically generating pages is the following:

I have a posts array that includes posts in multiple languages. If I want to create multiple paginated and localized post list pages, what do I do? One way is to create multiple collections with a key of post-${lang} and then creating multiple .11ty.js files (my templating language of choice) that paginate over each collection. Here's an example:

const lang = 'en'

class BlogTemplateEN {
  data() {
    return {
      pagination: {
        data: `posts-${lang}`,
        size: 1
      },
      permalink: props => {
        return `/${lang}/blog${
          props.pagination.pageNumber === 0
            ? ""
            : `/${props.pagination.pageNumber + 1}`
        }/index.html`;
      }
    };
  }
  render(props) {
    // final template...
  }
}

Problem is: I then have to copy and paste the same file, solely changing the lang to something else. I can't even abstract the BlogTemplate class into its own file and spin an instance of it with a different lang, as Eleventy won't be able to pass down its props to it... And say I have to include a new language: instead of simply changing a configuration, I now have to create a whole new file 馃様

I'm super open to ways of solving this specific problem, but overall I miss an imperative API for creating pages as it'd be much easier to leverage JS without having to get into data strings, sizes and other Eleventy-specific ways of doing pagination (which I don't think is super intuitive, honestly!).

558 and #463 will greatly help with this, as they'll give more power to JS, but it still won't be quite there yet, IMO! And hey, I'm down to helping with this API, count on me 馃槃

@hcavalieri can't you do something like this:
BlogTemplate.js

class BlogTemplate {
  language;
  data() {
    return {
      pagination: {
        data: `posts-${this.language}`,
        size: 1
      },
      permalink: props => {
        return `/${this.language}/blog${
          props.pagination.pageNumber === 0
            ? ''
            : `/${props.pagination.pageNumber + 1}`
        }/index.html`;
      }
    };
  }
  render(props) {
    // final template...
  }
}

export default BlogTemplate;

BlogTemplateEn.11ty.js

import BlogTemplate from './BlogTemplate';
class BlogTemplateEn extends BlogTemplate {
  language = 'EN';
}

That way you don't need to duplicate code.

That makes perfect sense, @therealshark ! I was trying to implement something similar but failed miserably, not much into classes haha

The thing is, though, this approach has limitations: you have to create on file for each language, for each template. Say I'm fetching an array of languages from an API and their respective content: how would I go about setting this up? One way is to manually create files, or to pre-fetch the languages and use fs to create these template files before running Eleventy... Regardless of how, it's not the nicest workflow, right?

Honestly, I'm a bit of a control freak and really want to get into the nitty gritty of how and when pages will be created, and that's why I'm putting Eleventy on hold for a while in favor of other options such as Metalsmith. Love the project, would love to use it, but it needs to work better on Windows (less bugs) and allow for controlling page creation. And be sure that I'll contribute if I find the time in the near future 馃槃

Thanks for the input, though, definitely helped o/

Related #620

I was all set to vote for this until I discovered how pagination can do this for me. SO slick.

layout: layouts/base.njk
pagination:
  data: eventslist.events
  size: 1
  alias: event
permalink: events/{{ event.date }}/index.html
---
<h1>{{ event.title }}</h1>
<p>{{ event.date }}</p>
...

鉂わ笍

But does it work for the large collection? I have 24k pages to generate. If I fetch whole data in a single call (in _data folder) then the response will be huge. it may fail.
I am looking for one by one web page generation. wondering how to do that in 11ty

Going to defer this one to #620鈥攁lthough this one was created earlier. #620 has a bit better discussion and example code 馃憤馃徎

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kaloja picture kaloja  路  3Comments

DirtyF picture DirtyF  路  3Comments

zachleat picture zachleat  路  3Comments

zachleat picture zachleat  路  3Comments

veleek picture veleek  路  3Comments