Reference

Utilities

Functions the library itself uses internally and publishes on V. They all also exist as a direct import, from voodoojs/utils, for anyone who builds the project with a bundler.

Identifiers and time

FunctionReturns
V.uuid()UUID v4. Uses crypto.randomUUID when available, with two fallbacks
V.uid(prefix?)Short identifier, useful for element ids
V.sleep(ms)Promise that resolves after the given time
V.parseDuration(value, fallback?)Milliseconds from 300, '300ms', '1.5s', '2m', '1h'
V.uid('field-');           // 'field-k3f9a2'
V.parseDuration('1.5s');   // 1500
V.parseDuration(null, 99); // 99

parseDuration accepts null because the most common source is a getAttribute that may not exist.

Higher-order functions

debounce and throttle

const search = V.debounce(loadProducts, 300);
search('mu');
search('mug');      // only this one runs
search.cancel();    // drops the pending call
search.flush();     // runs now, without waiting

const saveNow = V.debounce(save, 1000, true);  // leading edge

const track = V.throttle(measureScroll, 100);
window.addEventListener('scroll', track, { passive: true });

once and memoize

const start = V.once(() => createConnection());
start();
start();  // returns the same connection

const compute = V.memoize((a, b) => expensiveOperation(a, b));
compute.cache.clear();
const byId = V.memoize(load, (id) => String(id));  // key of your own
Careful with the name

V.once on the global object is the event bus (V.once('event', handler)). The utility that runs a function a single time is in the direct import: import { once } from 'voodoojs/utils'.

Objects and arrays

FunctionWhat it does
V.clone(value)Deep copy. Uses structuredClone when it exists; understands Date, Map and Set
V.merge(target, ...sources)Deep merge. Arrays are replaced, not concatenated
V.groupBy(list, keyOrFn)Groups into an object
V.unique(list, keyOrFn?)Removes duplicates
V.chunk(list, size)Breaks into pieces
V.sortBy(list, keyOrFn, direction?)Sorts without changing the original
V.get(object, path, fallback?)Reads a nested path, safely
V.set(object, path, value)Writes, creating the objects in between
V.random(min, max), V.sample(list)Integer in the range, and any item
V.merge({ a: { b: 1 } }, { a: { c: 2 } });  // { a: { b: 1, c: 2 } }
V.unique(points, (p) => p.x + ',' + p.y);
V.sortBy(products, 'price', 'desc');
V.get(data, 'list.0.name', 'no name');
V.set(form, 'items.0.qty', 2);

Two details that avoid surprises: merge modifies the first object, pass {} in front when you want to preserve it. And sortBy compares text with localeCompare and numeric ordering, which puts item 2 before item 10; null values go to the end.

Text

V.slugify('Crème Brûlée');         // 'creme-brulee'
V.slugify('My Post', '_');         // 'my_post'
V.truncate('A rather long text', 10);       // 'A rathe...'
V.truncate('A rather long text', 10, '…');  // 'A rather…'
V.capitalize('voodoo');            // 'Voodoo'
V.titleCase('JAVASCRIPT feels');   // 'Javascript Feels'
V.escapeHtml('<b>hi</b>');         // '&lt;b&gt;hi&lt;/b&gt;'
V.stripTags('<b>hi</b>');          // 'hi'
stripTags is not a sanitizer

escapeHtml is the safe way to assemble HTML by hand. stripTags removes tags in a simple way and does not work as a defence against hostile content. The security page explains the difference.

Formatters

They all use the default locale and currency, both adjustable:

V.setFormatDefaults('pt-BR', 'BRL');

The bootstrap already sets this up from V.config.locale and V.config.currency.

CallResult
V.formatCurrency(1234.5)R$ 1.234,50
V.formatCurrency(99, { currency: 'USD', locale: 'en-US' })$99.00
V.formatNumber(1234.5678)1.234,568
V.formatNumber(0.75, { style: 'percent' })75%
V.formatDate(new Date())29/08/2026
V.formatDate(date, 'long')29 de agosto de 2026
V.formatDate(date, 'DD/MM/YYYY HH:mm:ss')text mask
V.relativeTime(Date.now() - 300000)há 5 minutos
V.formatFileSize(1536)1.5 KB
V.formatPercent(0.256, 1)25,6%

formatDate accepts a Date, a timestamp or ISO text, and returns empty text for an invalid date. The named formats are long, full, time and datetime; the text mask understands YYYY, YY, MM, DD, HH, mm and ss; and an object is passed straight to Intl.DateTimeFormat.

Environment

MemberWhat it is
V.isBrowserIs there a DOM?
V.deviceGetters for touch, size, motion and theme
V.screenReactive object with width, height and breakpoints
V.networkReactive object with the state of the connection
V.clipboardcopy and read

Inside HTML, the last four appear as $screen, $network, $device and $clipboard. Because screen and network are reactive, an expression that reads them updates on its own when the window changes size or the connection drops.

<div v-data="{}">
  <p>Width: { $screen.width }px</p>
  <p>Is mobile: { $screen.mobile }</p>
  <p>Online: { $network.online }</p>
  <p>Touch: { $device.touch }</p>
  <button @click="$clipboard.copy('PROMO10')">Copy coupon</button>
</div>

Importing directly

import { debounce, formatCurrency, slugify, once } from 'voodoojs/utils';

This entry point does not load the DOM runtime, so it works in Node as well, it is the way to reuse the formatters on the server without dragging in the whole library.