3 releases
0.1.2 | Jun 6, 2024 |
---|---|
0.1.1 | Jun 6, 2024 |
0.1.0 | Jun 6, 2024 |
#82 in Template engine
110KB
3K
SLoC
platelet
platelet
is an HTML-first templating language.
This repo contains a Rust library for rendering platelet
templates.
Why platelet?
Unlike moustache
, handlebars
, Jinja
, Liquid
and other templating languages, platelet
's syntax is part of HTML (similar to Vue.js).
This has a few upsides:
- Higher level but less powerful than direct string manipulation
- The language is natural to read and write when working with HTML, and control flow follows HTML structure
- You can use your own HTML formatters and tooling
- HTML sanitization is more natural and straightforward
Example
You can explore live examples in the platelet playground
Template
<ul pl-if="n > 0">
<li pl-for="i in [1, 2, 3]">{{ i }} × {{ n }} = {{ i * n }}</li>
</ul>
Context (input)
{ "n": 7 }
Output
<ul>
<li>1 × 7 = 7</li>
<li>2 × 7 = 14</li>
<li>3 × 7 = 21</li>
</ul>
More examples
Advanced example
Template templates/index.html
<!doctype html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<template pl-for="b in blogposts" pl-src="./blogpost.html" ^blogpost="b">
</template>
</body>
</html>
Template templates/blogpost.html
<article>
<img ^src="blogpost.img_url" />
<div>
<h2>
<a ^href="blogpost.link">{{blogpost.title}}</a>
</h2>
<template pl-html="blogpost.summary"></template>
<date>{{blogpost.date}}</date>
</div>
</article>
<style>
article {
display: flex;
}
</style>
Context (input)
{
"title": "Angus' Blog",
"blogposts": [
{
"img_url": "...",
"link": "...",
"summary": "...",
"title": "...",
"date": "01/11/2025"
},
{
"img_url": "...",
"link": "...",
"summary": "...",
"title": "...",
"date": "01/11/2020"
}
]
}
Reference
Syntax | Example | Details |
---|---|---|
pl- directives |
pl-if , pl-for ... |
→ |
^ attributes |
^class , ^name ... |
→ |
{{ ... }} nodes |
{{ user.email }} |
→ |
Expressions | 1 + users[i].score |
→ |
pl-
Directives
HTML Attributes starting with a pl-
are special. They are inspired by Vue's directives.
attribute |
---|
pl-if |
pl-else-if |
pl-else |
pl-for |
pl-html |
pl-src |
pl-slot |
pl-is |
Conditionals: pl-if
, pl-else-if
, pl-else
pl-if
will only render this element if the expression is truthy
pl-else-if
, used following a pl-if
, will only render this element if the expression is truthy
pl-else
, used following a pl-if
or pl-else-if
, will render this element otherwise
<button pl-if="stock >= 1">Add to cart</button>
<button pl-else-if="stock > 0">Add to cart (1 item left!)</button>
<button pl-else disabled>Out of stock</button>
If applied to a <template>
, the template will be and the children rendered.
pl-for
Render element multiple times.
Allows 4 types of expression:
<div pl-for="item in items">{{item.text}}</div>
<div pl-for="(item, index) in items">...</div>
<div pl-for="(value, key) in object">...</div>
<div pl-for="(value, name, index) in object">...</div>
If applied to a <template>
, the template will be removed and the children rendered.
pl-html
Set the innerHTML (without sanitization) to the given expression.
To set the outerHTML, apply this to a <template>
.
<p pl-html="markdown"></p>
{ "markdown": "<h1>Content from a CMS</h1>..." }
pl-src
Given a path as a string, renders the template at the path and replaces the element.
<slot pl-src="./sidebar.html" ^username="data.username">
<p>Some text...</p>
</slot>
The attributes set on the element (regular attributes or rendered ^
attributes) are used as the context for rendering the template.
pl-slot
On a <slot>
, pl-slot
(with an optional name) marks the element as a slot, to be replaced.
On a <template>
, pl-slot
marks the template content as "what fills that slot".
index.html
<slot pl-src="layout.html">
<template pl-slot="sidebar">
<ul> ...
</template>
<template pl-slot="content">
<table> ...
</template>
</slot>
layout.html
<body>
<nav>
<slot pl-slot="sidebar"></slot>
</nav>
<main>
<slot pl-slot="content"></slot>
</main>
</body>
Output
<body>
<nav>
<ul> ...
</nav>
<main>
<table> ...
</main>
</body>
pl-is
Replace the rendered element's tag with this element, given an expression that returns a string
<slot pl-is='i == 0 ? "h1" : "h2"'>{item}</slot>
^
Attributes
In an HTML attribute, prefixing the attribute with ^
allows you to set the value to a platelet
expression.
<a ^href='"/products/" + slug'></a>
If the expression is false
or null
, the attribute will not render.
<div
class="static"
^class="{ active: isActive, 'text-danger': hasError }"
^name="null"
></div>
This will render:
<div class="static active text-danger"></div>
Text Nodes
In an HTML text node, {{variable}}
inserts a (sanitized) string.
<h1>Welcome back {{user.name}}!</h1>
If the variable is not defined then an error is returned.
Data type | Rendered as |
---|---|
Number | A number |
String | A string |
Boolean | true or false |
Null | blank |
Array | error |
Object | error |
Expressions
All valid JSON values are valid platelet
expressions. On top of this, single-quoted strings 'like this'
are allowed for convenience when working with HTML.
Operators
On anything: ==
, !=
, &&
, ||
, !
, x ? y : z
On numbers: +
(addition)
On strings and arrays: +
(concatenation)
On objects: +
(shallow merge, right hand side overriding)
On numbers: -
, *
, /
, %
(mod)
On numbers: >
, <
, >=
, <=
On objects arrays and strings, indexing operator a[b]
On objects, dot access: {"name": "angus"}.name
On arrays, objects and strings: len(z)
Expressions can be bracketed (9 + 3) / 2 == 6
Truthiness
false
, []
, ""
, {}
, null
are falsy.
All other values are truthy.
Dependencies
~6–13MB
~165K SLoC