Page Variables

Overview

The main purpose of page variables is to communicate informations to the HTML layout from the page content. However, once you get the hang of it, you might decide to use them for quite a bit more than that.

As a first example, let's say that you would like the layout of your blog pages to include an author blurb at the bottom. This could be some fixed HTML structure with different information for each page (e.g.: different author name, bio, path for mugshot, ...).

In Franklin you can indicate this in the layout with something like:

<div class="author-card">
  <div class="author-name">{{author_name}}</div>
  <div class="author-blurb">{{author_blurb}}</div>
  <div class="author-mug">
    <img src="{{author_mug_src}}" alt="{{author_name}}"/>
  </div>
</div>

The {{author_name}} syntax means "insert the content of the author_name page variable here". Page variables can be defined in the Markdown of a page inside a block fenced with '+++':

+++
author_name  = "Emmy Noether"
author_blurb = """
  German mathematician who made many important
  contributions to abstract algebra.
  """
author_mug = "/assets/emmy.png"
+++

This might already give you a good idea but let's now dive into the specifics.

Page variables basics

Assignment

Page variables can be defined in any *.md file in a dedicated block fenced with '+++'. We'll call such a block a def-block from now on. We will distinguish:

  • global assignments for any assignment in a def block in the config.md file,
  • local assignments for any assignment in a def block in another *.md file.

We will get back to local and global contexts a bit further.

What is placed inside a def-block must be valid Julia code, and will be evaluated as such, all assignments will be extracted. There can be multiple (separate) def-blocks on a page though, usually, there will only be one, usually at the top of the page.

Here's an example where we define two page variables a and b. The content of a page variable can then be accessed from within the Markdown itself or in the HTML layout:

markdown
+++
a = 5
b = "hello"
+++

Rest of the markdown, we can also expose variables here:
* `a`: {{a}}
* `b`: {{b}}
resultRest of the markdown, we can also expose variables here:
  • a: 5
  • b: hello

Since the content of the def-block is evaluated as Julia code, you can load packages and use code to define variables:

markdown
+++
using Dates
todays_date = Dates.today()
+++

Build-date: {{todays_date}}.
resultBuild-date: 2022-06-12.

Insertions and HTML functions

Page variables can be used with the following general syntax (in markdown files, HTML files, and layout HTML files):

{{name_of_function name_of_var_1 ...}}

The case {{name_of_var}} is a shortcut for {{fill name_of_var}}. Note that if there is a clash between the name of a var and the name of a function, the function will be called with priority.

The functions called in such a way are called HTML-functions or hfuns for short, and can have zero or more arguments. There are several core functions like fill (see also the relevant section), and you can define your own using the utils file.

When inserting a page variable with {{var_name}} or {{fill var_name}}, the Julia function repr will be called on the value of var_name, and that is what will effectively be included. For basic Julia types (Int, String, Bool, ...), this will typically look like what you would expect. For custom types that you would have defined, or that are defined in a package, you would have to consider what repr(obj) returns.

markdown
+++
aa = true
bb = [1,2,3]
cc = Dict(:a => 5, :b => 7)
+++

* aa: {{aa}}
* bb: {{bb}}
* cc: {{cc}}
result
  • aa: true
  • bb: [1, 2, 3]
  • cc: Dict(:a => 5, :b => 7)

Observe that the output is placed "raw" (not in a HTML code block).

⚠️ Note Inside a code block, { and } are escaped which means that you cannot directly have insertions in those. For instance:
markdown
```
{{aa}}
```
result
{{aa}}

While here we mostly showed you examples where the insertion was done in the Markdown (so that you could directly see the result), insertions are typically very useful in the _layout. For instance you may want to fill <meta> tags in your layout with information that you define in your config.md.

For instance say that in your config.md you have

+++
site_author = "The Author"
site_title = "The site title"
site_descr  = "Description of the website"
+++

In the layout you might have

...
<head>
  ...
  <meta name="author" content="{{site_author}}">
  <meta name="description" content="{{site_descr}}">
  <meta name="twitter:title" content="{{site_title}}">
  ...
</head>
...

See also this article on setting meta tags (a useful thing to do if you want your website to show up nicely on social media etc.).

Local and global contexts

In a Franklin session there is a global (site-wide) context and a set of local (page-related) contexts which are attached to it. Page variables can be assigned at both levels. You have already seen how to do so at a page-level by just adding a +++...+++ block in the corresponding Markdown, you can do the same at global-level by adding such a block in your config.md file.

Variables defined at global-level are accessible from anywhere whereas variables defined at local-level are only directly accessible from the page on which they are defined (see also the next point). When a variable is defined at both levels, the local one takes precedence.

Accessing variables defined on another page

You can fill the content of a page variable defined on another page with

