Getting started

Installation and your first file

Voodoo.js is a finished file. You download it, drop it next to your HTML, point a tag at it, and that is it. There is no installer, no build step, and no mandatory config file.

1. Pick and download the file

Three files are published. They are all the same project, cut at different points. Pick one.

FileWhat is insideGzipDownload
voodoo.core.min.js Reactivity, expressions, components, chainable DOM, state directives, rendering, events and requests by attribute 42 KB download
voodoo.min.js The above, plus forms with validation, masks, the full UI kit, drag and drop, dialogs, toasts and sound 79 KB download
voodoo.full.min.js Everything, plus charts, physics animation, the router, languages, the reactivity inspector and 29 ready-made components 124 KB download

When in doubt, start with voodoo.min.js. It covers most pages. Switch to the full one the day you need a chart, spring animation, the router, languages or the ready-made components. Switching means rewriting the src of the tag: the HTML you already wrote stays exactly the same.

From the terminal:

curl -L -o voodoo.min.js https://github.com/kwy404/Voodoo.js/raw/main/packages/voodoojs/dist/voodoo.min.js

Or download the whole repository from the Code button, or from the v0.5.0 release, and take the files out of packages/voodoojs/dist/. Each file has a .map next to it; copy both if you want to debug the original code in the inspector.

2. Point a tag at the file

<script src="voodoo.full.min.js" defer></script>

Done. There is no V.init() to call: the library starts on its own as soon as the page is ready, publishes the global V object (and Voodoo, the same object), applies the saved theme, applies the saved palette and walks document.body binding the v-* attributes.

defer makes the browser finish reading the HTML before running the file. Without it, the library starts earlier and the MutationObserver ends up doing the work again — it works, but it costs more.

3. Write HTML

<div v-data="{ n: 0 }">
  <button @click="n++">Clicked { n } times</button>
</div>

Open the file in the browser. Click. The number changes. That is the whole cycle.

Your first file, line by line

Create an index.html next to the voodoo.min.js you downloaded:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>My first Voodoo</title>
  <script src="voodoo.full.min.js" defer></script>
</head>
<body>

<div v-data="{ name: '', count: 0 }">

  <input v-model="name" placeholder="Your name">
  <h1>Hello, { name || 'stranger' }!</h1>

  <button @click="count--">-</button>
  <strong>{ count }</strong>
  <button @click="count++">+</button>

  <p v-show="count > 5">You have clicked a lot.</p>

</div>

</body>
</html>

This is the result, running right here:

<div v-data="{ name: '', count: 0 }">
  <input v-model="name" placeholder="Your name">
  <h3>Hello, { name || 'stranger' }!</h3>
  <div class="linha">
    <button @click="count--">-</button>
    <strong>{ count }</strong>
    <button @click="count++">+</button>
  </div>
  <p v-show="count > 5">You have clicked a lot.</p>
</div>
  1. <script src="voodoo.full.min.js" defer> loads the library.
  2. v-data="{ name: '', count: 0 }" creates a scope: two variables that hold for that <div> and for everything inside it.
  3. v-model="name" binds the field to the variable in both directions. Type, and the variable changes; change the variable, and the field changes.
  4. { name || 'stranger' } is interpolation: it writes the value into the text and updates itself.
  5. @click="count++" runs the expression on click and notifies whoever depends on count.
  6. v-show="count > 5" shows the paragraph only while the condition is true.

What to notice: you did not write a single line of loose JavaScript. There is no querySelector, no addEventListener, no render function. Open the inspector after it loads: the v-model, @click and v-show attributes are gone. What is left is clean HTML.

Avoiding the flash of raw content

Between the browser painting the HTML and the library starting there is an instant where the raw text shows on screen — braces of the interpolation included. v-cloak solves it: the library removes the attribute when it finishes mounting that stretch.

<style>
  [v-cloak] { display: none !important; }
</style>

<div v-cloak v-data="{ loading: true }">
  <p>{ loading ? 'Loading...' : 'Ready' }</p>
</div>

The [v-cloak] rule already ships in the CSS the library injects, but declaring it in your own file guarantees it exists before the first frame — which is exactly the moment that matters.

Configuring from the tag itself

The fastest way to configure is with attributes on the script tag, without writing any JavaScript at all:

<script
  src="voodoo.full.min.js"
  data-base-url="https://api.example.com"
  data-locale="en-US"
  defer
></script>
AttributeEffect
data-manualDoes not start on its own. You call V.start() when you want to
data-defer-initThe same as data-manual
data-prefixChanges the attribute prefix, for example data-v-
data-base-urlBase URL for V.http requests and for the HTTP directives
data-localeLocale used by the date, number and currency formatters
data-devtoolsTurns on detailed warnings in the console
data-no-stylesDoes not inject the CSS for the UI components
data-no-observerTurns off the MutationObserver that initializes HTML created later
data-keep-attributesKeeps the v-* attributes in the HTML after they are processed

The full list of options, with the default value of each, is in the configuration reference.

When you need data-manual

Automatic start-up happens early, on purpose. If your page registers components, its own directives or global data in another file, that registration has to happen before the first sweep — otherwise the library walks past a tag it does not know yet.

<script src="voodoo.full.min.js" data-manual defer></script>
<script src="my-components.js" defer></script>
<script defer>
  V.config.locale = 'en-US';
  V.config.currency = 'USD';
  V.http.setBaseURL('https://api.example.com');
  V.start();
</script>
A simple rule

Whoever calls V.start() is always the last script in the list. Any file that registers a component, a directive or data goes in before it.

A prefix that passes strict HTML

If your HTML validator complains about v-text, change the prefix:

<script src="voodoo.full.min.js" data-prefix="data-v-" defer></script>

<div data-v-data="{ n: 0 }">
  <button data-v-click="n++">Add</button>
  <b data-v-text="n"></b>
</div>

The library always accepts data-v-name, even when the configured prefix is another one. The :attribute and @event shorthands keep working in both modes.

Checking that it worked

<script>
  document.addEventListener('voodoo:ready', (e) => {
    console.log('Voodoo', V.version, 'started on', e.detail.root);
  });
</script>

If the console prints the version, the library loaded and mounted. If it prints nothing, the src path is wrong — the network tab of the inspector confirms that in a second.