Embed a VSM widget in plain JavaScript
No Vue, no React, no build step, no dependencies. Two ways — pick one. ← back
click the button…
VSM is framework-agnostic. The editor happens to be built with Vue, but the thing you ship is pure DOM + a ~2 KB core. Vue/React bindings are optional conveniences, never required.
Option A — one self-contained file (simplest)
In the editor, click Export JS module → you get widget.js with the SVG, the state machine, and the runtime all inlined (≈16 KB, 0 deps). Then:
- Put
widget.jsnext to your page. - Add a container and the snippet below.
<!-- index.html --> <div id="btn"></div> <script type="module"> import mount from './widget.js'; const m = mount('#btn', { persist: { key: 'btn' } }); m.on('onPress', () => console.log('pressed!')); // events from the animation document.querySelector('#btn01-path') .addEventListener('click', () => m.fire(m.value === 'default' ? 'press' : 'release')); </script>
Option B — keep files separate
Export SVG + .vsm.json and the small runtime (src/). Use the mount() helper with URLs:
<div id="btn"></div> <script type="module"> import { mount } from './src/index.mjs'; const m = await mount('#btn', { svgUrl: './button.svg', specUrl: './button.vsm.json', persist: { key: 'btn' }, }); m.on('onPress', () => {/* your app reacts */}); </script>
That's the whole API:
m.fire('trigger') to drive it · m.on('event', fn) for events back · m.subscribe(s => s.value) for state · m.value = current state. Works with any framework, or none.Using a framework? There's a Vue example too — same core, one thin wrapper.