Core concepts
Data and renderers
Lists, cards, timelines: anything repeated comes from a JSON file and a renderer. The page says which file it shows, the renderer says how.
A data source
[
{ "title": "Static sites in 2026", "date": "2026-03-12", "place": "Milan" },
{ "title": "Agents and the web", "date": "2026-05-02", "place": "Online" }
]Data sources live in src/app/data-sources (sub-folders are fine). Keep them as data: records, texts, dates. Which items go on which page, and how many, belongs in the HTML.
A renderer
import { RendererProps } from 'yassb-web';
interface Talk {
title: string;
date: string;
place: string;
}
export function talkList({ source, options }: RendererProps<Array<Talk>, { limit?: number }>): string {
return source
.slice(0, options?.limit ?? source.length)
.map(talk => `<li><time>${talk.date}</time> ${talk.title} <span>${talk.place}</span></li>`)
.join('');
}Register it in the configuration:
import { talkList } from '../builder/renderers';
export default {
customRenderers: { talkList }
};Placing it on a page
<ul class="talks">
<!-- create-from-data-source="talks.json" renderer="talkList" options="{"limit":3}" -->
</ul>options is JSON written as is inside the quotes and is passed to the renderer. Repeat the directive to show several files; each directive renders one file.
Renderer props
| Prop | Content |
|---|---|
source | the parsed JSON |
sourcePath | absolute path of the JSON file |
options | the parsed options attribute |
lang | the language being built (undefined without translations) |
currentFilePath | the file being processed |
config | the configuration, with absolute folders |
frontMatterStore | front matter of every page, by output path |
Renderers return a string and never write files. Escape the data you insert into HTML. Emit fixed UI text as <span data-t="KEY">English</span> so it gets translated. The JSON file is parsed again for every directive, so a renderer can sort or change its data freely.
Built-in renderers
basicElement
One element per item, with item.description as content (item.description[lang] with translations). Used when a directive has no renderer.
<ul>
<!-- create-from-data-source="items.json" renderer="basicElement" options="{"tag":"li","attributes":{"class":"item"}}" -->
</ul>Options: tag (default span) and attributes (true writes a bare attribute, false omits it, arrays are joined with spaces).
liDateDescription
<li><u>date</u>: description</li> per item, with dates formatted in the current language:
[
{ "date": { "start": "2019/03", "end": "-" }, "description": "Working at Acme" },
{ "date": { "start": "2015/09/12", "end": "2018/06/30" }, "description": "Studying" },
{ "date": { "raw": "Someday" }, "description": "Retiring" }
]Dates can be YYYY, YYYY/MM or YYYY/MM/DD; end: "-" becomes <span data-t="PRESENT">, translated by your language files.
Errors
| Code | When |
|---|---|
DATA_SOURCE_NOT_FOUND | the JSON file does not exist; nothing is rendered |
DATA_SOURCE_INVALID_JSON | the file is not valid JSON; the build fails |
RENDERER_MISSING | no renderer attribute; basicElement is used |
RENDERER_NOT_FOUND | the renderer is not registered; basicElement is used |
RENDERER_FAILED | the renderer threw; the build fails with the location of the directive |