Agnostic Micro Reactive JavaScript Library
This library is about 5 KBytes compressed.
LemonadeJS is a dependency-free lightweight library featuring an abstract reactive layer and two-way data binding. It enables the creation of modern platform-agnostic components using pure JavaScript, JSX, or TypeScript.
Weekly downloads
GitHub stars
Free and open-source

Why Use LemonadeJS?
LemonadeJS ensures efficiency, speed, and clarity in your code. It facilitates distribution, standardizes reactivity, and empowers you with full control over your development process. The library is dependency-free, allowing you to decide if you want to use transpiling or external dependencies.
Freedom to Develop Your Way
Whether you prefer the simplicity of JavaScript Browser, the robust type-checking of TypeScript, or the modern syntax of JSX, LemonadeJS supports it all. Designed to adapt to your workflow, the library ensures flexibility without sacrificing performance or compatibility.



A robust JavaScript library in a compressed package of just 5KB.
Integrates with popular front-end frameworks like VUE, React, and Angular.
Code in your browser with no dependencies or transpiling required.
Sugar
Sugar is a communication system in LemonadeJS that enables components to share data and actions using a pub/sub pattern. Components subscribe to named events with set, defining global actions or state updates, and trigger updates with dispatch, ensuring seamless synchronization and reactivity across the application.
When an action is available, you can execute it anywhere in your application using the dispatch method.
Learn more about Sugar
import { set, dispatch } from 'lemonadejs';
export default function Profile() {
// Register this action and make it available on the global scope
set('updateName', (s) => {
this.name = s.name;
});
// Counter is created from the attribute counter
return render => render`<form>
<label>Name:</label><br/>
<input type="text" :bind="${this.name}" /><br/>
</form>`;
}
Two-way Data Binding
LemonadeJS enables two-way data binding to synchronize changes between a component property and its corresponding HTML element value seamlessly.
The :bind creates a transparent event to keep the self property in sync with the value of the input text.
The checkbox works similarly to the example above. The state of the checkbox and the value of the {self}property is bound.
On a radio HTML element, the self attribute should be the same so that self property holds the exact value of the radio.
Components
A component provides a powerful solution for crafting reusable functionalities. This section outlines the essential considerations for developing your custom components within LemonadeJS.



