Mike Slinn

jekyll_draft

Published 2020-10-03. Last modified 2025-01-10.
Time to read: 7 minutes.

This page is part of the jekyll_plugins collection.
The version described in this article has not been released yet. Performance is not where I want it to be yet. You can build it as documented, or wait a week or two, and it will probably become available.

Jekyll has various ways of specifying that a page or document is visible when the website is published in production mode. The Jekyll documentation is scattered and incomplete regarding detecting draft pages and documents. This plugin provides standard ways for detecting draft pages and documents.

Jekyll_draft provides the following:

  • Jekyll block tags: if_draft, if_page_draft, unless_draft, and unless_page_draft.
  • Jekyll inline tags: else_if_draft, else_if_page_draft, else_unless_draft and else_unless_page_draft. These are meant for use within the above block tags. They are all identical; they are provided so, usage seems natural.
  • Jekyll inline tag draft_html, which generates HTML that indicates if the document it is embedded within is a draft.
  • Liquid filters:
    • is_draft returns a boolean indicating if the document passed to it is a draft.
    • page_match_is_draft is like is_draft, but works on other pages.
    • draft_html returns the same string the draft_html tag returns, indicating if the Jekyll::Page passed to the filter is a draft.
    • page_match_draft_html is like draft_html, but works on other pages. It accepts a string that will be matched against APage.href values in site.everything.
  • Module Jekyll::Draft defines an API that your plugin can call. It has the following methods:
    • draft? returns a boolean indicating if the object passed to it is a draft. This method handles AllCollectionsHooks::APage instances, Jekyll::Page instances, and Jekyll::Document instances.
    • draft_html accepts the same parameter types as draft?, and returns the same string that draft_html tag returns. The response indicates if the document passed to it is a draft.
    • page_match returns the APage whose href uniquely ends with the string passed to it.
    • root returns the path to the root of the collection that the document passed to it is a member of. This method is not functionally related to Jekyll draft documents.

The difference between the tag called draft_html and the filter of the same name is that the tag only works on the document that it is embedded in, while the filter works on any document passed to it. Both the tag and the filter call the same API methods defined in the Jekyll::Draft module.

Front Matter

To mark a blog as a draft, put it in the _drafts directory.

To mark any other article as a draft, add the following to its front matter:

Jekyll page front matter fragment
---
published: false
---

Demo

The demo/ subdirectory has working examples of this Jekyll plugin's functionality in a demonstration website. It can be used to debug the plugin, or it can run freely. Please examine the HTML files in the demo to see how the plugin works.

To run the demo freely from the command line, type:

Shell
$ demo/_bin/debug -r

View the generated website at localhost:4444.

When the demonstration is running, any time you modify the .html files, the demo website will regenerate. Each time you make a change, the website instantly regenerates. This helps the learning experience.

Please play with the contents of the .html files, so you can learn how to write Jekyll pages that include this functionality.

Installation

For Use In A Jekyll Website

  1. Add the CSS found in demo/assets/css/jekyll_draft.css to your Jekyll layout(s).
  2. Add the following to Gemfile:
    Gemfile
    group :jekyll_plugins do
      gem 'jekyll_all_collections', '>= 0.3.8'
      gem 'jekyll_draft', '>=2.1.0'
    end
  3. From your Jekyll site's top-level directory, type:
    Shell
    $ bundle
  4. Restart Jekyll.

For Use In a Gem

  1. Add the following to your gem’s `.gemspec`:
    my_project.gemspec
    spec.add_dependency 'jekyll_all_collections', '>= 0.3.8'
    spec.add_dependency 'jekyll_draft', '>=2.1.0'
  2. Type:
    Shell
    $ bundle

Configuration

_config.yml

The default settings run more quickly than if verify_unique_match is enabled, which guarantees that no broken internal links exist. If a Jekyll web page has hundreds or thousands of pages, site generation can take many minutes if verify_unique_match is enabled.

The following settings are the default values for _config.yml. They apply to all tags defined by this plugin (if_draft, unless_draft, if_page_draft, and unless_page_draft):

