Theme
Version
GitHub PyPI Discord
On this page

Plugins and the Vue app

Sometimes you want something in every component on the page: a global directive such as v-autofocus, a shared component, or an error reporter. In a plain Vue project you would set these up on the app returned by createApp(). Citry creates the Vue app for you, so there is no createApp() call to configure. Use a Vue plugin instead.

Add a Vue plugin

A Vue plugin is an object with an install(app) method. Register it with Citry.vue.use(plugin, ...options). Citry installs it on every Vue app it creates on the page, before the app mounts.

Here a plugin adds a v-autofocus directive:

// static/vue-plugins.js
Citry.vue.use({
  install(app) {
    app.directive("autofocus", {
      mounted(el) {
        el.focus();
      },
    });
  },
});

Any template can then use it:

<input v-autofocus />

In install(app) you can:

  • register directives with app.directive() and components with app.component();
  • provide values with app.provide(). A component reads a provided value with inject in $component({...}); see provide and inject;
  • set app.config.errorHandler; see Handle Vue errors;
  • add app.config.globalProperties.

Load the plugin file

Load the file in the page's <head> with defer:

<script defer src="/static/vue-plugins.js"></script>

defer makes the file run after Citry's runtime loads and before Citry creates the first Vue app, which is the window in which Citry.vue.use() works.

To add the file to every page from Python, write an extension that adds it to ctx.early_scripts; see Add scripts and styles.

Handle Vue errors

To send errors to your own reporting tool, set app.config.errorHandler in the plugin's install:

Citry.vue.use({
  install(app) {
    app.config.errorHandler = (error, instance, info) => {
      // With a handler set, Citry leaves logging to you.
      console.error(error);
      // sendToTracker stands for your reporting tool.
      sendToTracker(error, info);
    };
  },
});

When a handler is set, Citry passes each error to it instead of the browser console, so log it there yourself. Errors from Citry's own browser code, such as a failed @c-poll refresh, reach the handler too, with an info text that starts with Citry, as in "Citry @c-poll".

With or without a handler, after an error Citry stops sending server updates to that Vue app. Reload the page to get them again.

Plugin limits

A plugin runs inside a Vue app that Citry has already set up, so a few things work differently from a plain Vue project.

Call use() early

Call Citry.vue.use() before Citry creates its first Vue app. A later call throws an Error, because that app would run without the plugin. The reference lists the other errors, such as passing a value that is not a plugin.

Components need render()

A component registered with app.component() can be used in any template by its name, such as <my-badge>. It needs a render() function written with Citry.vue.h. A template string does not compile in the browser:

Citry.vue.use({
  install(app) {
    app.component("my-badge", {
      props: ["label"],
      render() {
        const h = Citry.vue.h;
        return h("span", { class: "badge" }, this.label);
      },
    });
  },
});

Keep $loading, $error

Citry adds $loading and $error to globalProperties. A plugin that sets either name replaces Citry's version, and $loading() and $error() stop working in every template. Give your properties other names.

One install per Vue app

A page can run several Vue apps, for example when HTML fragments add components to it. The plugin's install runs once for each app, and each app has its own app.provide() values. For one value that every app shares, create it outside install and provide that same object:

// One store for the whole page, created once.
const store = Citry.vue.reactive({ cartCount: 0 });

Citry.vue.use({
  install(app) {
    app.provide("store", store);
  },
});

No mixins

A plugin cannot add a mixin to components, because $component({...}) rejects mixins and extends; see Unsupported options.

Ignored config options

app.config.compilerOptions has no effect, because Citry compiles templates on the server. app.config.warnHandler is never called, because the Vue build Citry loads leaves out Vue's warnings.

A failing install

If the plugin's install throws, that Vue app does not start, and the error shows as a page error.

See also