Skip to content

Template Syntax

Collagraph uses a Vue-inspired template syntax. If you know Vue, you already know most of this. The key difference is that all expressions are Python instead of JavaScript.

For full Vue template syntax documentation, see: Vue Template Syntax

Prefer plain Python?

Everything on this page can also be expressed in pure Python, without a .cgx file. See Python Views.

File Format

A .cgx file has a template section and a <script> section:

<div>
  <!-- template here -->
</div>

<script>
import collagraph as cg

class MyComponent(cg.Component):
    pass
</script>

The template is everything outside the <script> tag. Unlike Vue, there's no <template> wrapper needed.

Multiple Root Nodes

Unlike Vue 2 (but like Vue 3), collagraph supports multiple root nodes in a template. There's no need to wrap everything in a single container element:

<label text="First" />
<label text="Second" />
<button text="Third" />

<script>
import collagraph as cg

class Multi(cg.Component):
    pass
</script>

Text Interpolation

Elements can contain text content, with double curly braces for dynamic expressions:

<text>Hello, {{ name }}!</text>
<text>{{ count }} items remaining</text>

Expressions inside {{ }} are evaluated as Python. Static and dynamic parts can be mixed freely, and multiline text is normalized (leading indentation after line breaks is stripped). To render literal braces, escape them with a backslash: \{{.

Renderer support

Text content requires the renderer to support text elements. The Pygfx renderer supports it inside <text> elements. The PySide6 renderer supports it inside widgets that display text, such as <label> and <button> (see PySide6 Renderer):

<label>Hello, {{ name }}!</label>
<button @clicked="bump">bump</button>

For other Qt widgets, set text via the text attribute instead:

<lineedit :text="f'Hello, {name}'" />

Attribute Binding

Static attributes:

<button text="Click me" />

Dynamic attributes (prefix with :):

<label :text="f'Count: {count}'" />
<button :enabled="items > 0" />

The expression after : is evaluated as Python. See Vue Attribute Bindings for the concept.

Event Handling

Prefix with @:

<button @clicked="handle_click" />
<lineedit @text_edited="handle_change" />

You can use inline expressions:

<button @clicked="lambda: set_count(count + 1)" />

See Vue Event Handling for the concept.

Conditional Rendering

<label v-if="show" text="Visible" />
<label v-else text="Hidden" />

With v-else-if:

<label v-if="status == 'loading'" text="Loading..." />
<label v-else-if="status == 'error'" text="Error!" />
<label v-else text="Done" />

See Vue Conditional Rendering.

List Rendering

<label v-for="item in items" :key="item" :text="item" />

With index:

<label v-for="(item, index) in items" :key="index" :text="f'{index}: {item}'" />

See Vue List Rendering.

Component Usage

Import and use components by their class name:

<TodoList :items="items" @clicked="handle_complete" />

<script>
from todo_list import TodoList
import collagraph as cg

class App(cg.Component):
    pass
</script>

Component names in templates use PascalCase.

Slots

Define where child content goes:

<!-- Card.cgx -->
<widget>
  <slot />
</widget>

Provide content to slots:

<Card>
  <label text="I go in the default slot" />
</Card>

Named slots:

<!-- Layout.cgx -->
<widget>
  <slot name="header" />
  <slot />
  <slot name="footer" />
</widget>

<!-- Usage -->
<Layout>
  <label slot="header" text="Header" />
  <label text="Body content" />
  <label slot="footer" text="Footer" />
</Layout>

Comments

HTML comments are supported:

<!-- This is a comment -->
<label text="visible" />