Shortcodes are small but powerful macros that can generate content or add metadata to your documents.
Statiq supports several templating engines like Markdown and Razor, but sometimes you need to generate some content regardless of the templating language you're using. Shortcodes are small text macros that can do big things and work across any templating engine. Statiq Framework and Statiq Web come with several helpful shortcodes and best of all it's very easy to add your own.
Using Shortcodes
Shortcodes are a special type of XML processing instruction delimited with <?#
and ?>
(or <?!
and ?>
depending on processing phase). This syntax allows the shortcodes to "fall-through" most templating engines like Markdown since those languages ignore XML processing instructions. Every shortcode has a name, often has parameters, and can optionally contain content depending on how and what the shortcode renders.
A shortcode must always be closed with a /
(similar to XML elements). If the shortcode contains no content, it can be self-closing by using a trailing slash: <?# ShortcodeName /?>
. If the shortcode does contain content, it must be closed with a matching shortcode: <?# ShortcodeName ?>content<?#/ ShortcodeName ?>
.
Nesting Shortcodes
Shortcodes can be nested, but keep in mind the current processing phase. If a shortcode result contains other shortcodes, those will be recursively processed but only if the nested shortcode is also part of the current phase.
Shortcode Names
By convention shortcode names use CamelCase due to the fact that most of them come from .NET classes, which are also named with CamelCase. That said, the shortcode name is case-insensitive so if you prefer to use some other casing convention, you can.
Parameters
Some shortcodes accept parameters which can be either positional, named, or a mixture of both depending on the shortcode. Shortcode parameters appear in the opening shortcode element and are delimited from the shortcode name and each other by whitespace. If the value of a parameter requires whitespace of it's own it can be enclosed in quotes. Parameter names do not need to appear in any specific order and are specified as key=value
(or key="value"
if the value contains whitespace).
A single unnamed parameter value:
<?# ShortcodeName parameter-value /?>
A single unnamed quoted parameter value:
<?# ShortcodeName "parameter value" /?>
Multiple unnammed positional parameter values:
<?# ShortcodeName "parameter 1" parameter2 "parameter value 3" /?>
A single named parameter and value:
<?# ShortcodeName Foo=Bar /?>
A single named parameter and quoted value:
<?# ShortcodeName Foo="Bar Baz" /?>
A mixture of unnamed positional parameter values and named parameters:
<?# ShortcodeName "unnamed value" Foo=Bar /?>
Note that unnamed positional parameters almost always must appear before named parameters.
Content
In addition to parameters, some shortcodes accept or expect content. Shortcode content goes between the opening and closing shortcode tag and is sent verbatim to the shortcode:
<?# ShortcodeName "parameter 1" ?>
Here is
Some Shortcode
Content
<?#/ ShortcodeName ?>
Because shortcode content is just text in your file, it will be changed by any templating engine(s) before being processed. For example, if the shortcode about was part of a Markdown file, it would end up looking like this before being processed by the shortcode (notice the surrounding <p>
that the Markdown engine added):
<?# ShortcodeName "parameter 1" ?>
<p>Here is
Some Shortcode
Content</p>
<?#/ ShortcodeName ?>
Many times that behavior is desirable because we want to use the templating language for the shortcode content. Other times you may want the shortcode content to stay unprocessed by templating engines. In that case, you can surround the content inside a special XML processing instruction with the syntax <?* ... ?>
. This works because like the shortcodes themselves, most templating engines will ignore XML processing instructions. The shortcode processor will remove the special wrapping XML processing instruction tags inside the content before processing.
For example, this:
<?# ShortcodeName "parameter 1" ?>
<?*
Here is
Some Shortcode
Content
?>
<?#/ ShortcodeName ?>
Will not get an added <p>
from the Markdown engine and instead will get processed by the shortcode as:
<?# ShortcodeName "parameter 1" ?>
Here is
Some Shortcode
Content
<?#/ ShortcodeName ?>
Writing Shortcodes
Shortcodes get passed the current document, execution context, any parameters, and any content and returns a collection of ShortcodeResult
objects that contains new content to add to the containing document in the place of the shortcode. The ShortcodeResult
type is implicitly convertible from both a string
and a Stream
, so a shortcode implementation can just return whatever content object is appropriate.
Bootstrapper
You can define shortcodes through the bootstrapper which contains many AddShortcode()
overloads for specifying shortcodes by delegates and other means.
For example, if you add the following:
.AddShortcode("Foo", (string x) => $"ABC{x}XYZ");
And then use it in a document like this:
<?# Foo ?>123<?#/ Foo ?>
The output will be:
ABC123XYZ
As A Class
To write a shortcode as a class, implement IShortcode
. The shortcode name will generally be the same as the implementing class name. Alternatively, several shortcode base classes are provided as a convenience:
Shortcode
is a base class for single-result asynchronous shortcodes.SyncShortcode
is a base class for single-result synchronous shortcodes.MultiShortcode
is a base class for multiple-result asynchronous shortcodes.SyncMultiShortcode
is a base class for multiple-result synchronous shortcodes.
Registering Your Shortcodes
Any custom shortcodes will need to be registered with the engine.
This can be done through the bootstrapper using its AddShortcode<TShortcode>()
extensions. Note that the bootstrapper automatically registers all shortcode types in all referenced assemblies by default so you'll rarely need to manually register shortcodes when using the bootstrapper.
You can also register shortcodes directly with an engine using its Shortcodes
property.
Processing Shortcodes
Use the ProcessShortcodes
module in your own pipelines to find shortcodes within a document and render them. It's generally an accepted pattern to use the ProcessShortcodes
module after all other templates have been evaluated, but you can certainly use it earlier in your pipelines if you want to.
W D Statiq Web Shortcodes
Statiq Web comes with several web-specific shortcodes and support multiple pre-defined processing phases for shortcodes.
Processing Phases
Statiq Web process shortcodes at three points with different syntax:
- Pre-rendering:
<?! ShortcodeName /?>
Performed before any other templating engines like Markdown or Razor. That means any output from a shortcode in this phase will be processed by those engines. For example, if you want to include a Markdown document in another Markdown document, you'll need to evaluate the Include
shortcode during the pre-rendering phase (otherwise the Markdown processor would have already been run and your included Markdown document would never get processed).
- Intermediate:
<?^ ShortcodeName /?>
Performed after the primary rendering is done (for example, Markdown) but before any post-processing is performed (I.e. Razor). This is valuable when you want a shortcode to flow through the first processing engine like Markdown but it generates code like Razor instructions for the post-processing module(s).
- Post-rendering:
<?# ShortcodeName /?>
The post-rendering phase happens after all template engines like Markdown and Razor have been evaluated. This is appropriate for most shortcodes and is indicated with the default #
delimiter as described above.
Of course, you can also continue to add your own pipelines and ProcessShortcodes
modules in Statiq Web with whatever delimiter you want for further customization.
Embed And oEmbed Support
oEmbed is a format for fetching the embedded representation of third-party content The Embed
shortcode provides general-purpose oEmbed support by calling an oEmbed endpoint and rendering the embedded content:
<?# Embed https://codepen.io/api/oembed https://codepen.io/gingerdude/pen/JXwgdK /?>
The Embed
shortcode accepts the following arguments:
Endpoint
: The oEmbed endpoint.Url
: The embeded URL to fetch an embed for.Format
: An optional format to use ("xml" or "json").
Many sites have implemented oEmbed support and some site-specific shortcodes for some of them are also available:
Giphy
Embeds a gif from Giphy:
<?# Giphy excited-birthday-yeah-yoJC2GnSClbPOkV0eA /?>
The only argument is the ID of the gif which can be obtained from it's URL: the ID for the URL https://giphy.com/gifs/excited-birthday-yeah-yoJC2GnSClbPOkV0eA is excited-birthday-yeah-yoJC2GnSClbPOkV0eA
.
Gist
Embeds a gist from GitHub:
<?# Gist 10a2f6e0186fa34b8a7b4bd7d436785d /?>
This shortcode accepts the following arguments:
Id
: The ID of the gist.Username
: Contains the username the gist belongs to (optional).File
: Specifies the file within the gist to embed (optional).
Embeds a Tweet from Twitter:
<?# Twitter 123456789 /?>
This shortcode accepts the following arguments:
Id
: the ID of the Tweet. This can be found at the end of the URL when you copy a link to a Tweet.HideMedia
: When set totrue
, links in a Tweet are not expanded to photo, video, or link previews.HideThread
: When set totrue
, a collapsed version of the previous Tweet in a conversation thread will not be displayed when the requested Tweet is in reply to another Tweet.Theme
: Optionally "light" or "dark". When set to "dark", the Tweet is displayed with light text over a dark background.OmitScript
: When set totrue
, the<script>
element that contains the Twitter embed JavaScript code will not be rendered (for example, if you're including it at the top of the page).
YouTube
Embeds a video from YouTube:
<?# YouTube u5ayTqlLWQQ /?>
The only argument is the ID of the video to embed.
Other Shortcodes
Link
Renders a link from the given path, using default settings or specifying overrides as appropriate:
<?# Link "/foo/bar" /?>
This shortcode accepts the following arguments:
Path
: The path to get a link for.IncludeHost
: If set totrue
the host configured in the output settings will be included in the link, otherwise the host will be omitted and only the root path will be included (default).Host
: The host to use for the link.Root
: The root of the link. The value of this argument is prepended to the path.Scheme
: The scheme to use for the link (this will override theUseHttps
argument).UseHttps
: If set totrue
, HTTPS will be used as the scheme for the link.HideIndexPages
: If set totrue
, "index.html" and "index.html" file names will be hidden.HideExtensions
: If set totrue
, extensions will be hidden.Lowercase
: If set totrue
, links will be rendered in all lowercase.
Figure
Generates HTML5 <figure>
elements:
<?# Figure Src="/assets/statiq.png" ?>
Statiq Logo
<?#/ Figure ?>
Will result in the following HTML:
<figure>
<img src="/assets/statiq.png" />
<figcaption>Statiq Logo</figcaption>
</figure>
This shortcode accepts the following arguments:
Src
: URL of the image to be displayed.Link
: If the image needs to be hyperlinked, URL of the destination.Target
: Optionaltarget
attribute for the URL ifLink
parameter is set.Rel
: Optionalrel
attribute for the URL ifLink
parameter is set.Alt
: Alternate text for the image if the image cannot be displayed.Class
: Theclass
attribute to apply to thefigure
element.Height
: Theheight
attribute of the image.Width
: Thewidth
attribute of the image.
Table
Renders an HTML table. The content of the shortcode contains the table with each row on a new line and each column separated by space. Enclose columns in quotes if they contain a space. Note that since the content of a shortcode may get processed by template engines like Markdown and the content of this shortcode should not be, you probably want to wrap the shortcode content in the special XML processing instruction that will get trimmed like <?* ... ?>
so it "passes through" any template engines (see example below).
<?# Table Class=table HeaderRows=1 ?>
<?*
Vehicle "Number Of Wheels"
Bike 2
Car 4
Truck "A Whole Lot"
?>
<?#/ Table ?>
Will result in the following HTML:
<table class="table">
<thead>
<tr>
<th>Vehicle</th>
<th>Number Of Wheels</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bike</td>
<td>2</td>
</tr>
<tr>
<td>Car</td>
<td>4</td>
</tr>
<tr>
<td>Truck</td>
<td>A Whole Lot</td>
</tr>
</tbody>
</table>
This shortcode accepts the following arguments:
Class
: Theclass
attribute to apply to thetable
element.HeaderRows
: The number of header rows in the table.FooterRows
: The number of footer rows in the table.HeaderCols
: The number of header columns to the left of the table.HeaderClass
: Theclass
attribute to apply to the<thead>
element.BodyClass
: Theclass
attribute to apply to the<tbody>
element.FooterClass
: Theclass
attribute to apply to the<tfoot>
element.