Skip to content

Hugo, Jekyll and Eleventy

Next release

How midcode runs a Hugo, Jekyll or Eleventy site, edits its layouts and the words of its Markdown content on the canvas, and where pages and styles come from.

View as Markdown

This ships with the next release of midcode. The version you can download today (1.1.2) doesn’t have it yet.

midcode opens a Hugo, Jekyll or Eleventy site, starts the generator’s own server, and edits two things on the canvas: the HTML written in your layouts and includes, and the words of your Markdown content. What the template language works out ({{ .Title }}, {% for post in site.posts %}) is left to your code.

The generator builds the page, so midcode finds each element in your files after the page loads. Sites a server renders explains how that works and what it means for editing.

At a glance

Hugo, Jekyll and Eleventy
Detected byHugo and Jekyll by their config file. Eleventy by its config file and a script in package.json
Runs withhugo server, jekyll serve, or the Eleventy site’s own dev script
Elements are marked bymidcode, after each page loads, by matching it against the layouts, includes and Markdown files
EditingIn templates: text, classes, attributes and tag; insert, move, duplicate and remove. In Markdown: the words, and a heading’s level
PagesThe files of content/ (Hugo), the files with front matter (Jekyll), the templates (Eleventy). New pages and removing them
StylesTailwind 4 classes, or mid: utilities with midcode.css in the folder the generator copies as it is
Tried withHugo (a base layout, a partial, a range), Eleventy (a Nunjucks layout) and Jekyll 4 (a layout, an include, a loop of posts: 17 of 17 elements found)

How midcode runs it

GeneratorRecognised byCommand
Hugohugo.toml, hugo.yaml, hugo.yml or hugo.json. Or config.toml / config.yaml beside content/ and layouts/ or themes/hugo server --bind 127.0.0.1 --port $PORT
Jekyll_config.yml or _config.tomlbundle exec jekyll serve --host 127.0.0.1 --port $PORT. Without a Gemfile, the same without bundle exec
Eleventyeleventy.config.js (or .mjs, .cjs, .eleventy.js) and a dev script in package.jsonnpm run dev (or pnpm, yarn, bun: the one the project uses)

$PORT is a free port midcode picks. Commands run in your login shell, in the project’s folder, and what they print is in the Dev server log in the top bar. Nothing in the project is changed to run it.

Eleventy is a Node project, so it starts like any project with a dev script: midcode runs the script and reads the address from what Eleventy prints. If there’s no dev script, it uses start or serve. When the script is only a call to eleventy (eleventy --serve) and names no port, midcode adds --port with its own, so the site doesn’t land on Eleventy’s usual 8080, which may be a server of yours: npm run dev -- --port 4310.

You need the generator itself: hugo on your PATH, Ruby with Jekyll (and Bundler, for a site with a Gemfile), or the site’s node_modules for Eleventy. midcode offers to install node_modules when they’re missing; it doesn’t install Hugo or gems.

New project makes an Eleventy or a Hugo site.

What you can edit

Layouts and includes

Anything written as HTML in a template: Hugo’s layouts/, Jekyll’s _layouts/ and _includes/, Eleventy’s _includes/ and pages. {{ }} and {% %} are never touched.

layouts/index.html
{{ define "main" }}
<main class="hero">
  <h1>Fresh bread, every morning</h1>
  <p class="lead">{{ .Site.Params.tagline }}</p>
  {{ range .Site.RegularPages }}
  <a class="post" href="{{ .RelPermalink }}">{{ .Title }}</a>
  {{ end }}
</main>
{{ end }}
  • The heading is written here: double-click it on the canvas.

  • The paragraph’s classes can be edited. Its text comes from the site’s config.

  • The link is one line shown once per page of the range. A class you add applies to all of them.

  • What baseof.html or a partial writes is edited in that file. The right panel shows the file and line under Code.

layouts/index.html
-<main class="hero">-  <h1>Fresh bread, every morning</h1>+<main class="hero mid:p-6">+  <h1>Bread, pastries and coffee</h1>

Markdown content

On these sites, a page’s content is a Markdown file. midcode finds each heading, paragraph and one-line list item of the page in your .md files by its words, and lets you change them:

content/posts/sourdough.md
 --- title: "Sourdough, day by day" ----## What you'll need+## What you need -Flour, water, salt and a week of patience.+Flour, water, salt and one week.
  • Words: double-click a heading, a paragraph or a list item. What Markdown would read as its own (asterisks, underscores, backticks, square brackets, a # at the start of a line) is escaped when it’s written. A paragraph written over several lines is written back as one.

  • Level: change the Tag under Typography in the right panel to make an ## an ###, or a heading a paragraph.

  • Remove it with ⌫.

That’s all Markdown takes. It has no tag to hang a class or an attribute on, so a style edit is refused: “This is written in Markdown: its words can be changed here, not its classes or attributes. Style it from the template around it.” Moving and inserting are refused the same way.

What is and isn’t found:

  • A block has to be only words. A paragraph with emphasis, a link, code, an image or a shortcode in it gets no mark.

  • Front matter, code blocks, tables, quotes and raw HTML are skipped. So are list items that run over several lines and task-list items.

  • Generators curl quotes and join dashes. midcode compares the words without that, so "day by day" in the file matches the curled quotes on the page.

  • When two files write the same words (a subtitle every post has), the file where most of the page’s words were found wins.

  • README.md, CHANGELOG.md, LICENSE.md and the like are never read as content.

A post’s title usually comes from its front matter ({{ .Title }}), not from a heading in the body, so it’s a text the template prints: see the first limit below. A folder of Markdown files with YAML front matter also shows as a collection in the CMS, where those fields are edited.

After an edit

Hugo and Eleventy reload the page themselves. Jekyll rebuilds the site first: midcode reloads the page a moment after the edit and, if it comes back unchanged, again a little later, up to two more times.

Pages

Pages in the top bar is filled from the site’s files:

  • Hugo: every .md or .html file of content/, up to four folders deep. content/about.md is /about, and content/posts/_index.md is /posts. A url: in YAML front matter wins. If no file gives the home page and there’s a layouts/index.html, Home opens that layout.

  • Jekyll: every .html, .md or .markdown file that starts with front matter, at its permalink: or at its own path. Folders that start with an underscore aren’t looked in, so posts aren’t listed one by one.

  • Eleventy: every template outside _includes and _data, at its permalink: or at its own path. A template whose permalink is computed makes one page per item and isn’t listed.

For a page that isn’t listed, type its path in Pages and press Enter.

“New page” in that menu writes a page the way each generator keeps them, and “Remove” moves a page’s file to the Trash. A new page at /about is:

GeneratorWhat’s written
Hugocontent/about.md, with its title in front matter
Jekyllabout.md at the top of the site, with the home page’s layout (default when it names none), a title and permalink: /about/
Eleventyabout.njk beside the home page (.liquid or .html when that’s what the home page is), with the home page’s layout and a title
content/about.md
---
title: "About"
---

A new page, made in midcode.

In Hugo and Jekyll the page is Markdown. In Eleventy it’s a template with a <main>, a heading and a paragraph, and its class names are written when Tailwind is in your package.json. Creating and removing a page was tried on the three.

Site settings

The globe in the top bar opens Site settings. The title, the description, the language, search engines, the favicon and the social image are written as tags in the <head> of a layout: the first template that closes a <head>, in alphabetical order of its path. Images are copied to the same folder as midcode.css (the table below).

What the template computes there (<title>{{ .Site.Title }}</title>, <title>{{ page.title }}</title>) shows as code and isn’t written: it’s still changed where the value is written. There’s one form for the whole site. Tried with Hugo and Jekyll.

Styles

With Tailwind 4 in the project, midcode writes Tailwind’s classes (Tailwind CSS). Without it, midcode writes mid: utilities and keeps their CSS in midcode.css (Without Tailwind), in the folder the generator copies to the site as it is:

Generatormidcode.css goes in
Hugostatic/
JekyllThe project’s top folder
Eleventypublic/, when the config has addPassthroughCopy({ public: '/' })

On your first style edit, every layout that closes a <head> gets one line, as one undoable step:

_layouts/default.html
     <link rel="stylesheet" href="{{ '/assets/site.css' | relative_url }}">+    <link rel="stylesheet" href="/midcode.css">   </head>

The link is the plain path /midcode.css. If the site is published under a sub-path (Jekyll’s baseurl, a Hugo baseURL with a folder), change that line to the form your other stylesheets use.

An Eleventy site that doesn’t copy public/ to its root gets midcode.css written at the top of the project (in src/ when there is one), and midcode says it couldn’t tell where to import it. Add the passthrough copy and the <link> yourself.

Limits

  • Text the template prints is edited where the value is written: front matter, a data file, the site’s config. Double-click one and midcode looks for those exact words in the project’s .md, .json, .js and .ts files, and changes them there when they’re written in one place (Text, images and video). So a title in a post’s front matter or a string in a JSON data file can be found, and a value in a .yml, .yaml or .toml file (the site’s config, most data files) can’t. That search wasn’t tried on these generators.

  • Hugo’s public/ is never read: it’s the built site. Neither are _site, dist, build and out. If your generator writes its pages to a folder with another name, those built files are read as if they were templates, and an element may point at one of them. Check the file under Code before you edit.

  • A theme’s layouts inside the project (themes/<name>/layouts) are matched like your own. Editing a theme you update from elsewhere is a change you’d lose.

  • Markdown was tried with Hugo (Goldmark) and Jekyll (kramdown), not with Eleventy.

  • The Jekyll that was tried ran with a Ruby that isn’t on the PATH, so its command was set in .midcode/server.json. The automatic start runs the same command.

  • Zola (config.toml beside templates/ and content/, run with zola serve --interface 127.0.0.1 --port $PORT) and MkDocs (mkdocs.yml, run with <python> -m mkdocs serve -a 127.0.0.1:$PORT and the project’s Python) are recognised and weren’t tried. Their Markdown content is read the same way as Hugo’s, and midcode doesn’t add pages to them.

  • A url in TOML front matter (+++) isn’t read for Hugo’s page list.

  • The free canvas is for Next.js projects.

Troubleshooting

“hugo” (or “bundle”, “jekyll”) isn’t installed on this Mac. midcode runs the command your login shell finds. Install it, or set the command with its full path in Change how it runs on the error card. It’s saved in .midcode/server.json (Any other stack).

An Eleventy site opens as plain HTML, with {{ }} showing on the page. Its package.json has no dev script and there’s an index.html at the top, so midcode took the folder for a static site. Name the script that serves it dev: "dev": "eleventy --serve".

A Markdown paragraph can’t be edited. It has a link, emphasis or a shortcode in it, so its words aren’t plain in the file. Edit it in the file: switch to Code in the top bar (Code view) or use your editor.

An edit doesn’t show in Jekyll. The rebuild took longer than midcode waited. Press Reload breakpoints in the top bar.