{"id":14974646,"url":"https://github.com/nabeelalihashmi/nibble","last_synced_at":"2026-02-06T20:03:04.022Z","repository":{"id":252055611,"uuid":"839272992","full_name":"nabeelalihashmi/nibble","owner":"nabeelalihashmi","description":"A minimal and fast library for automatic reactive DOM updates, two-way data binding with signals.","archived":false,"fork":false,"pushed_at":"2024-08-07T09:55:16.000Z","size":32,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-26T21:02:46.713Z","etag":null,"topics":["dom","dom-manipulation","framework","javascript","javascript-framework","javascript-library","laravel","laravel-framework","reactive","reactivity","signal","signals"],"latest_commit_sha":null,"homepage":"https://nabeelalihashmi.github.io/nibble/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nabeelalihashmi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-07T09:27:00.000Z","updated_at":"2025-05-22T16:17:23.000Z","dependencies_parsed_at":"2024-08-07T13:13:12.341Z","dependency_job_id":"da6318f3-7c6a-4bd5-85fe-c3098b39de27","html_url":"https://github.com/nabeelalihashmi/nibble","commit_stats":null,"previous_names":["nabeelalihashmi/nibble"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nabeelalihashmi/nibble","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabeelalihashmi%2Fnibble","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabeelalihashmi%2Fnibble/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabeelalihashmi%2Fnibble/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabeelalihashmi%2Fnibble/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nabeelalihashmi","download_url":"https://codeload.github.com/nabeelalihashmi/nibble/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabeelalihashmi%2Fnibble/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29174345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T19:56:27.068Z","status":"ssl_error","status_checked_at":"2026-02-06T19:56:18.934Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dom","dom-manipulation","framework","javascript","javascript-framework","javascript-library","laravel","laravel-framework","reactive","reactivity","signal","signals"],"created_at":"2024-09-24T13:50:51.395Z","updated_at":"2026-02-06T20:03:04.000Z","avatar_url":"https://github.com/nabeelalihashmi.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nibble.js\n\n![nibble.js Logo](nibble.png)\n\n[![LinkedIn](https://img.shields.io/badge/LinkedIn-Profile-blue)](https://linkedin.com/in/nabeelalihashmi)\n[![Website](https://img.shields.io/badge/Website-aliveforms.com-green)](https://aliveforms.com)\n\nNibbleJS is a minimal lightweight and efficient JavaScript framework for reactive programming and data binding. It provides a minimalistic approach to manage reactivity with signals in your web applications. It is written in 150 lines of code only. It's API is extremly small, so learning curve is very small. Signal API is composed of 4 functions only: `signal`, `compute`, `watch`, `effect` and Nibble DOM has only following directives `bind:value|text|html|group|any attribute`, `class:`, `this`, `if` and `nibble`if you want to activate nibble dom only on selected nodes,\n\n![Lines of Code](cloc.png)\n\n\n## Developer Information\n\n- **Developer:** Nabeel Ali\n- **Email:** mail2nabeelali@gmail.com\n- **LinkedIn:** [https://linkedin.com/in/nabeelalihashmi](https://linkedin.com/in/nabeelalihashmi)\n- **Twitter/X:** [https://twitter.com/nabeelalihashmi](https://twitter.com/nabeelalihashmi)\n- **YouTube:** [https://youtube.com/@nabeelalihashmi](https://youtube.com/@nabeelalihashmi)\n- **Website:** [aliveforms.com](https://aliveforms.com)\n\n\n\n## Why NibbleJS?\n\nNibbleJS is designed to remove the overhead of large frameworks while simplifying the development of web applications by offering a streamlined, easy-to-use reactive programming library. Here are some reasons why you might choose NibbleJS:\n\n- **No Compilation Required:** NibbleJS works directly with the DOM. Just plug and play.\n- **Decoupled Design:** You can use NibbleJS with signals for reactivity or add the DOM library for additional features.\n- **Lightweight and Fast:** Optimized for performance, ensuring minimal overhead.\n- **Flexible Data Binding:** Easily bind data to DOM elements and keep your UI in sync with your state.\n- **Minimal Codebase:** At just 150 lines of code, NibbleJS provides a compact alternative to larger frameworks like Vue and Svelte, reducing complexity while maintaining functionality.\n- **Avoid Overhead:** Designed to avoid the overhead of larger frameworks, making it easier to integrate with other platforms.\n- **Easy Integration:** Can be seamlessly used with PHP frameworks like Laravel to add interactivity without the need for heavy dependencies.\n\nWith NibbleJS, you can achieve efficient reactivity and data binding in your web applications without the bloat of larger frameworks.\n\n## Features\n\n## Features of NibbleJS\n\n- **Reactive Programming:** Manage reactivity efficiently with a simple signal system.\n- **Flexible Data Binding:**\n  - **Two-Way Binding:** Use `bind:value` for two-way data binding between DOM elements and application state.\n  - **Property Binding:** Bind attributes to elements with `bind:`, enabling dynamic property updates.\n  - **Group Binding:** Handle radio and checkbox groups efficiently with group binding functionality with `bind:group`.\n- **Conditional Rendering:** Use `if` for conditional rendering of DOM elements based on your application's state.\n- **Class Binding:** Dynamically add or remove CSS classes with `class:` for better styling control.\n- **Watcher:** Utilize watchers to react to changes in state and perform actions accordingly.\n- **Minimal Overhead:** Avoids the complexity and bloat of larger frameworks, making integration with other platforms seamless.\n- **Easy Integration:** Can be easily integrated with PHP frameworks like Laravel to add interactivity to your applications.\n- **Dynamic Updates:** Supports real-time updates and efficient DOM manipulation.\n- **Simple API:** Provides a straightforward API for managing signals, effects, and reactivity.\n- **No Dependencies:** Operates independently of other libraries, reducing potential conflicts and bloat.\n- **Cross-Browser Compatibility:** Works across various browsers to ensure consistent functionality.\n\n\n\n## Installation and Usage\n\nDownload the files and link using script\n```\n\u003cscript src=\"signal.js\"\u003e\n\u003cscript src=\"dom.js\"\u003e\n```\n\nVia CDN links, making it easy to include in your project:\n\n```\n\u003cscript src=\"https://cdn.jsdelivr.net/gh/nabeelalihashmi/nibble@main/js/v1/signal.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/gh/nabeelalihashmi/nibble@main/js/v1/dom.js\"\u003e\u003c/script\u003e\n```\n\n## Signal API Documentation\n\n### signal\n\nCreates a reactive signal.\n\n```\nconst count = signal(0);\n```\n\n### effect\n\nRuns a function whenever its dependent signals change.\n\n```\neffect(() =\u003e {\n    console.log(count.value);\n});\n```\n\n### compute\n\nCreates a computed signal based on other signals.\n\n```\nconst doubleCount = compute(() =\u003e count.value * 2);\n```\n\n### watch\n\nWatches for changes in a signal and runs a callback with the old and new values.\n\n```\nwatch(count, (newValue, oldValue) =\u003e {\n    console.log(`Count changed from ${oldValue} to ${newValue}`);\n});\n```\n\n## Nibble DOM Documentation\n\n\nNibbleJS supports various bindings and directives to synchronize the DOM with your application state. When using NibbleJS DOM functionality, make sure to declare signals with `var`. call `nibbleDom` to initialize. \n\n### nibbleDom\n\nInitializes NibbleJS bindings in the DOM.\n\n```\ndocument.addEventListener(\"DOMContentLoaded\", () =\u003e {\n    nibbleDom();\n});\n```\n\n### bind:value\n\nBinds the value of an input element to a signal.\n\n```\n\u003cinput type=\"number\" bind:value=\"count\" /\u003e\n```\n\n### bind:text\n\nBinds the inner text of an element to a signal.\n\n```\n\u003cbutton bind:text=\"count\"\u003e0\u003c/button\u003e\n```\n\n### bind:html\n\nBinds the inner HTML of an element to a signal.\n\n```\n\u003cdiv bind:html=\"doubleCount\"\u003e0\u003c/div\u003e\n```\n\n\n### bind:property\n\nBinds the property of an element to a signal.\n\n```\n\u003cdiv bind:src=\"imgLink\"\u003e0\u003c/div\u003e\n```\n\n```\n\u003cbutton this=\"dis\" bind:disabled=\"disable\"\u003eDisable\u003c/button\u003e\n\n// after nibble dom is initialized\n dis.addEventListener('click', function () {\n    disable.value = \"disable\";\n})\n\n```\n\n\n### bind:group\n\nBinds a group of checkboxes or radio buttons to a signal.\n\n#### Checkbox Group\n\n```\n\u003cinput type=\"checkbox\" name=\"fruits\" value=\"apple\" bind:group=\"selectedFruits\" /\u003e\n\u003cinput type=\"checkbox\" name=\"fruits\" value=\"banana\" bind:group=\"selectedFruits\" /\u003e\n\u003cinput type=\"checkbox\" name=\"fruits\" value=\"mango\" bind:group=\"selectedFruits\" /\u003e\n```\n\n#### Radio Group\n\n```\n\u003cinput type=\"radio\" name=\"agree\" value=\"no\" bind:group=\"agree\" /\u003e\n\u003cinput type=\"radio\" name=\"agree\" value=\"yes\" bind:group=\"agree\" /\u003e\n```\n\n### class:classname\n\nToggles a class on an element based on a signal's value.\n\n```\n\u003cbutton class:bg-danger=\"isDanger\"\u003e0\u003c/button\u003e\n```\n\n### if\n\nConditionally displays an element based on a signal's value.\n\n```\n\u003cdiv if=\"agreement\"\u003e\n    \u003ch1\u003eOOOPS!!!\u003c/h1\u003e\n\u003c/div\u003e\n```\n\n\n\n### this\n\nBinds reference of button without requiring querySelector or getElementById. Only usable after `nibbleDom()` is called, which is automatically called in dom.js. Make sure to put the code access reference after DOMContentLoaded.\n\n```\n\u003cbutton bind:this=\"myButton\"\u003eSet\u003c/button\u003e\n\nmyButton.addEventListener('click', function() {\n    name.value = 'Test'\n})\n```\n\n### nibble\n\nBy default, Nibble DOM selects all nodes while applying. To optimize it, set `window.explicitNibble = true` before adding `dom.js` and in elements, add attribute `nibble`\n\n## Examples\n\n### Singls Only without Nibble DOM\n```\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\n\u003chead\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n    \u003ctitle\u003eNibbleJS\u003c/title\u003e\n    \u003cstyle\u003e\n        body {\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            min-height: 100vh;\n        }\n\n        .main {\n            padding: 1rem;\n            border: 1px solid #FFBF00;\n            border-radius: 1rem;\n        }\n    \u003c/style\u003e\n\n\u003clink rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/picocss/2.0.6/pico.amber.min.css' integrity='sha512-kvYLueAc7RD0XOfxhjiaUKbXBmh5JTZdyJo/12oY/zpT0l6o82H2Ap5f27CrAa16VgvXj02Rsb0Prwtq7oYOSw==' crossorigin='anonymous'/\u003e\n\u003c/head\u003e\n\n\u003cbody\u003e\n\n    \u003c!-- Signals Only without Nibble DOM --\u003e\n    \u003cdiv class=\"main border\"\u003e\n        \u003cdiv\u003e\n            \u003ch1\u003eNibbleJS Signal\u003c/h1\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n            \u003cinput type=\"number\" id=\"number\"\u003e\n        \u003c/div\u003e\n        \u003cp\u003e\n            Value: \u003cspan id=\"value\"\u003e\u003c/span\u003e \u003cbr\u003e\n            Computed: \u003cspan id=\"computed\"\u003e\u003c/span\u003e\n        \u003c/p\u003e\n        \u003cp\u003e\n            \u003cbutton id=\"button\"\u003eReset\u003c/button\u003e\n        \u003c/p\u003e\n        \u003cp\u003e\n            \u003csmall\u003e\n                Created by Nabeel Ali - \u003ca href=\"https://linkedin.com/in/nabeelalihashmi\"\u003elinkedin.com/in/nabeelalihashmi\u003c/a\u003e\n            \u003c/small\u003e\n        \u003c/p\u003e\n    \u003c/div\u003e\n\n    \u003c!-- Nibble DOM --\u003e\n\n    \u003cscript src=\"./js/v1/signal.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"./js/v1/dom.js\"\u003e\u003c/script\u003e\n\n    \u003cscript\u003e\n        // Complete signal api has only following members \n        // signal, compute, effect, watch\n\n        let count = signal(0);\n        let computedValue = compute(() =\u003e count.value * 10);\n\n        let input = document.querySelector(\"#number\");\n        let valueDiv = document.querySelector(\"#value\");\n        let computedDiv = document.querySelector(\"#computed\");\n        let resetButton = document.querySelector(\"#button\");\n\n        input.addEventListener('input', function (event) {\n            count.value = event.target.value\n        })\n\n        resetButton.addEventListener('click', function () {\n            count.value = 0;\n        })\n\n        effect(() =\u003e {\n            valueDiv.innerText = count.value;\n        })\n\n        effect(() =\u003e {\n            input.value = count.value;\n        })\n\n\n        effect(() =\u003e {\n            computedDiv.innerText = computedValue.value;\n        })\n\n        watch(count, (newValue, oldValue) =\u003e {\n            console.log(`count changed from ${oldValue} to ${newValue}`)\n            if (newValue \u003e 50) {\n                alert(\"Above 50 not allowed\");\n                count.value = 50;\n            }\n        })\n    \u003c/script\u003e\n\u003c/body\u003e\n\n\u003c/html\u003e\n```\n\n### With Nibble DOM\n\n```\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\n\u003chead\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n    \u003ctitle\u003eNibbleJS\u003c/title\u003e\n    \u003cstyle\u003e\n        body {\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            min-height: 100vh;\n        }\n\n        .main {\n            padding: 1rem;\n            border: 1px solid #FFBF00;\n            border-radius: 1rem;\n        }\n\n        .danger {\n            border: 2px solid #ff0000 !important;\n        }\n    \u003c/style\u003e\n\n    \u003clink rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/picocss/2.0.6/pico.amber.min.css'\n        integrity='sha512-kvYLueAc7RD0XOfxhjiaUKbXBmh5JTZdyJo/12oY/zpT0l6o82H2Ap5f27CrAa16VgvXj02Rsb0Prwtq7oYOSw=='\n        crossorigin='anonymous' /\u003e\n\u003c/head\u003e\n\n\u003cbody\u003e\n\n    \u003c!-- Signals Only with Nibble DOM --\u003e\n    \u003c!-- bind:value|text|html|property, class:, this, if --\u003e\n\n    \u003cdiv class=\"main border\" class:danger=\"isDanger\"\u003e\n        \u003cdiv\u003e\n            \u003ch1\u003eNibbleJS DOM\u003c/h1\u003e\n        \u003c/div\u003e\n        \u003cdiv\u003e\n            \u003cinput type=\"number\" bind:value=\"count\"\u003e\n        \u003c/div\u003e\n        \u003cp\u003e\n            Value: \u003cspan bind:text=\"count\"\u003e\u003c/span\u003e \u003cbr\u003e\n            Computed: \u003cspan bind:text=\"computedValue\"\u003e\u003c/span\u003e\n        \u003c/p\u003e\n        \u003cp\u003e\n            \u003cbutton this=\"button\"\u003eReset\u003c/button\u003e\n        \u003c/p\u003e\n       \n        \u003cp\u003e\n        \u003cfieldset\u003e\n            \u003clegend\u003eFruits\u003c/legend\u003e\n            \u003cdiv\u003e\n                \u003cul bind:html=\"fruits\" class=\"list-group\"\u003e\u003c/ul\u003e\n            \u003c/div\u003e\n            \u003cdiv\u003e\n                \u003cinput type=\"checkbox\" id=\"apple\" name=\"fruit[]\" value=\"apple\" bind:group=\"selectedFruits\" /\u003e\n                \u003clabel for=\"apple\"\u003eApple\u003c/label\u003e\n            \u003c/div\u003e\n            \u003cdiv\u003e\n                \u003cinput type=\"checkbox\" id=\"banana\" name=\"fruit[]\" value=\"banana\" bind:group=\"selectedFruits\" /\u003e\n                \u003clabel for=\"banana\"\u003eBanana\u003c/label\u003e\n            \u003c/div\u003e\n            \u003cdiv\u003e\n                \u003cinput type=\"checkbox\" id=\"mango\" name=\"fruit[]\" value=\"mango\" bind:group=\"selectedFruits\" /\u003e\n                \u003clabel for=\"mango\"\u003eMango\u003c/label\u003e\n            \u003c/div\u003e\n        \u003c/fieldset\u003e\n        \u003c/p\u003e\n\n        \u003cp\u003e\n        \u003cfieldset\u003e\n            \u003clegend\u003eAgree\u003c/legend\u003e\n            \u003cdiv if=\"agreement\" class:danger=\"isDanger\"\u003e\n                \u003ch3\u003eConditional Agree\u003c/h3\u003e\n            \u003c/div\u003e\n    \n            \u003cdiv class=\"form-check\"\u003e\n                \u003cinput type=\"radio\" id=\"agree-no\" name=\"agree\" value=\"no\" bind:group=\"agree\" /\u003e\n                \u003clabel for=\"agree-no\"\u003eNo\u003c/label\u003e\n            \u003c/div\u003e\n            \u003cdiv\u003e\n                \u003cinput type=\"radio\" id=\"agree-yes\" name=\"agree\" value=\"yes\" bind:group=\"agree\"\n                 /\u003e\n                \u003clabel for=\"agree-yes\"\u003eYes\u003c/label\u003e\n            \u003c/div\u003e\n        \u003c/fieldset\u003e\n        \u003c/p\u003e\n\n        \u003cp\u003e\n            \u003csmall\u003e\n                Created by Nabeel Ali - \u003ca href=\"https://linkedin.com/in/nabeelalihashmi\"\u003elinkedin.com/in/nabeelalihashmi\u003c/a\u003e\n            \u003c/small\u003e\n        \u003c/p\u003e\n    \u003c/div\u003e\n\n    \u003c!-- Nibble DOM --\u003e\n\n    \u003cscript src=\"./js/v1/signal.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"./js/v1/dom.js\"\u003e\u003c/script\u003e\n\n    \u003cscript\u003e\n        // Complete Nibble DOM api has only following directives \n\n        // with dom, signal must use var\n        var count = signal(0);\n        var computedValue = compute(() =\u003e count.value * 10);\n\n        var selectedFruits = signal([\"mango\"]);\n        var fruits = compute(() =\u003e\n            selectedFruits.value.map((f) =\u003e `\u003cli class=\"list-group-item\"\u003e${f}\u003c/li\u003e`).join(\"\"),\n        );\n\n        var agree = signal(\"yes\");\n        var agreement = compute(() =\u003e\n            agree.value == \"yes\" ? true : false,\n        );\n\n        var isDanger = compute(() =\u003e count.value \u003e 30 ? true : false);\n        var showItem = compute(() =\u003e count.value \u003e 30 ? true : false);\n\n        document.addEventListener('DOMContentLoaded', app);\n        function app() {\n\n            console.log('loaded')\n            button.addEventListener('click', function () {\n                count.value = 0;\n            })\n\n            // make sure it runs after DOMContentLoaded\n            watch(count, (oldValue, newValue) =\u003e {\n                console.log(`count changed from ${oldValue} to ${newValue}`)\n                if (newValue \u003e 50) {\n                    alert(\"Above 50 not allowed\");\n                    count.value = 50;\n                }\n            })\n\n        }\n    \u003c/script\u003e\n\u003c/body\u003e\n\n\u003c/html\u003e\n```\n\n## Feedback\n\nI value your feedback! Please share your thoughts, suggestions, and experiences with NibbleJS. Your input helps to improve and tailor the framework to better meet your needs.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnabeelalihashmi%2Fnibble","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnabeelalihashmi%2Fnibble","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnabeelalihashmi%2Fnibble/lists"}