Subscribe for updates and more.

Creating a recipe site with Jekyll

Planted 02020-10-15

In my push for more people to own their own piece of the internet I created a recipe website for my girlfriend.

“No, what would even be on it?”

That’s fair. Working on preparing for job interviews, so resume related stuff would work.

“What do you post/save/etc on social media?”

Recipes.

Now we’re cooking.

Website Stack

My favorite stack for most websites is Jekyll and Netlify. Jekyll builds the site on Netlify, and Netlify CMS takes care of no-code posting and editing. All for free.

Jekyll

Initializing a new Jekyll site is easy. However, getting a new Jekyll site configured how I want it to be is a bit more effort.

The default Jekyll project assumes you’re using a gem theme and doesn’t come with any folders for customization.

A few _includes I bring in to every project:

  • analytics.html
  • footer.html
  • head.html
  • header.html
  • schema.html

After getting the _layouts and _includes all set, I started with a plain markdown document for the homepage. This forces me to not weigh down the page with code and to design with the future in mind.

Starting a site with plain HTML and CSS is wonderful.

Recipes in Jekyll

To add a recipe collection, I created a _recipe folder make Jekyll see the collection by adding some code to the _config.yml file:

collections:
  recipes:
    output: true
    permalink: /:collection/:name/
    layout: "recipe"

Recipe Structured Data

After learning about how to implement structured data on this site previously I’ve wanted to explore more examples of schema data.

Recipes are a perfect example of structured data.

Following the “Get your recipes on Google” guide by Google Search, I laid out the following data points:

  • title
  • description
  • category
  • ingredients
  • cuisine
  • date
  • image
  • instructions
  • calories
  • preptime
  • cooktime
  • servings

These make up the front matter of every recipe post, to be added to from the Netlify CMS and to be used to generate the page and schema data.

Here is an example of what the front matter looks like:

---
layout: recipe
title: Old-Fashioned Goulash
description: description
category: main course
ingredients:
  - 2 Tbsp Olive Oil
  - 2 lbs Lean Ground beef
  - 1 large yellow onion (diced)
  - 3 cloves minced garlic
  - 1 cup low sodium beef broth
  - 2 14.5 oz cans diced Tomatoes with juice
  - 2 15oz cans tomato sauce
  - 2 tsp low sodium soy sauce
  - 2 tsp salt
  - 1 tsp black pepper
  - 1 tbsp paprika
  - 2 cups uncooked elbow macaroni
  - 2 tsp Italian seasoning
cuisine: American
date: 2020-10-15 19:58
instructions:
  - Heat soup pot on medium-high heat. Once hot, add oil, then ground beef.
  - Cook ground beef until browned (about 10 minutes) and spoon off some excess
    fat.
  - Add diced onion and garlic. Cook for four minutes, stirring occasionally.
  - Add beef broth, diced tomatoes, tomato sauce, soy sauce, salt, pepper,
    Italian seasoning, and paprika. Stir well.
  - Reduce heat to low, cover and cook for 15 minutes, stirring occasionally.
  - Stir in uncooked macaroni noodles, cover and cook for an additional 15-25 minutes, or until pasta is cooked.
  - Serve and Enjoy!
preptime: 10
cooktime: 60
servings: 6
---

Adding time in Jekyll

Jekyll uses the liquid templating language created by Shopify.

I wanted to add the preptime and cooktime minutes and display as “X hours XX minutes.”

That is surprisingly difficult.

I’ll spare the details and toiling and show the end result.

{% assign totaltime = recipe.preptime | plus: recipe.cooktime %}
{% assign minutes = totaltime | modulo: 60 %}
<p>{% if totaltime > 60 %}{{ totaltime | divided_by: 60}} hours{% endif %}{% if minutes > 0 %} {{ minutes }} minutes{% endif %}</p>

It’s not perfect, it will say “1 hours,” but it works.

Editing Recipe Structured Data with Netlify CMS

Netlify CMS allows you to edit Jekyll front-matter without dealing with any code or development setup.

Netlify CMS has its own config.yml file that you create in a folder titled admin.

I’ll spare the details and toiling it took to get the end result and just show the code for the Netlify CMS config.yml:

media_folder: "images" # Media files will be stored in the repo under images
public_folder: "/assets/images/" # The src attribute for uploaded media will begin with /images

collections:
  - name: 'pages'
    label: 'Pages'
    editor:
      preview: false
    files:
      - label: 'Home'
        name: 'home'
        file: 'index.md'
        fields:
          - { label: 'Title', name: 'title', widget: 'string', default: 'Change Management & Six Sigma Leader' }
          - {label: "Body", name: "body", widget: "markdown"}
      - label: 'Resume'
        name: 'resume'
        file: 'resume.md'
        fields:
          - { label: 'Title', name: 'title', widget: 'string', default: 'Resume' }
          - {label: "Body", name: "body", widget: "markdown"}

  - name: "recipe" # Used in routes, e.g., /admin/collections/blog
    label: "Recipes" # Used in the UI
    folder: "_recipes/" # The path to the folder where the documents are stored
    create: true # Allow users to create new documents in this collection
    slug: "---" # Filename template, e.g., YYYY-MM-DD-title.md
    fields: # The fields for each document, usually in front matter
      - {label: "Layout", name: "layout", widget: "hidden", default: "recipe"}
      - {label: "Title", name: "title", widget: "string"}
      - {label: "Description", name: "description", widget: "string"}
      - {label: "Category", name: "category", widget: "select", default: "main course", options: ["main course", "dessert", "dinner", "snack", "appetizer", "entree"]}
      - label: "Ingredients"
        name: "ingredients"
        widget: "list"
        field: {label: Ingredient, name: ingredient, widget: string}
      - {label: "Cuisine", name: "cuisine", widget: "string", default: "American", required: false}
      - {label: "Date", name: "date", widget: "datetime", dateFormat: 'YYYY-MM-DD', timeFormat: 'HH:mm', format: 'YYYY-MM-DD HH:mm'}
      - {label: "Image", name: "image", widget: "image", required: false, media_folder: "/assets/images/posts/", public_folder: "/assets/images/posts/", hint: 'Use tinypng.com before uploading!'}
      - label: "Instructions"
        name: "instructions"
        widget: "list"
        field: {label: Instructions, name: instructions, widget: string}
      - {label: "Calories", name: "calories", widget: "number", required: false}
      - {label: "Preparation Time", name: "preptime", widget: "number", hint: "Time in minutes!"}
      - {label: "Cook Time", name: "cooktime", widget: "number", hint: "Time in minutes!"}
      - {label: "Servings", name: "servings", widget: "number", required: false}
      - {label: "Body", name: "body", widget: "markdown", required: false}