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.
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 by | Hugo and Jekyll by their config file. Eleventy by its config file and a script in package.json |
| Runs with | hugo server, jekyll serve, or the Eleventy site’s own dev script |
| Elements are marked by | midcode, after each page loads, by matching it against the layouts, includes and Markdown files |
| Editing | In templates: text, classes, attributes and tag; insert, move, duplicate and remove. In Markdown: the words, and a heading’s level |
| Pages | The files of content/ (Hugo), the files with front matter (Jekyll), the templates (Eleventy). New pages and removing them |
| Styles | Tailwind 4 classes, or mid: utilities with midcode.css in the folder the generator copies as it is |
| Tried with | Hugo (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
| Generator | Recognised by | Command |
|---|---|---|
| Hugo | hugo.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.toml | bundle exec jekyll serve --host 127.0.0.1 --port $PORT. Without a Gemfile, the same without bundle exec |
| Eleventy | eleventy.config.js (or .mjs, .cjs, .eleventy.js) and a dev script in package.json | npm 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.
{{ 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.htmlor a partial writes is edited in that file. The right panel shows the file and line under Code.
-<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:
--- 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.mdand 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
.mdor.htmlfile ofcontent/, up to four folders deep.content/about.mdis/about, andcontent/posts/_index.mdis/posts. Aurl:in YAML front matter wins. If no file gives the home page and there’s alayouts/index.html, Home opens that layout.Jekyll: every
.html,.mdor.markdownfile that starts with front matter, at itspermalink: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
_includesand_data, at itspermalink: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:
| Generator | What’s written |
|---|---|
| Hugo | content/about.md, with its title in front matter |
| Jekyll | about.md at the top of the site, with the home page’s layout (default when it names none), a title and permalink: /about/ |
| Eleventy | about.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 |
---
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:
| Generator | midcode.css goes in |
|---|---|
| Hugo | static/ |
| Jekyll | The project’s top folder |
| Eleventy | public/, when the config has addPassthroughCopy({ public: '/' }) |
On your first style edit, every layout that closes a <head> gets one line, as one undoable step:
<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,.jsand.tsfiles, 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,.yamlor.tomlfile (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,buildandout. 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.tomlbesidetemplates/andcontent/, run withzola serve --interface 127.0.0.1 --port $PORT) and MkDocs (mkdocs.yml, run with<python> -m mkdocs serve -a 127.0.0.1:$PORTand 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
urlin 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.