{{fill var_name relative_path}}

where the relative_path indicates the relative path to the page which defines var_name.

For instance both the current page and /syntax/basics.md define a variable header:

markdown
* by default we fill from the local page: **{{header}}** (or **{{fill header}}**)
* but we can query a specific page: **{{fill header syntax/basics}}**
result
  • by default we fill from the local page: Page Variables (or "Page Variables")
  • but we can query a specific page: "Markdown Basics"

The path is relative to the website folder. You can add a / at the start and a .md at the end but it's not required.

Multiple def-blocks

Though you would typically have only one def-block with all your assignments, in the case where you find it convenient to have multiple ones on a page, it is important to understand that the page variable assignments are read in one pass, and that their eventual insertion is done in a later pass.

This means that if you define a variable twice on the same page, the first assignment will be ignored. This is best seen with a small example:

markdown
+++
var1 = 0
+++
* {{var1}}
+++
var1 = 1
+++
* {{var1}}
result
  • 1
  • 1

The def-blocks are all executed in a first pass (so that, in the example above, var1 == 1), and the insertion (resolution of the {{...}}) is done after that, which is why the result is 1 both times.

Default variables

Franklin defines a number of page variables with default values that you can use and overwrite. You don't have to use or set any of those unless you find one useful.

Local variables

Variable name Default value Purpose / comment
title nothing title of the page
date Dates.Date(1) publication date of the page
lang "julia" default language of executed code blocks
tags String[] tags for the page (see tags)
slug "" slug for the page (see below)
ignore_cache Bool if true re-evaluate all the code on the page on first pass
mintoclevel 1 minimum heading level to add to the table of contents
maxtoclevel 6 maximum heading level to add to the table of contents
showall true show the output of every auto-executed code blocks
fn_title "Notes" heading of the footnotes section

The slug variable allows you to specify an explicit secondary output location for the page. For instance if you're currently working on a page a/b/c.md, the default output path will be such that the relevant page can be addressed at /a/b/c/. However if you specify slug = "c/d" then that page will also be available at /c/d/. You can also specify a path with a .html extension in which case exactly that path will be used:

  • slug = "c/d" β†’ the page will also be available at /c/d/
  • slug = "c/d.html" β†’ the page will also be available at /c/d.html

For more on the topic you might want to read about default paths in Franklin. Check also the keep_path global variable in the next section.

Global variables

Just as with default local variables, you might find it useful to access or set some of those though many might be irrelevant for you.

