includemarkdownjekyllyaml-front-matter

Jekyll: including a post inside another post


Does Jekyll provide a way to include a post inside another post? I know that sounds a bit goofy, but I'm using it for a cooking/recipe site. Some of my recipes are made up of components, or other, smaller recipes.

I'm looking for a way to include a handful of posts inside another post, complete with template and all. Is this possible?

I did a ton of googling, and found I was only able to include into a page, not post, and it didn't use the default liquid template, just plain markdown.

Ideas?


Solution

  • I used Jekyll's Collections in my website to solve a problem similar to yours.

    Collections are still an experimental feature in Jekyll, and the solution I used is pretty hacky by itself, but if you don't go too crazy with it it's manageable enough.

    For a more detailed and friendly intro to collections, here's a great post about getting started with them.

    One thing we need to have in mind is that we should get away from posts for cases like this.

    As mentioned by Ben Balter in the link above, “If people are using blog posts for a non-blog post thing, Jekyll has already failed”. Recipes are not posts, and we should avoid using them as such if we can.

    With that said, here's my approach in three steps:

    1. Create two collections (our custom post types) — let's say these are “Recipes” and “Ingredients”

    2. Find a way to relate them to each other — To make sure “lettuce” will be listed under “salad”

    3. Include the relevant “Ingredients” in your “Recipes” — Adding the liquid code to actually display information from “lettuce” somewhere in the “salad” page.


    Step 1

    Create _ingredients and _recipes folders for each of your collections (make sure to name them in plural) and add them to your _config.yml file:

        collections:
          ingredients:
            output: true 
          recipes: 
            output: true  
            permalink: /recipes/:path/  
    

    The output: true parameter creates an individual HTML page for each item in the collection and permalink (optional) lets you control the URL.


    Step 2

    Add a piece of metadata like this to the YAML front-matter of your lettuce.md collection item:

        ---        
        parent-collection: salad
        ---
    

    If lettuce.md belongs to more than one recipe, you can add more than one. Make sure to add content below the metadata if you need a description:

        ---
        parent-collection: 
         - salad
         - hamburguer
         - taco
        ---
    
        Lettuce is really good for your health, but it kinda sucks as a food.
    

    Note that salad, hamburguer and taco should named exactly like the recipe URL slug or this won't work (i.e. greatrecipes.com/recipes/salad). If you have a recipe name that's a sentence, you wrap it in quotes.

    This variable will be slugified later on, so a name like parent-collection: The Amazing Souflè Français will match the-amazing-soufle-francais. Still, weird stuff happens: when in doubt, just write smallcaps salad.

    This is the hacky part.


    Step 3

    Create a specific layout for your recipes (like _layout/recipe-page.html) and add the code below — this is where we'll include the ingredients into the recipe page.

    Remember your recipes (salad.md and friends) should be pointing to this layout.

        {% capture current_collection %}{{ page.url | remove: "recipes" | remove: "/" | remove: " " }}{% endcapture %}
    

    Here we capture the name of the recipe from the URL and assign it to the current_collection variable. Make sure this in a single line because making it multi-line will add a bunch of whitespace to the captured string and break your code 10/10 times. Insane.

        <div class="recipe-content">
        <p>{{ content }}</p> -- This is the content of the current recipe in the collection, your "post text".
        </div>
    
    
        {% for ingredient in site.ingredients %}
    
        {% capture captured_parent_collection %}{{ ingredient.parent-collection | slugify }}{% endcapture %} -- This capture is just to pass the slugify filter and sanitize parent.collection to make sure it matches the URL slug   
    
          {% if captured_parent_collection == current_collection %}
            <div class="recipe-ingredient">
              <h3>{{ ingredient.name }}</h3> -- "<h3>Lettuce</h3>"
              <p>{{ ingredient.content }}</p> -- "<p>Lettuce is really good for your health, but it kinda sucks as a food.</p>"
            </div>
          {% endif %}
        {% endfor %}
    

    Match the ingredients for this recipe and includes each one of them into the layout. YASS!

    Each of your ingredients should be appearing right under your recipe text.

    Note that you're not pulling the ingredients inside the recipe text like you described in your question, but including them in the layout after it. As far as I know you can't really add things in the middle of your post/collection text — unless you hack your way through a custom plugin for Jekyll.


    There are certainly less hacky ways to do this, but this is how I made it work. Let me know if you find any trouble trying this for yourself — and anyone else, feel free to correct me.

    I have this setup going on in my site — take a look at the layout where I pull in my child collection items and the metadata on the child collection items themselves.