Skip to content

Native Templates Guide

Vyasa uses a Native Template System (RFC-011) that allows you to define how commands are rendered directly within your .vy files.

Unlike external template engines (like Jinja2 or Handlebars), Vyasa templates are written in Vyasa syntax. This ensures your templates are part of the compilation graph and can use standard language features.

Vyasa organizes templates by Target Format using a convention-based directory structure:

workspace/
├── templates/
│ ├── html/ <-- Target: "html"
│ │ ├── default.html <-- Default Shell
│ │ ├── main.vy <-- Template definitions
│ │ └── reference.html <-- Optional shell (usage: --template reference)
│ └── json/ <-- Target: "json"

When you place a .vy file inside a target folder (e.g., templates/html/), you can use Implicit Syntax.

File: templates/html/main.vy

// Defines a template for `verse` in the "html" target
`verse {
`div { class="verse-box" } [
`span { class="ref" } [ $.argument ]
`p { class="content" } [ $.text ]
]
}

You can also explicitly define the target, which is useful for single-file examples or overrides outside the templates/ folder.

`template `verse `for "html" {
...
}

Templates are composed of commands that map to the target format. For HTML, the SimpleHtmlBackend supports:

  • Block: div, span, p, blockquote
  • Text: em, strong, b, i
  • List: ul, ol, li
  • Link: a
  • Heading: h1, h2, h3, title
  • Breaks: br

Inside the template body, you can use special variables starting with $. to inject content from the source node.

VariableDescription
$.textThe full content (children) of the source node.
$.argumentThe argument of the command (e.g., the “1.1” in `verse 1.1).
$.attributeAny attribute value from the source node (e.g., $.class, $.id).

Given source:

`quote { author="Gandhi" } [ Be the change. ]

Template:

`template `quote `for "html" {
`blockquote [
$.text
`footer [ — $.author ]
]
}

Output (HTML equivalent):

<blockquote>
Be the change.
<footer>— Gandhi</footer>
</blockquote>

Sometimes you want a command to be purely semantic (for the graph) but render transparently (flattened) in the output.

Use $.text alone to unwrap the node.

// Remove the `uvacha` wrapper but keep its content
`uvacha {
$.text
}

Since templates use Vyasa syntax, you add CSS classes using standard attribute syntax:

`div { class="my-class" style="color: red;" } [ ... ]

Templates can access global entity definitions if they are passed as attributes.

If your context defines:

`set entities {
krishna = { label="Lord Krishna" }
}

And you have a command representing Krishna:

`krishna {
`span { class="speaker" } [ Lord Krishna ]
}

(Note: Direct entity lookup in templates entities[name] is planned but currently templates rely on static definitions or attribute passing.)

A common pattern in Vyasa is Streams—where a block contains a sequence of different data types (like Devanagari, IAST, and English lines).

Approach: Composition, not Extraction.

Instead of trying to “address” or “split” the segments inside the parent template, you should:

  1. Ensure the content is structured into commands (e.g., using stream-def to implicitly tag lines as d, i, e).
  2. Define simple templates for those child commands.
  3. Use $.text in the parent to render them all in order.

Example:

Source (stream-def applied):

`verse 1.1 [
`d [ धर्मक्षेत्रे कुरुक्षेत्रे... ]
`i [ dharma-kshetre... ]
]

Parent Template:

`verse {
`div { class="verse-card" } [
$.text // Renders `d` then `i`
]
}

Child Templates:

`d {
`div { class="devanagari" } [ $.text ]
}
`i {
`div { class="translation" } [ $.text ]
}

8. Collections & Extraction (Table of Contents)

Section titled “8. Collections & Extraction (Table of Contents)”

For “Whole Work” templates (like a Table of Contents), you use the collection command.

Instead of just rendering content, you often want to extract specific data points from each item (chapter/document) to display in a summary table.

`collection {
extract="devanagari, translation"
infer_speaker="krishna, arjuna"
} [
`each work.items [
`div { class="row" } [
`span { class="speaker" } [ $.speaker ]
`span { class="text" } [ $.devanagari ]
]
]
]
  • extract="cmd1, cmd2": Tells the compiler to look into each document, find the first occurrence of cmd1, and make its text available as $.cmd1 in the loop context.
  • infer_{key}="cmd1, cmd2": Tells the compiler: “If you see cmd1 in the document, set $.{key} to ‘cmd1’”.
    • Example: infer_speaker="arjuna" checks for an arjuna command. If found, $.speaker becomes “arjuna”.
  • Entity Expansion: If $.speaker is set to “arjuna”, the compiler automatically looks up entities.arjuna.* in the global context and flattens it.
    • entities.arjuna.label becomes $.speaker_label.
    • entities.arjuna.color becomes $.speaker_color.