Variable name Default value Purpose / comment
author The Author
base_url_previx "" the site's base URL prefix (see also how to deploy)
content_tag "div" the HTML tag which will wrap the content (can be empty)
content_class "franklin-content" the class of the content wrapper
content_id "" the id of the content wrapper
autosavefigs true whether to automatically save figures
autoshowfigs true whether to automatically show figures
layout_head "_layout/head.html" the path to the layout head file (see also page structure)
layout_foot "_layout/foot.html" the path to the layout foot file
layout_page_foot "_layout/page_foot.html" the path to the page foot file
layout_head_lx "_layout/latex/head.tex" the path to the LaTeX preamble
parse_script_blocks true whether to parse <script> blocks
date_format "U d, yyyy" base date format used
date_days String[] specify custom day names (e.g. ["Lundi", ...])
date_shortdays String[] specify custom short day names (e.g. ["Lun", ...])
date_months String[] specify custom month names (e.g. ["Janvier", ...])
date_shortmonths String[] specify custom short month names (e.g. ["Jan", ...])
ignore_base ["LICENSE.md", "README.md", ...] base list of strings or regexes of files and directories to ignore
ignore [] complements ignore_base
keep_path String[] relative paths of files that should have their build path be identical to their source path
robots_disallow String[] relative paths of files that should disallow robots
generate_robots true whether to generate robots.txt
generate_sitemap true whether to generate a sitemap
heading_class "" class to add to headings (converted from ## ...)
heading_link true whether to make headings into links
heading_link_class "" class of the heading links if any
heading_post "" HTML that should be placed after every heading if any (can contain {{...}})
toc_class "toc" class of the table of contents
anchor_class "anchor" class of the page anchors
anchor_math_class "anchor-math" class of anchors in math environment
anchor_bib_class "anchor-bib" class of anchors in references
date_format "U dd, yyyy" date format on the site
date_days String[]
date_shortdays String[]
date_months String[]
date_shortmonths String[]
generate_rss false whether to generate an RSS feed
rss_website_title ""
rss_website_url ""
rss_feed_url ""
rss_website_descr ""
rss_file "feed" file name for the RSS feed
rss_full_content false whether to insert the full page content on RSS items
tags_prefix "tags" tags will be at /tags/...
tabs_tospaces 2 conversion tabs to spaces in list creation


For legacy purposes, a number of these variables have aliases:

Alias Variable name
prepath base_url_prefix
prefix base_url_prefix
base_path base_url_prefix
website_url rss_website_url
website_title rss_website_title
website_description rss_website_descr
website_descr rss_website_descr

HTML functions and environments

By now you should already have an idea

  • XXX functions can be used in Markdown no problem
  • environments should only be used in HTML (their scope is not resolved in Markdown which so something like {{for x in iter}} **{{x}}** {{end}} will try to resolve {{x}} first, fail) (or within a raw HTML block)

Default functions

(XXX) Environments: conditionals

markdown
+++
flag = true
+++

{{if flag}}
Hello
{{else}}
Not Hello
{{end}}
result Hello
πŸš€ Tip While here the environment is demonstrated directly in Markdown, environments are best used in layout files (_layout/...) or raw HTML blocks and should, ideally, not be mixed with Markdown as the order in which elements are resolved by Franklin may not always be what you think and that can sometimes lead to unexpected results.This is even more true for loop environments introduced further below.

Naturally, you can have multiple branches with {{elseif variable}}. Here's another example

markdown
+++
flag_a = false
flag_b = true
+++

{{if flag_a}}
ABC
{{elseif flag_b}}
DEF
{{end}}
result DEF

There are some standard conditionals that can be particularly useful in layout.

conditional effect
{{ispage path}} or {{ispage path1 path2 ...}} checks whether the present page corresponds to a path or a list of paths, this can be particularly useful if you want to toggle different part of layout for different pages
{{isdef var}} ...

etc + check

(XXX) Environments: loops

markdown
+++
some_list = ["bob", "alice", "emma", "joe"]
+++

{{for x in some_list}}
{{x}} \\
{{end}}
result b
a
e
j
markdown
+++
iter = ["ab", "cd", "ef"]
+++

{{for e in iter}}
_value_ ? **{{e}}**\\
{{end}}
result value ? a
value ? c
value ? e
πŸš€ Tip The example above shows mixing of Markdown inside the scope of the loop environment, here things are simple and work as expected but as indicated in the previous tip, you should generally keep environments for HTML blocks or layout files.

E-strings

E-strings allow you to run simple Julia code to define the parameters of a {{...}} block. This can be useful to

  1. insert some value derived from some page variables easily,
  2. have a conditional or a loop environment depend on a derivative of some page variables.

The first case is best illustrated with a simple example:

markdown
+++
foo = 123
+++

* {{e"1+1"}}
* {{e"$foo"}} you can refer to page variables using `$name_of_variable`
* {{e"$foo^2"}}
* {{e"round(sqrt($foo), digits=1)"}}
result
  • 2
  • 123 you can refer to page variables using $name_of_variable
  • 15129
  • 11.1

More generally the syntax is {{ e"..." }} where the ... is valid Julia code where page variables are prefixed with a $. The alternative syntax {{> ...}} is also supported:

markdown
+++
bbb = 321
+++

{{> $bbb // 3}}
result107//1

The code in the e-string is evaluated inside the utils module and so could leverage any package it imports or any function it defines. For instance we added a function

bar(x) = "hello from foo <$x>"

to utils.jl and can call it from here in an e-string as:

markdown
{{e"bar($foo)"}}
resulthello from foo <123>

As noted earlier, these e-strings can also be useful for conditional and loop environments:

  • {{if e"..."}}
  • {{for ... in e"..."}}

For the first, let's consider the case where you'd like to have some part of your layout depend on whether either one of two page variables is true. For this you can use an e-string and write {{if e"$flag1 || $flag2"}}:

markdown
+++
flag1 = false
flag2 = true
+++
{{if e"$flag1 || $flag2"}}
Hello
{{else}}
Not Hello
{{end}}
result Hello

More generally you can use an e-string for any kind of condition written in Julia as long as it evaluates to a boolean value (true or false).

For loop environments, you can likewise use an e-string that would give you some derived iterator that you'd wish to go over:

markdown
+++
iter1 = [1, 2, 3]
iter2 = ["abc", "def", "ghi"]
+++
~~~
<ul>
{{for (x, y) in e"zip($iter1, $iter2)"}}
<li><strong>{{x}}</strong>: <code>{{y}}</code></li>
{{end}}
</ul>
~~~
result
  • 1: abc
  • 2: def
  • 3: ghi

More customisation

If the default functions and environments, possibly coupled with e-strings are not enough for your use case or are starting to feel a bit clunky, it's time to move on to writing custom hfuns.

This approach allows you to write {{your_function p1 p2 ...}} where your_function maps to a Julia function that you define and that produces a string based on whatever logic that would be appropriate for your use case.

Website built with Franklin.jl and the Julia programming language.