Vue in Citry
Some interactions should happen in the browser without a call to the server: opening a menu, counting clicks, switching a tab. For these, a Citry component is also a Vue component. Python renders its HTML on the server, and Vue makes it interactive in the browser.
Add a browser counter
Write Vue attributes, such as @click and v-text, in the component's template. In the component's js, call $component({...}) with Vue's component options. $component tells Citry how the component behaves in the browser. Here data() returns the value the browser keeps:
from citry import Component
class Counter(Component):
template = """
<button type="button" @click="count += 1">
Add one
</button>
<output v-text="count"></output>
"""
js = """
$component({
data() {
return { count: 0 };
},
});
"""
Each copy of the component on the page keeps its own count.
How Citry runs Vue
You write Vue directives, the Vue attributes such as v-show and @click, in the template, and Vue options in $component({...}). Citry does the rest:
- Citry compiles each template on the server into the code Vue uses to render it in the browser. There is no build step.
- Citry loads Vue in the browser, starts it on the HTML the server sent, and applies each new server render to the live page. See Server-rendered HTML.
- Citry creates the Vue app itself, so there is no Vue app for you to create. Do not mount another Vue app over Citry's output. To add a plugin or a global directive, see Plugins and the Vue app.
When Vue turns on
Citry adds Vue to the page as soon as one component on it does any of these:
- uses Vue directives in its template;
- has its own JavaScript;
- returns data from
js_data(); - uses Vue props or events;
- keeps server-event
State.
Complete pages and HTML fragments work the same way.
Tell Python from Vue
Citry runs {{ ... }} and c-* values as Python on the server, once per render. Vue runs directive values as JavaScript in the browser. Both can sit in one template:
<section c-class="{'has-results': results}">
<button type="button" @click="open = !open">
Toggle details
</button>
<p v-show="open">{{ details }}</p>
</section>
Python sets the class and inserts details once. After the page loads, Vue changes open. Define open in data(), setup(), or js_data().
A Vue expression cannot see Python names. A common first attempt inside a c-for loop reads the loop name from Vue, and no item gets a title:
<!-- Wrong: Vue looks for `item` in browser data. -->
<li c-for="item in items" :title="item">{{ item }}</li>
<!-- Right: Python sets `title` for each item. -->
<li c-for="item in items" c-title="item">{{ item }}</li>
citry check and the editor report the first form. They also report it when the browser data has its own item: then every item shows that one value, and nothing fails. When Vue needs the value too, for example in a v-show that changes later, send it with js_data() or loop with Vue's v-for.
Choose <c-if> or v-if
Use Vue's v-if and v-for for plain HTML inside one component, and <c-if> and <c-for> when the branch or loop creates Citry components. Choose v-for or <c-for> explains why and shows an example of each.
Where each value goes
Each Python value goes to one place:
| Python | Reaches | Read it as |
|---|---|---|
Kwargs | The server only. Nothing is sent to the browser. | kwargs.name in Python methods |
template_data() | The template, on the server | {{ name }} or c-* attributes |
js_data() | The component in the browser, as JSON | name in Vue expressions, this.name in JS |
css_data() | CSS custom properties | var(--name) in the component's CSS |
State | The browser, through server events | $state.name, this.$state.name |
Vue props are separate from all of these: the parent component passes them in the browser with :name.
A Vue expression cannot read a template_data() or Kwargs value. When the browser needs one, return it from js_data() too.
Which Vue version
Citry ships Vue 3.5.42. Each Citry release bundles one fixed Vue version, and you cannot swap in another. To check the version a page runs, enter Citry.vue.version in the browser console.
Where to go next
- Vue in templates: the
v-*directives,@listeners, and:bindings you write in a template. - Component options:
data(), methods,js_data(),onServerRender, and the rest of$component({...}). - Props and events: pass values to a child component and listen to the events it emits.
- Content and slots: which component's Vue data the content you pass into a slot reads.
- Server-rendered HTML: what the page shows before Vue starts, and what it keeps when the server renders again.
- Plugins and the Vue app: add Vue plugins, global directives, and an error handler.
- Security and CSP: use a Content Security Policy, or send no JavaScript at all.
- Limits and errors: the Vue features Citry rejects, and how to find the cause of a failure.
- Browser APIs: the reference for
$component,onServerRender, andCitry.vue.