Bringing in a third-party library for easy, reliable line numbering
Draft Eleventy posts are included on the front end in development mode only, not on the production site. When using version control software (VCS) the simplest way to make a post dev-only is to not track it— with Git, for example, do not commit the file. But with that approach you miss out on tracking changes to the draft, and you can’t distinguish drafts from published posts on the front end. Another option is a dedicated drafts’ Eleventy collection. But that approach can require reworking plugins to consume both the published posts’ collection and the drafts’ collection (for example when displaying all or related posts); and, with Eleventy’s path-based collections, publishing will require relocating the post file which can complicate looking at its VCS history. A good third option hinges on adding a new “draft” data field to posts.
Contents
We’ll need a way to distinguish development and production environments. The standard solution is to store an environment-specific variable in an .env file, and to use dotenv to access the env variable in the site JS.
gitignore the path .env. .env files are often used to store sensitive information (read Algolia Search in VuePress Without Joining DocSearch for an example). Here, it will help us distinguish between “dev” and non-“dev” environments.
text.env
text.env
Create a file .env in your project root, and define DEV to be true.
yamlDEV=true
yamlDEV=true
I like to use an .env.example file to keep a record of all expected environment variables. I leave out sensitive information; “true” isn’t sensitive, so it’s safe to leave in. Then, if you or someone else clones the project, they can cp .env{.example,}.
yamlDEV=true
yamlDEV=true
Add the dotenv dependency. Instructions in the dotenv docs. In my case, that’s
shellpnpm add -D dotenv
shellpnpm add -D dotenv
In production, draft should not be written to the file system (the page should not exist) and they should be excluded from collections (keep them out of post archives, “related post” sections, and anything else that uses a collection).
Setting eleventyExcludeFromCollections: true in a post’s front matter will exclude it from collections.[1] Setting permalink: false will keep it from being written to the file system.[2] So one way of marking a post as a “draft” is
md---# …eleventyExcludeFromCollections: truepermalink: false# …---<!-- … -->
md---# …eleventyExcludeFromCollections: truepermalink: false# …---<!-- … -->
But if you set eleventyExcludeFromCollections: true or permalink: false on any posts for a reason other than its being a draft, keeping track of true drafts will be tough.
The solution is to make that data dynamic. Then draft status and publication date can be used as factors in eleventyExcludeFromCollections and permalink.
Use eleventyComputed for dynamic data.[3] Use a collection-level JS data file to define dynamic data for all posts in a collection.[4]
jsrequire('dotenv').config(); // or ESM: `import 'dotenv/config'`const dev = process.env.DEV === "true";const now = new Date();/*** If a post is a `draft`, it is for dev mode only.** @param {object} data Post data* @param {boolean} [data.draft=false] Post draft status* @returns {boolean}*/function devOnly(data) {return Boolean(data.draft);}module.exports = {eleventyComputed: {eleventyExcludeFromCollections: data => {if (!dev && devOnly(data)) {return true;}return data.eleventyExcludeFromCollections;},permalink: data => {if (!dev && devOnly(data)) {return false;}return data.permalink;},},// …}
jsrequire('dotenv').config(); // or ESM: `import 'dotenv/config'`const dev = process.env.DEV === "true";const now = new Date();/*** If a post is a `draft`, it is for dev mode only.** @param {object} data Post data* @param {boolean} [data.draft=false] Post draft status* @returns {boolean}*/function devOnly(data) {return Boolean(data.draft);}module.exports = {eleventyComputed: {eleventyExcludeFromCollections: data => {if (!dev && devOnly(data)) {return true;}return data.eleventyExcludeFromCollections;},permalink: data => {if (!dev && devOnly(data)) {return false;}return data.permalink;},},// …}
Now, setting draft: true in a post’s front matter will make it a draft, and it will not be part of production builds.
md---title: This is a draftdraft: true---<!-- … -->
md---title: This is a draftdraft: true---<!-- … -->
With a bunch of drafts in the hopper, “all posts” lists can be cluttered and significantly different in development from in production. Consider grouping drafts together. The Eleventy docs suggest creating a new collection with a custom sort[5]; I prefer a custom Eleventy filter.
jsmodule.exports = function(eleventyConfig) {// …eleventyConfig.addFilter("collectionOrder", (posts) => {const drafts = [];const published = [];for (const post of posts) {if (post?.data?.draft) {drafts.push(post);continue;}published.push(post);}return [...drafts, ...published];});// …}
jsmodule.exports = function(eleventyConfig) {// …eleventyConfig.addFilter("collectionOrder", (posts) => {const drafts = [];const published = [];for (const post of posts) {if (post?.data?.draft) {drafts.push(post);continue;}published.push(post);}return [...drafts, ...published];});// …}
twig<ul>{% for post in collections.posts %}{% for post in collections.posts|collectionOrder %}<li>{{ post.title }}</li>{% endfor %}</ul>
twig<ul>{% for post in collections.posts %}{% for post in collections.posts|collectionOrder %}<li>{{ post.title }}</li>{% endfor %}</ul>
Posts’ draft data can be used to surface draft status on the front end:
twig<ul>{% for post in collections.posts|collectionOrder %}<li>{{ post.data.title }}{% "(draft)" if post.data.draft and not post.data.draft == "false" %}</li>{% endfor %}</ul>
twig<ul>{% for post in collections.posts|collectionOrder %}<li>{{ post.data.title }}{% "(draft)" if post.data.draft and not post.data.draft == "false" %}</li>{% endfor %}</ul>
Numbered Code Block Lines in Eleventy with Shiki Twoslash
Bringing in a third-party library for easy, reliable line numbering
Highlight Code Block Lines In Eleventy with Shiki Twoslash
Eleventy Markdown code block line highlighting made simple
Fundamental Twig for Front-End Development
Your comprehensive guide for Twig front-end view templates.