Published 2020-10-03.
Last modified 2025-09-26.
Time to read: 5 minutes.
jekyll_plugins collection.
Jekyll's built-in include tag does not support including files
with paths that lie outside of the _includes folder.
Originally called include_absolute,
this plugin now does much more than just include files using absolute paths.
The plugin is now named jekyll_,
and the tag is called flexible_.
This plugin supports 4 types of includes:
-
Filenames relative to the top-level directory of the Jekyll web site.
It is not necessary to preface these paths with
./). -
Absolute filenames (first character is
/). This feature can be modified or denied where security is a concern by specifying an array of Ruby glob expressions in theFLEXIBLE_INCLUDE_PATHSenvironment variable. -
Filenames relative to the user home directory (first character is
~). This feature can also be modified or denied where security is a concern by specifying an array of Ruby glob expressions in theFLEXIBLE_INCLUDE_PATHSenvironment variable. -
Executable filenames on the
PATH(first character is!). This feature can be disabled by defining theDISABLE_FLEXIBLE_INCLUDEenvironment variable before launching Jekyll.
In addition, filenames that require environment expansion because they contain
a $ character are expanded according to the environment variables
defined when the jekyll build process was launched.
Installation
Gem
Add the following highlighted line to your Jekyll project's Gemfile,
within the jekyll_plugins group:
group :jekyll_plugins do gem 'jekyll_flexible_include' end
And then execute:
$ bundle
Add the following to your Jekyll _config.yml:
plugins: - flexible_include
CSS and Assets
Copy assets and CSS from the demo/ directory of the
jekyll_flexible_include_plugin GitHub project.
-
Copy
demo/assets/images/clippy.svgto a directory of the same name in your Jekyll project. -
Copy
demo/assets/css/jekyll_flexible_include.cssto your Jekyll project assets directory. -
Copy
demo/assets/css/jekyll_plugin_support.cssto your Jekyll project assets directory. -
Copy
demo/assets/css/shared_include_pre.cssto your Jekyll project assets directory. -
Incorporate the CSS stylesheets into the appropriate layout in your Jekyll project:
_layouts/default.html{% assign nowMillis = site.time | date: '%s' %} <link rel="stylesheet" href="{{ '/assets/css/jekyll_flexible_include.css?v=' | append: nowMillis }}" type="text/css"> <link rel="stylesheet" href="{{ '/assets/css/jekyll_plugin_support.css?v=' | append: nowMillis }}" type="text/css"> <link rel="stylesheet" href="{{ '/assets/css/shared_include_pre.css?v=' | append: nowMillis }}" type="text/css">
JavaScript
Copy
demo/assets/js/clipboard.min.jsfrom thejekyll_flexible_include_pluginGitHub project to your Jekyll project’s JavaScript directory.Modify the Jekyll layout or selected pages to load the JavaScript. You can load it from your project, as shown below, or from a CDN.
One way of loading JavaScript<script defer src="/assets/js/clipboard.min.js"></script>
Syntax
The following are equivalent:
- {% flexible_include path [ OPTIONS ] %}
- {% flexible_include 'path' [ OPTIONS ] %}
- {% flexible_include "path" [ OPTIONS ] %}
- {% flexible_include file='path' [ OPTIONS ] %}
- {% flexible_include file="path" [ OPTIONS ] %}
By default, the included file will escape characters
<,{and}, unlessdo_not_escapeis specified. Note that the [square brackets] merely indicate optional parameters and are not intended to be written literally.The file must be specified as a relative or absolute path on the server, or a command to execute. A URL cannot be provided (you cannot write a file name that starts with
http:orhttps:). This capability is under consideration for a possible future release.Options
do_not_escapeincludes the content without HTML escaping it.precauses the included file to be wrapped inside a<pre></pre>tag, no label is generated.stripremoves leading and trailing whitespace.
The following options imply
pre:copyButtondraws an icon at the top right of the<pre></pre>area, which causes the included contents to be copied to the clipboard.downloaduses the name of the file as a label, and displays it above the<pre></pre>tag. Clicking the label causes the file to be downloaded. The file must be local to the Jekyll website.labelspecifies that an automatically generated label be placed above the contents. There is no need to specify this option ifdownloadorcopyButtonoptions are provided.label="blah blah"specifies a label for the contents; this value overrides the default label. The value can be enclosed in single or double quotes. If you want to display a text message other than the file name, use this option.numbernumbers the lines.
Usage Examples
This example shows a typical set of options. A label is automatically generated from the file name or process output that is included:
HTML or markdown{% flexible_include copyButton download file="~/.mem_settings.yaml" %}Here is what the above looks like when rendered by a web browser:
append_file_name: sample.html output_format: qt
... and without the
downloadoption, but still including thecopyButtonoption:.mem_settings.yamlappend_file_name: sample.html output_format: qt
... now with just the
preoption:.mem_settings.yamlappend_file_name: sample.html output_format: qt
... and finally this is the result of using
flexible_includewithout any options. The generated text is outlined in a<pre></pre>tag so it is noticable. The file has a trailing newline, which is apparent below, howeverflexible_includetrims any leading and trailing whitespace when theprekeyword option is specified or implied, or when thestripkeyword option is specified. You can disable this behavior by specifyingstrip=false.append_file_name: sample.html output_format: qt
Dark Mode
Normally my website uses light colors, however some content displays better on a dark background. The
darkoption causes the generated <pre> tag to have thedarkclass applied. You can define the CSS for thedarkanddarkLabelclasses. The CSS that defines those classes for this web site is here..mem_settings.yamlappend_file_name: sample.html output_format: qt
Home Directory
The included file path can use a tilde (
~) to denote the user$HOMEdirectory.{% flexible_include ~/.mem_settings.yaml %}append_file_name: sample.html output_format: qt
Environment variables can be used; they will be expanded according to the environment variables that were current in the process when the Jekyll generator launched.
{% flexible_include '/home/mslinn/.gitconfig' %}[alias] lol=log --graph --decorate --pretty=oneline --abbrev-commit lola=log --graph --decorate --pretty=oneline --abbrev-commit --all ls=ls-files st = status ci = commit br = branch co = checkout df = diff dc = diff --cached dif = diff --word-diff=color --ignore-space-at-eol lg = log -p ign = ls-files -o -i --exclude-standard pwd = !pwd [branch "master"] remote = origin merge = refs/heads/master [core] filemode = false autocrlf = input safecrlf = false excludesfile = C:\\Users\\Mike Slinn\\Documents\\gitignore_global.txt pager = less -F [color] status = auto branch = auto ui = auto [gui] trustmtime = true [push] default = matching autoSetupRemote = true [user] name = Mike Slinn email = mslinn@mslinn.com [rebase] autostash = true [diff "exif"] textconv = exiftool [diff] compactionHeuristic = true renames = 0 [hub] protocol = git [pull] rebase = false [init] defaultBranch = master [log] date = local [creategem] githubuser = mslinn [nugem] githubuser = mslinn gemserver = "" [safe] directory = /mnt/f/work/ibm [credential] helper = store [filter "lfs"] clean = git-lfs clean -- %f smudge = git-lfs smudge -- %f process = git-lfs filter-process required = trueEnvironment Variable Expansion
This example includes the output of running the bash command
which jekyll, according to the Ruby environment that was current when the Jekyll generator was launched:{% flexible_include '!which jekyll' %}/home/mslinn/.rbenv/versions/3.4.6/bin/jekyll
2025-09-25 Windows Special Consideration
If Jekyll runs on a Windows computer, and you want to use
#WindowsEnvironmentVariables#withflexible_include, please read these instructions fromjekyll_plugin_support.If a Windows-style env var is evaluated on a non-Windows machine, then a Bash environment variable of the same name is searched for and used, if found.
- If an exact case-sensitive match is found, it is used. A debug-level log message is emitted stating what happened.
- If a case-insensitive match is found, it is used, and a warning is issued.
- If more than one case-insensitive match is found, Jekyll is shut down.
Strip Leading and Trailing Whitespace
Sometimes a file or process contains leading or trailing whitespace.
{% flexible_include copyButton download .mem_settings.yaml %}Renders as:
append_file_name: sample.html output_format: qt
The
stripkeyword option removes leading and trailing whitespace.{% flexible_include copyButton download strip .mem_settings.yaml %}Renders as:
append_file_name: sample.html output_format: qt
Does This Make Your Brain Hurt?
Ready to have your mind twisted? This invocation:
Shell{% flexible_include '~/.mem_settings.yaml' download copyButton label='{% flexible_include download copyButton ~/.mem_settings.yaml %}' %}Renders like this:
append_file_name: sample.html output_format: qt
Hint:
{and}are HTML entities for{and}, respectively.Highlighting Text
A regular expression can be passed to the
highlightoption. This causes text that matches the regex pattern to be wrapped within a<span class="bg_yellow"></span>tag.The following highlights filenames in a directory listing that contain
django-adminthat might contain dots, underscores, dashes and forward slashes:HTML or markdown{% flexible_include highlight="[\w./\-_]*django-admin[\w.\-_]*" label="ls ~/venv/aw/bin/*" file="!ls ~/venv/aw/bin/*" %}Renders as:
ls ~/venv/aw/bin/*/home/mslinn/venv/aw/bin/activate /home/mslinn/venv/aw/bin/activate.csh /home/mslinn/venv/aw/bin/activate.fish /home/mslinn/venv/aw/bin/activate.ps1 /home/mslinn/venv/aw/bin/activate.xsh /home/mslinn/venv/aw/bin/activate_this.py /home/mslinn/venv/aw/bin/django-admin /home/mslinn/venv/aw/bin/django-admin.py /home/mslinn/venv/aw/bin/easy_install /home/mslinn/venv/aw/bin/easy_install-3.8 /home/mslinn/venv/aw/bin/easy_install3 /home/mslinn/venv/aw/bin/faker /home/mslinn/venv/aw/bin/pip /home/mslinn/venv/aw/bin/pip3 /home/mslinn/venv/aw/bin/pip3.10 /home/mslinn/venv/aw/bin/pip3.11 /home/mslinn/venv/aw/bin/pybabel /home/mslinn/venv/aw/bin/python /home/mslinn/venv/aw/bin/python3 /home/mslinn/venv/aw/bin/python3.8 /home/mslinn/venv/aw/bin/sqlformat /home/mslinn/venv/aw/bin/wheel /home/mslinn/venv/aw/bin/wheel-3.8 /home/mslinn/venv/aw/bin/wheel3 /home/mslinn/venv/aw/bin/__pycache__: django-admin.cpython-39.pyc
Numbering Lines
HTML or markdown{% flexible_include label="ls ~/venv/aw/*" file="!ls ~/venv/aw/*" number %}Renders as (notice that the numbers are unselectable):
ls ~/venv/aw/*1: /home/mslinn/venv/aw/pyvenv.cfg 2: 3: /home/mslinn/venv/aw/bin: 4: __pycache__ 5: activate 6: activate.csh 7: activate.fish 8: activate.ps1 9: activate.xsh 10: activate_this.py 11: django-admin 12: django-admin.py 13: easy_install 14: easy_install-3.8 15: easy_install3 16: faker 17: pip 18: pip3 19: pip3.10 20: pip3.11 21: pybabel 22: python 23: python3 24: python3.8 25: sqlformat 26: wheel 27: wheel-3.8 28: wheel3 29: 30: /home/mslinn/venv/aw/lib: 31: python3.10 32: python3.11 33: python3.8 34: python3.9
CSS
Below are the CSS declarations that I defined for the
flexible_includetag that produced the above output. This CSS is the same as used bypre..clear { clear: both; } ol li .codeLabel { padding-left: 1.75em; } .error { color: white; background-color: darkred; padding: 2px; } #main-content li > div.jekyll_pre, li > div.jekyll_pre { margin-top: 20px; } .jekyll_pre + div, .jekyll_pre + p, .jekyll_pre + ul, .jekyll_pre + ol, .jekyll_pre + dl, .jekyll_pre + span.emoji + div, .jekyll_pre + span.emoji + p, .jekyll_pre + span.emoji + ul, .jekyll_pre + span.emoji + ol, .jekyll_pre + span.emoji + dl { margin-top: 20px; } .jekyll_pre + .jekyll_pre { margin-top: 2em; } .pre_tag { margin-bottom: 1em; tab-size: 2; } .tree { line-height: 1; }Restricting Directory Access
By default,
flexible_includecan read from all directories according to the permissions of the user account that launched thejekyllprocess. For security-conscience environments, the accessible paths can be restricted.Defining an environment variable called
FLEXIBLE_INCLUDE_PATHSprior to launching Jekyll will restrict the paths thatflexible_includewill be able to read from. This environment variable consists of a colon-delimited set of regular expressions. For example, the following restricts access to only the files within:- The
~/my_dirdirectory tree of the account of the user that launched Jekyll. - The directory tree rooted at
/var/files. - The directory tree rooted at the expanded value of the
$workenvironment variable.
Shell$ export FLEXIBLE_INCLUDE_PATHS='~/my_dir/.*:/var/files/.*:$work/.*'If a reference to an unauthorized file is intercepted, a big red message will appear on the generated web page that says something like
Access to #{path} denied by FLEXIBLE_INCLUDE_PATHS value.
... and an error message will be logged on the console that looks something like:
ERROR FlexibleInclude: _posts/2020/2020-10-03-jekyll-plugins.html - Access to #{path} denied by FLEXIBLE_INCLUDE_PATHS value.
Restricting Arbitrary Processes
By default,
flexible_includecan execute any command. You can disable that by setting the environment variableDISABLE_FLEXIBLE_INCLUDEto any non-empty value.Shell$ export DISABLE_FLEXIBLE_INCLUDE=trueIf a potential command execution is intercepted, a big red message will appear on the generated web page that says:
Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.
... and an error message will be logged on the console that looks something like:
ERROR FlexibleInclude: #{path} - Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.