_config.yml fragment
if_draft:
  error_if_no_match: true
  verify_unique_match: false

Environment Variables

Two environment variables override the above values in _config.yml

  1. if_draft_error_if_no_match overrides if_draft.error_if_no_match
  2. if_draft_verify_unique_match overrides if_draft.verify_unique_match

Usage in a Web Page

This section documents the usage of the inline and block tags described [above](#top).

The if_draft and if_page_draft block tags act as if-then or if-then-else programming constructs. Draft documents only exist in development mode. In production mode, there is no way to programmatically detect if a draft document exists in development mode.

Thus, if_draft and if_page_draft actually mean “if in development mode and the document is a draft”.

Furthermore, unless_draft and unless_page_draft actually mean “if in production mode or the document is not a draft”. These block tags act as Ruby unless-then or unless-then-else programming constructs.

The draft_html inline tag generates HTML that indicates if a page is a draft or not.

if_draft and unless_draft Block Tags

These tags consider the status of the document that the tag is embedded in.

The if_draft block tag acts as an if-then or an if-then-else programming construct.

The following generates <p>This is a draft document!</p> if the document it is embedded in is a draft, and the Jekyll website generation was performed in development mode:

HTML or markdown
{% if_draft %}
  <p>This is a draft document!</p>
{% endif_draft %}

The following enhances the previous example by generating <p>This is a draft document!</p> if the document this code is embedded in is not a draft. The message for the else clause is generated for documents that are not drafts, regardless of whether Jekyll is in production or development mode.

HTML or markdown
{% if_draft %}
  <p>This is a draft document!</p>
{% else_if_not_draft %}
  <p>This is not a draft document!</p>
{% endif_draft %}

The unless_draft block tag switches the then and else clauses of the if_draft block tag. It acts as a Ruby unless-then or unless-then-else programming construct.

HTML or markdown
{% unless_draft %}
  <p>This is not a draft document!</p>
{% endunless_draft %}

This is how you can specify an else clause for unless_draft:

HTML or markdown
{% unless_draft %}
  <p>This is not a draft document!</p>
{% else_if_draft %}
  <p>This is a draft document!</p>
{% endunless_draft %}

You can use the keywords else_if_draft and else_if_not_draft interchangeably. They are actually made by registering the same code twice with different subclass names. Use the keyword that makes the most sense to you.

if_page_draft and unless_page_draft Block Tags

These tags consider the status of the document whose URL uniquely includes the specified string.

An error is raised in development mode if no page exists whose URL contains the matching string.

The following generates <p><code>/directory/blah_blah.html</code> is a draft document!</p> if the APage with an href that ends with the string blah.html in is a draft, and the Jekyll website generation was performed in development mode:

Jekyll page
{% if_page_draft blah.html %}
  <p><code>{{matched_page.url}}</code> is a draft document.</p>
{% endif_page_draft %}

Notice the reference to a Jekyll variable called matched_page in the above example. It is set to the APage whose uniquely matched the string following the if_page_draft tag. This variable is only defined for development mode within the then clause of the if_page_draft tag, and for the else clause of the unless_page_draft block tag.

You can specify any Page attribute with matched_page, for example {{matched_page.title}}.

The following generates <p><code>/directory/blah_blah.html</code> is not a draft document!</p> if the /directory/blah_blah.html in is a draft document, and the Jekyll website generation was performed in development mode:

Jekyll page
{% unless_page_draft blah.html %}
  <p><code>{{matched_page.url}}</code> is not a draft document!</p>
{% endunless_draft %}

The following shows how to specify else clauses for if_page_draft and unless_page_draft. Note that the if_draft else clause activates in production mode regardless of whether a page matches or not, so you should not reference matched_page in an else clause without testing jekyll.environment. Instead, you can reference the match string with {{path_portion}}:

Jekyll page
{% if_page_draft blah.html %}
  <p><code>{{matched_page.url}}</code> is a draft document!</p>
{% else_if_page_draft %}
  {% if jekyll.environment == 'development' %}
    <p><code>{{matched_page.url}}</code> is not a draft document!</p>
  {% else %}
    <p>Production mode cannot detect if <code>{{path_portion}}</code> matches a draft document or not.</p>
  {% endif %}
{% endif_page_draft %}

Similarly, the unless_draft then clause activates in production mode regardless of whether a page matches or not, so you should not reference matched_page in a then clause without testing jekyll.environment.

Jekyll page
{% unless_page_draft blah.html %}
  {% if jekyll.environment == 'development' %}
    <p>The APage whose <code>href</> contains <code>{{path_portion}}</code> is not a draft document!</p>
  {% else %}
    <p>Production mode cannot detect if <code>{{path_portion}}</code> matches a draft document or not.</p>
  {% endif %}
{% else_unless_page_draft %}
  <p><code>{{matched_page.url}}</code> is a draft document!</p>
{% endunless_page_draft %}

As with the if_page_draft and unless_page_draft examples, the above example references scoped Jekyll variables called matched_page and path_portion. These variables are only defined within the body of if_page_draft and unless_page_draft blocks.

Else Clauses

You can use the keywords else_if_draft, else_if_page_draft, else_unless_draft, and else_unless_page_draft interchangeably. They are actually made by registering the same code multiple times with different subclass names. Use the keyword that makes the most sense to you.

draft_html Inline Tag

The draft_html inline tag can return the status of the page it is embedded in, or any other page that exists. By definition, draft pages do not exist in production mode.

Here is an example of embedding the draft_html inline tag into an HTML document:

HTML or markdown
<p>Is this a draft document? Look here to see: {% draft_html %}</p>

By default, if the document is a draft, and the Jekyll website generation was performed in development mode, draft_html emits <i class='jekyll_draft>Draft</i> if the document is a draft, and the Jekyll website generation was performed in development mode, otherwise it does not emit anything.

You can change this behavior several ways:

  • Add the draft_output parameter to specify the HTML that should be emitted if the document is a draft, and the Jekyll website generation was performed in development mode:
    HTML or markdown
    {% draft_html
      draft_output="<p>Is a draft</p>"
    %}
    {% draft_html
      draft_output="<p>Is a draft</p>"
      published_output="<p>Not a draft</p>"
    %}
  • Add the published_output parameter to specify the HTML that should be emitted if the document is not a draft. The default message will continue to be output for draft documents when the published_output parameter is used.
    HTML or markdown
    {% draft_html published_output="<p>Not a draft</p>" %}
  • Add the draft_class parameter to specify the CSS class that should be added to the emitted HTML if the document is a draft, and the Jekyll website generation was performed in development mode:
    HTML or markdown
    {% draft_html draft_class="my_draft_class" %}
    {% draft_html
      draft_class="my_draft_class"
      published_output="<p>Not a draft</p>"
    %}
  • Add the draft_style parameter to specify the CSS class that should be added to the emitted HTML if the document is a draft, and the Jekyll website generation was performed in development mode:
    HTML or markdown
    {% draft_html draft_style="font-size: 24pt;" %}
     {% draft_html
      draft_class="my_draft_class"
      draft_style="font-size: 24pt;"
    %}
     {% draft_html
      draft_class="my_draft_class"
      draft_style="font-size: 24pt;"
      published_output="<p>Not a draft</p>"
    %}

Testing Another Page

The path_portion option enables the draft_html tag to report on the draft status of other pages when Jekyll is in development mode. By default, an empty string is always returned when in production mode.

HTML or markdown
<p>
  Is <code>/directory/blah_blah.html</code> a draft document?
  Look here to see: {% draft_html path_portion='blah.html' %}
</p>

It might be desirable to enclose the above in a test for Jekyll mode as follows:

HTML or markdown
{% if jekyll.environment == "development" %}
  <p>
    Is <code>/directory/blah_blah.html</code> a draft document?
    Look here to see: {% draft_html path_portion='blah.html' %}
  </p>
{% endif %}

Additional Options

In addition to the path_portion option, the following can be also be specified:

  • Add the published_output parameter to specify the HTML that should be emitted if the document is not a draft, and the Jekyll website generation was performed in development mode. The default message will continue to be output for draft documents when the published_output parameter is used.
    HTML or markdown
    {% draft_html published_output="<p>Not a draft</p>" %}
  • Add the draft_output parameter to specify the HTML that should be emitted if the document is a draft, and the Jekyll website generation was performed in development mode:
    HTML or markdown
    {% draft_html
      draft_output="

    Is a draft

    " %} {% draft_html draft_output="

    Is a draft

    " published_output="

    Not a draft

    " %}
  • Add the draft_class parameter to specify the CSS class that should be added to the emitted HTML if the document is a draft and the Jekyll website generation was performed in development mode:
    HTML or markdown
    {% draft_html draft_class="my_draft_class" %}
    {% draft_html
      draft_class="my_draft_class"
      published_output="

    Not a draft

    " %}
  • Add the draft_style parameter to specify the CSS class that should be added to the emitted HTML if the document is a draft, and the Jekyll website generation was performed in development mode:
    HTML or markdown
    {% draft_html draft_style="font-size: 24pt;" %}
    {% draft_html
      draft_class="my_draft_class"
      draft_style="font-size: 24pt;"
    %}
     {% draft_html
      draft_class="my_draft_class"
      draft_style="font-size: 24pt;"
      published_output="

    Not a draft

    " %}

Liquid Filters

draft_html

By default, the draft_html Liquid filter generates HTML if a page is invisible when published in production mode. If the page is not a draft then the empty string is returned.

The optional parameters for the draft_html inline tag are not available for use with the draft_html filter.

The generated HTML for draft pages is:
" <i class='jekyll_draft'>Draft</i>"

Invoke the filter like this:

HTML or markdown
{{ page | draft_html }} => " <i class='jekyll_draft'>Draft</i>"

Here is a code snippet that shows the draft_html filter in use:

HTML or markdown
{% assign docs = site.myCollection | sort: "order" %}
<ol id="titles">
{% for doc in docs %}
  <li>
    <a href="{{doc.url}}" class="title">{{doc.title}}{{ doc | draft_html }}
  </li>
{% endfor %}
</ol>

page_match_draft_html

This filter detects if the page whose URL uniquely contains a string is invisible when published in production mode, and returns the same strings that draft_html returns.

HTML or markdown
{{ "unpublished.html" | page_match_draft_html }}

is_draft

This filter detects if a page is invisible when published in production mode, and either returns true or false.

HTML or markdown
{{ page | is_draft }} <!-- true for draft documents in development mode --> 

page_match_is_draft

This filter detects if the page whose URL uniquely contains a string is invisible when published in production mode, and returns true or false.

HTML or markdown
{{ "unpublished.html" | page_match_is_draft }}

Invoking From Another Jekyll Plugin

Ruby
require 'jekyll_draft'
puts 'Found a draft' if Jekyll::Draft.is_draft post
puts "draft_html is #{Jekyll::Draft.draft_html post}"

Usage in a Plugin

The methods in lib/draft_html.rb can be invoked by qualifying them with Jekyll::Draft. Here is a complete example of a Jekyll Support plugin that displays an indication of whether the page is a draft or not:

my_plugin.rb
require 'jekyll_plugin_support'

module MyPluginTag
  MyPluginError = JekyllSupport.define_error
  PLUGIN_NAME = 'my_plugin'.freeze
  VERSION = '0.1.0'.freeze

  class MyPluginTag < JekyllSupport::JekyllTag
    def render_impl
      <<~HEREDOC
        Draft or not? #{Jekyll::Draft.draft_html(@page)}
      HEREDOC
    end
  end
end

Liquid::Template.register_tag(MyPluginTag::PLUGIN_NAME, MyPluginTag::MyPluginTag)
PluginMetaLogger.instance.info { "Loaded #{MyPluginTag::PLUGIN_NAME} v#{MyPluginTag::VERSION} plugin." }
* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.