A solution for rendering data in rows and columns. It offers features like search, filter, pagination, and in-cell editing, making it ideal for building complex interfaces.
create dynamic floating modals. It offers flexible configuration options, allowing extended features such as draggability, closability, and resizability to meet specific user requirements.
Create a user-friendly date picker. Utilize event handling to integrate seamlessly with your application’s functionality.
A lightweight, high-performance JavaScript plugin with a reactive design. It offers various configurable options.
<html>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/data-grid/dist/index.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/data-grid/dist/style.min.css" />
<div id='root'></div>
<script>
const datagrid = Datagrid(document.getElementById('root'), {
data: [
{
id: 1,
name: "T-Shirt",
price: 19.99,
description: "This is a high-quality cotton t-shirt in a variety of colors and sizes.",
},
{
id: 2,
name: "Jeans",
price: 49.99,
description: "These are premium denim jeans in a slim-fit style.",
},
{
id: 3,
name: "Sneakers",
price: 79.99,
description: "These are comfortable and stylish sneakers in a range of colors.",
},
{
id: 4,
name: "Backpack",
price: 39.99,
description: "This is a durable and spacious backpack with multiple compartments.",
},
],
columns: [
{ name: 'name', title: 'Product', width: '80px', align: 'left' },
{ name: 'price', title: 'Price', width: '80px', align: 'center' },
{ name: 'description', title: 'Description', width: '300px', align: 'left' },
],
pagination: 2,
});
</script>
</html>
import Datagrid from '@lemonadejs/data-grid';
import '@lemonadejs/data-grid/dist/style.css';
export default function App() {
this.data = [
{
id: 1,
name: "T-Shirt",
price: 19.99,
description: "This is a high-quality cotton t-shirt in a variety of colors and sizes.",
},
{
id: 2,
name: "Jeans",
price: 49.99,
description: "These are premium denim jeans in a slim-fit style.",
},
{
id: 3,
name: "Sneakers",
price: 79.99,
description: "These are comfortable and stylish sneakers in a range of colors.",
},
{
id: 4,
name: "Backpack",
price: 39.99,
description: "This is a durable and spacious backpack with multiple compartments.",
},
]
this.columns = [
{ name: 'name', title: 'Product', width: '200px', align: 'left' },
{ name: 'price', title: 'Price', width: '100px', align: 'center' },
{ name: 'description', title: 'Description', width: '300px', align: 'left' },
]
return render => render`<div style="display: flex; justify-content: space-evenly">
<Datagrid data="${this.data}" columns="${this.columns}" pagination="2" :ref="self.ref" />
</div>`
}
import React, { useRef, useState } from "react";
import Datagrid from '@lemonadejs/data-grid/dist/react';
import '@lemonadejs/data-grid/dist/style.css';
export default function App() {
const datagridRef = useRef();
const [columns] = useState([
{ name: 'name', title: 'Product', width: '200px', align: 'left' },
{ name: 'price', title: 'Price', width: '100px', align: 'center' },
{ name: 'description', title: 'Description', width: '300px', align: 'left' },
])
const [data] = useState([
{
id: 1,
name: "T-Shirt",
price: 19.99,
description: "This is a high-quality cotton t-shirt in a variety of colors and sizes.",
},
{
id: 2,
name: "Jeans",
price: 49.99,
description: "These are premium denim jeans in a slim-fit style.",
},
{
id: 3,
name: "Sneakers",
price: 79.99,
description: "These are comfortable and stylish sneakers in a range of colors.",
},
{
id: 4,
name: "Backpack",
price: 39.99,
description: "This is a durable and spacious backpack with multiple compartments.",
},
])
return (<>
<Datagrid
ref={datagridRef}
data={data}
columns={columns}
pagination={2}
/>
</>);
}
<template>
<div>
<Datagrid ref="datagridRef" :data="data" :columns="columns" :pagination="2" />
</div>
</template>
<script>
import Datagrid from '@lemonadejs/data-grid/dist/vue';
import '@lemonadejs/data-grid/dist/style.css';
export default {
name: 'App',
components: {
Datagrid,
},
data() {
const columns = [
{ name: 'name', title: 'Product', width: '200px', align: 'left' },
{ name: 'price', title: 'Price', width: '100px', align: 'center' },
{ name: 'description', title: 'Description', width: '300px', align: 'left' },
];
const data = [
{
id: 1,
name: 'T-Shirt',
price: 19.99,
description: 'This is a high-quality cotton t-shirt in a variety of colors and sizes.',
},
{
id: 2,
name: 'Jeans',
price: 49.99,
description: 'These are premium denim jeans in a slim-fit style.',
},
{
id: 3,
name: 'Sneakers',
price: 79.99,
description: 'These are comfortable and stylish sneakers in a range of colors.',
},
{
id: 4,
name: 'Backpack',
price: 39.99,
description: 'This is a durable and spacious backpack with multiple compartments.',
},
];
return {
columns,
data,
};
},
};
</script>
<html>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/modal/dist/index.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/modal/dist/style.min.css" />
<div id="root">
<div style="padding: 10px;">
<p>Quick example!</p>
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor...</div>
</div>
</div>
<input type="button" value="Toggle Modal" id="button" />
<script>
const modal = Modal(document.getElementById("root"), {
width: 400,
height: 200,
title: "My window modal",
closed: true,
closable: true,
draggable: true,
position: 'center',
});
button.addEventListener('click', () => {
modal.closed = !modal.closed;
});
</script>
</html>
import lemonade from 'lemonadejs'
import Modal from '@lemonadejs/modal';
import '@lemonadejs/modal/dist/style.css';
export default function App() {
const toggle = () => {
this.modal.closed = ! this.modal.closed;
}
return render => render`<div>
<Modal width="400" height="200" title="My window modal" position="center"
closable="${true}" draggable="${true}" closed="${true}" :ref="self.modal">
<div style="padding: 10px;">
<p>Quick example!</p>
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor...</div>
</div>
</Modal>
<input type="button" value="Toggle Modal" onclick="${toggle}" />
</div>`
}
import React, { useRef, useState } from "react";
import Modal from "@lemonadejs/modal/dist/react";
import "@lemonadejs/modal/dist/style.css";
export default function App() {
const modalRef = useRef(null);
const [closed, setClosed] = useState(true)
return (<>
<Modal ref={modalRef} width={400} height={200} title={"My window modal"} closed={closed}
closable={true} draggable={true} position={"center"}>
<div>
<p>Quick example!</p>
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor...</div>
</div>
</Modal>
<input type="button" value="Toggle Modal" onClick={() => setClosed(!closed)} />
</>);
}
<template>
<Modal ref="modal" :position="center" :width="400" :height="200" title="My window modal"
:closed="true" :closable="true" :draggable="true" position="center">
<div>
<p>Quick example!</p>
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor...</div>
</div>
</Modal>
</template>
<script>
import Modal from '@lemonadejs/modal/dist/vue';
import '@lemonadejs/modal/dist/style.css'
export default {
name: 'App',
components: {
Modal,
},
data() {
return {
width: 400,
height: 200,
title: "My window modal",
closed: true,
closable: true,
draggable: true,
position: 'center',
};
}
};
</script>
<!-- codesandbox: https://codesandbox.io/p/sandbox/dreamy-waterfall-mh572x -->
<html>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/modal/dist/style.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/calendar/dist/style.min.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons" />
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/modal/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/calendar/dist/index.min.js"></script>
<input type="text" id="input-range-eg" /> <div id="root"></div>
<script>
Calendar(document.getElementById("root"), {
range: true,
input: document.getElementById("input-range-eg"),
});
</script>
</html>
// codesandbox: https://codesandbox.io/p/sandbox/lemonadejs-reactive-app-forked-wfjw3n
import Calendar from '@lemonadejs/calendar';
import "@lemonadejs/calendar/dist/style.css";
import "@lemonadejs/modal/dist/style.css";
export default function App() {
return render => render`<div>
<input type="text" :ref="this.input" />
<Calendar :input="this.input" range="${true}" />
</div>`
}
// codesandbox: https://codesandbox.io/p/devbox/nostalgic-jackson-ljty72
import React, { useRef, useEffect, useState } from 'react';
import Calendar from '@lemonadejs/calendar/dist/react';
import "@lemonadejs/calendar/dist/style.css";
import "@lemonadejs/modal/dist/style.css";
export default function App() {
const calendarRef = useRef();
const inputRef = useRef();
return (<>
<input type='text' ref={inputRef}/>
<Calendar range={true} input={inputRef} ref={calendarRef} />
</>);
}
<!-- codesandbox: https://codesandbox.io/p/sandbox/funny-sea-yfxyjr -->
<template>
<input type="text" ref="inputRef" />
<Calendar :input="inputRef" ref="calendarRef" />
</template>
<script>
import { ref } from 'vue';
import Calendar from '@lemonadejs/calendar/dist/vue';
import "@lemonadejs/calendar/dist/style.css";
import "@lemonadejs/modal/dist/style.css";
export default {
name: 'App',
components: {
Calendar
},
setup() {
const inputRef = ref(null);
return {
inputRef,
};
}
}
</script>
<html>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/dropdown/dist/style.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/modal/dist/style.min.css" />
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/dropdown/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/modal/dist/index.min.js"></script>
<div id='root'></div>
<script>
let root = document.getElementById('root');
let data = [];
for (let i = 0; i < 100000; i++) {
data.push({
value: i,
text: faker.commerce.productName()
});
}
// Initial time before creating the table
let s = Date.now();
Dropdown(root, {
data: data,
value: 1,
width: 260,
autocomplete: true,
onload: function() {
let e = Date.now();
let p = document.createElement('p');
p.textContent = 'The dropdown with 100000 items was created in: ' + (e - s) + 'ms';
root.appendChild(p)
},
});
</script>
</html>
import Dropdown from '@lemonadejs/dropdown';
import { faker } from '@faker-js/faker';
import '@lemonadejs/dropdown/dist/style.css'
import '@lemonadejs/modal/dist/style.css';
export default function App() {
let data = [];
for (let i = 0; i < 100000; i++) {
data.push({
value: i,
text: faker.commerce.productName()
});
}
this.data = data;
return render => render`<div>
<Dropdown data="${this.data}" value="${1}" />
</div>`
}
import React, { useRef } from 'react';
import Dropdown from '@lemonadejs/dropdown/dist/react';
import { faker } from '@faker-js/faker';
import '@lemonadejs/dropdown/dist/style.css'
import '@lemonadejs/modal/dist/style.css'
let data = [];
for (let i = 0; i < 100000; i++) {
data.push({
value: i,
text: faker.commerce.productName()
});
}
export default function App() {
const myRef = useRef();
return (<div>
<Dropdown ref={myRef} data={data} value={1} width={260} />
</div>);
}
<template>
<Dropdown :data="data" :value="1" :width="260" />
</template>
<script>
import Dropdown from '@lemonadejs/dropdown/dist/vue';
import { faker } from '@faker-js/faker';
import '@lemonadejs/dropdown/dist/style.css';
import '@lemonadejs/modal/dist/style.css';
export default {
name: 'App',
components: {
Dropdown
},
data() {
let data = [];
for (let i = 0; i < 100000; i++) {
data.push({
value: i,
text: faker.commerce.productName()
});
}
return {
data: data
};
},
}
</script>
JavaScript Components
Explore the powerful and versatile components designed to elevate your productivity. From data management to collaboration, our ecosystem seamlessly integrates to meet your needs.
Jspreadsheet Pro
Enterprise JavaScript data grid component to integrate spreadsheet UI into your web-based application.
Intrasheets
Collaborate with ease using Intrasheets, an intuitive tool for managing spreadsheets across teams, ensuring that everyone stays on the same page.
Subscribe to our newsletter
Tech news, tips and technical advice