Skip to main content
Version: 2026-09-21 (current)

Assets

Voshi loads your tool's stylesheets and templates before it constructs your element, and hands them over as api.assets. Your tool never fetches its own files.

connectedCallback() {
// styles.css is already applied to the shadow root; index.html is a template
this.shadowRoot.replaceChildren(this.api.assets.template('index.html'))
this.shadowRoot.querySelector('img.map').src = this.api.assets.url('map.png')
}

Which files count as stylesheets and templates is decided by manifest.json: app.styles and app.templates, or every .css and .html in the folder when those keys are absent.

api.assets.stylesCSSStyleSheet[]

The stylesheets, in manifest order. If your shadow root is open (this.attachShadow({ mode: 'open' })), they are already adopted into it when connectedCallback runs — do nothing. With a closed root, adopt them yourself: root.adoptedStyleSheets = [...root.adoptedStyleSheets, ...this.api.assets.styles].

api.assets.template(name)DocumentFragment

A fresh copy of a template's content, ready to put in the shadow root. Every call is a new copy, so a template can be stamped out many times — one per card, one per row.

api.assets.text(name)string

A template's raw text, for a tool that does its own templating.

api.assets.url(name)string

The URL of any of your tool's own files — an image, a font, a data file — for anything the browser must fetch. This is the only URL to one of your files your tool should ever use.

A name that is not one of the tool's files throws, naming the file.

Why not fetch them yourself​

Every upload and every published version of your tool is served from a different address, named by the content of the files. A URL you wrote into your code, or built from import.meta.url and cached, is the address of one particular upload. api.assets.url() is always the address of the files that are running. And styles.css and index.html have already been fetched once, by Voshi; fetching them again is a second round trip the learner waits on.

Importing another JavaScript module of your own with a relative import './lib/cards.js' is fine, and the normal way to split a tool into files — the browser resolves it against the running copy.