
VanillaCreamJS is a JavaScript library that literally gives “superpowers” to native JavaScript, enabling powerful and modern capabilities like reactivity without the need for build tools or compilation steps.
It’s a one-of-a-kind technology because it:
- Brings reactivity to plain JavaScript
- Works with outstanding performance thanks to the absence of diffing or recalculation algorithms
- Offers powerful tools with a minimal, intuitive API
- Helps improve your understanding of vanilla JavaScript
- Can integrate with any backend stack with zero friction
- Increases developer productivity in small to mid-size projects
- Combines the best of tools like HTMX, Vue, and Alpine
- Its name reflects its goal: refine vanilla JavaScript and elevate it, without ever straying too far from its roots.
VanillaCreamJS is created and maintained by Riccardo Degni, a senior Italian web developer. 👉 https://www.riccardodegni.com/projects/vanillacreamjs
📦 Installation
Simply include VanillaCreamJS via CDN:
HTML
<script src="https://cdn.jsdelivr.net/npm/vanillacream/dist/vanillacream.min.js"></script>
Code language: HTML, XML (xml)
Or, install it via NPM:
Bash
npm install vanillacreamjs
💥 Superpowers for DOM Elements
At the core of VanillaCream lies the $ function. At first glance it might remind you of jQuery, but it’s an entirely different — and much more modern — universe of functionality.
This function returns an element (or a set of elements) with superpowers, including reactive state binding:
HTML
<div id="app">
<div id="el"></div>
</div>
<script>
const [el, s] = $('#el', { counter: 10 })
el.text = () => s.counter
el.onClick(() => s.counter++)
</script>
Code language: HTML, XML (xml)
Here, the $ function returns a DOM element (in this case el) and a reactive state object s. Every element enhanced by $ exposes powerful properties:
- text: set text content
- html: set inner HTML
- class: dynamically toggle CSS classes
- on: add event listeners (like onClick, onInput, etc.)
- attr: set HTML attributes
- data: set data attributes
When you assign a function to one of these properties, it becomes reactive — automatically updating the DOM when the state changes.
Example: toggling a CSS class
HTML
<div id="app">
<div id="el">box</div>
</div>
<script>
const [el, s] = $('#el', { hl: true })
el.class.highlighted = () => s.hl
el.on.click(() => s.hl = !s.hl)
</script>
<style>
.highlighted { background-color: lightgreen; padding: 10px; }
</style>
Code language: HTML, XML (xml)
Clicking the element toggles the value of s.hl, which in turn dynamically adds or removes the highlighted class — with no need for manual DOM manipulation.
⚙️ Bulk Reactivity with .set
You can also apply multiple reactive properties at once using .set:
JavaScript
el.css.set({
color: 'blue',
backgroundColor: () => state.bg,
width: () => state.x + 'px'
})
el.class.set({
active: () => state.active,
box: () => state.counter >= 10,
otherClass: () => state.active && state.counter >= 10
})
el.attr.set({
title: () => `Title: ${state.title}`
})
el.data.set({
x: () => state.x * 10
})
Code language: CSS (css)
Refs: Powerful Destructuring
The $.refs function accepts a root element and returns auto-bound variables for every ref in the template, plus a shared reactive state object.
HTML
<div id="app">
<div ref="el"></div>
<button ref="btn">Update state</button>
</div>
<script>
const { el, btn, state } = $.refs('#app', { counter: 0, bg: 'lightblue', boxClass: false })
el.html = () => `Counter: ${state.counter}`
el.css.backgroundColor = () => state.bg
el.class.box = () => state.boxClass
btn.onClick(() => {
state.counter++
if (state.bg !== 'lightgreen') state.bg = 'lightgreen'
state.boxClass = !state.boxClass
})
</script>
Code language: HTML, XML (xml)
You can even namespace your refs for more organized destructuring:
HTML
<button ref="btn.AZ">Sort A-Z</button>
<button ref="btn.ZA">Sort Z-A</button>
<button ref="btn.Random">Randomize</button>
<ul ref="el"></ul>
<script>
const { el, btn, state } = $.refs('#app', { /* ... */ })
</script>
Code language: HTML, XML (xml)
🧠 Deep Reactivity
VanillaCream’s reactive engine supports both primitive and reference types like arrays, objects, and matrices. You can trigger reactivity simply by mutating array elements or object properties — even using methods like push, pop, or splice.
HTML
<div id="app">
<div ref="box"></div>
Insert fruit: <input ref="fruit">
<button ref="btn">Add Fruit</button>
</div>
<script>
const { box, fruit, btn, state } = $.refs('#app', { fruits: ['apple', 'pear', 'kiwi'] })
box.text = () => 'Fruits: ' + state.fruits.join(', ')
btn.onClick(() => {
state.fruits.push(fruit.attr.value)
fruit.attr.value = ''
})
</script>
Code language: HTML, XML (xml)
Here, updating the array automatically updates the DOM. No need for complex hooks or rendering logic — just plain JavaScript.
Reactive HTML Templates
VanillaCream supports reactive expressions directly in your HTML using x- attributes:
HTML
<div id="app">
<div x-text="`Counter: ${state.counter}`"></div>
<button x-on.click="() => state.counter++">Increase counter</button>
<div x-if="state.counter === 10">Counter is 10!</div>
</div>
<script>
const { state } = $.refs('#app', { counter: 0 })
</script>
Code language: HTML, XML (xml)
AJAX Requests Made Easy
VanillaCream gives you total control over AJAX with a very simple syntax.
JavaScript
// Generic example (not explicitly detailed in the original text but implied by context)
// Assumed 'el' is a reactive DOM element.
el.post('https://jsonplaceholder.typicode.com/posts/', {
data: {
title: 'VanillaCream.js',
body: 'Best Vanilla JS framework',
userId: 1
}
})
// The original text mentions 'el.html = result' immediately after the request,
// suggesting 'result' is the response from the request.
// A more complete example of how this might work is shown here.
// Note that there's an inconsistency in the original text where the 'data: { ... }' line
// appears to be floating rather than inside a method call.
// It's assumed it was intended to be part of a POST call.
Code language: PHP (php)
🧩 Creating Reusable Components with VanillaCream
One of the most powerful features of VanillaCream.js is the ability to build reusable, reactive components in pure JavaScript — without the need for custom render functions, hooks, or any virtual DOM.
Here’s how you can define a minimal, yet fully reactive component:
JavaScript
const Counter = {
state: () => ({ count: 0 }),
template: `
<p ref="p"></p>
<button ref="btn">+</button>
`,
setup({ refs, state }) {
const {p, btn} = refs
p.text = () => `Count: ${state.count}`
btn.onClick(() => state.count++)
}
}
Code language: JavaScript (javascript)
To render this component in one or more places, just use:
JavaScript
$('#counter1, #counter2').component(Counter)
Code language: JavaScript (javascript)
Components in VanillaCream are ultra-light, reactive by default, and can be created with just a simple object structure: state, template, and setup.
Bonus Tip: Components with Parameters
VanillaCream components can also receive custom input data. Just pass it as a second parameter:
HTML
<div id="app">
<div ref="counter1"></div>
<div ref="counter2"></div>
</div>
<script>
const Counter = {
data: {start: 0},
state: () => ({ count: 0 }),
template: `<p ref="p"></p><button ref="btn">+</button>`,
setup({ refs, state, data }) {
state.count = data.start
refs.p.text = () => `Count: ${state.count}`
refs.btn.onClick(() => state.count++)
}
}
const {counter1, counter2} = $.refs('#app')
counter1.component(Counter, { data: {start: 5} })
counter2.component(Counter, { data: {start: 100} })
</script>
Code language: HTML, XML (xml)
Using Computed Properties
In VanillaCream, computed properties allow you to derive values based on other reactive state — similar to what you’d find in Vue or SolidJS — but in a cleaner, lighter syntax.
HTML
HTML
<div id="app">
<div ref="box"></div>
<input ref="input" type="text">
</div>
<script>
const { box, input, state } = $.refs('#app', {
firstName: 'John',
lastName: 'Doe'
})
const fullName = () => `${state.firstName} ${state.lastName}`
box.text = () => `Hello, ${fullName()}!`
input.onInput(() => {
const [fn, ln] = input.attr.value.split(' ')
state.firstName = fn
state.lastName = ln ?? ''
})
</script>
Code language: HTML, XML (xml)
Works with Any Backend
Although VanillaCream supports components, you can also delegate component rendering to your backend. It integrates seamlessly with any backend stack.
For example, in Laravel you could have a Blade view providing data from the DB:
PHP
<div id="app">
<div ref="box"></div>
Insert fruit: <input ref="fruit">
<button ref="btn">Add Fruit</button>
</div>
<script>
const { box, fruit, btn, state } = $.refs('#app', {
fruits: @json($fruits)
})
box.text = () => 'Fruits: ' + state.fruits.join(', ')
btn.onClick(async () => {
const v = fruit.attr.value
await $.get('/my-backend/add-fruit', {
data: { fruit: v }
})
state.fruits.push(v)
fruit.attr.value = ''
})
</script>
Code language: HTML, XML (xml)
🚀 Ready to Try VanillaCream?
Whether you’re building a quick prototype, enhancing an existing app, or simply want to rediscover the joy of writing clean reactive JavaScript — VanillaCream.js offers a lightweight, elegant solution with zero build steps.
🛠️ Playground
Try it out, and experience the power of reactivity — the vanilla way.
You can now copy and paste this text directly into your Word document! Let me know if there’s anything else I can help you with.