{"id":41139329,"url":"https://github.com/thomasbrus/stimulus-reactive-ui","last_synced_at":"2026-01-22T18:46:51.653Z","repository":{"id":305486969,"uuid":"1022996664","full_name":"thomasbrus/stimulus-reactive-ui","owner":"thomasbrus","description":"A reactive UI system built on top of Stimulus.js that enables declarative, state-driven interfaces with minimal JavaScript. ","archived":false,"fork":false,"pushed_at":"2025-07-27T16:41:05.000Z","size":210,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-27T18:47:39.900Z","etag":null,"topics":["hotwire","stimulus","turbo"],"latest_commit_sha":null,"homepage":"https://thomasbrus.github.io/stimulus-reactive-ui","language":"HTML","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thomasbrus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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,"zenodo":null}},"created_at":"2025-07-20T09:57:52.000Z","updated_at":"2025-07-27T16:41:08.000Z","dependencies_parsed_at":"2025-07-20T12:19:23.454Z","dependency_job_id":"142ced4d-31c3-44b3-9188-e45a11451536","html_url":"https://github.com/thomasbrus/stimulus-reactive-ui","commit_stats":null,"previous_names":["thomasbrus/stimulus-reactive-ui"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/thomasbrus/stimulus-reactive-ui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasbrus%2Fstimulus-reactive-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasbrus%2Fstimulus-reactive-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasbrus%2Fstimulus-reactive-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasbrus%2Fstimulus-reactive-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thomasbrus","download_url":"https://codeload.github.com/thomasbrus/stimulus-reactive-ui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasbrus%2Fstimulus-reactive-ui/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28668384,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T17:07:18.858Z","status":"ssl_error","status_checked_at":"2026-01-22T17:05:02.040Z","response_time":144,"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":["hotwire","stimulus","turbo"],"created_at":"2026-01-22T18:46:47.761Z","updated_at":"2026-01-22T18:46:51.643Z","avatar_url":"https://github.com/thomasbrus.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Stimulus Reactive UI\n\nA reactive UI system built on top of Stimulus.js that enables declarative, state-driven interfaces with minimal JavaScript. This project demonstrates how to create dynamic, interactive web applications using reactive patterns within the Stimulus framework.\n\n## Demo\n\n\u003e [!TIP]\n\u003e 🚀 **[View Live Demo](https://thomasbrus.github.io/stimulus-reactive-ui/)**\n\n---\n\n\u003ca href=\"https://thomasbrus.github.io/stimulus-reactive-ui/\"\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/thomasbrus/stimulus-reactive-ui/refs/heads/main/demo.png\" alt=\"Stimulus Reactive UI Demo\" /\u003e\n\u003c/a\u003e\n\n---\n\n```html\n\u003cdiv data-controller=\"live\"\u003e\n  \u003c!-- State --\u003e\n  \u003cinput type=\"hidden\" data-live-state=\"count\" value=\"0\" /\u003e\n\n  \u003c!-- Computed properties --\u003e\n  \u003cscript type=\"text/template\" data-live-computed=\"isPositive\"\u003e\n    state.count \u003e 0\n  \u003c/script\u003e\n  \u003cscript type=\"text/template\" data-live-computed=\"statusText\"\u003e\n    state.count \u003c 0 ? 'negative' : 'positive'\n  \u003c/script\u003e\n\n  \u003c!-- Demo --\u003e\n  \u003ch2 live:text=\"state.count\"\u003e0\u003c/h2\u003e\n  \u003cp live:class=\"{ 'bg-green-100': computed.isPositive, 'bg-red-100': !computed.isPositive }\"\u003e\n    Counter is \u003cspan live:text=\"computed.statusText\"\u003epositive\u003c/span\u003e\n  \u003c/p\u003e\n\n  \u003cbutton data-action=\"click-\u003elive#update\" data-live-update-param=\"state.count++\"\u003eIncrement\u003c/button\u003e\n  \u003cbutton data-action=\"click-\u003elive#update\" data-live-update-param=\"state.count--\"\u003eDecrement\u003c/button\u003e\n\u003c/div\u003e\n```\n\nThe demo is entirely self-contained in a single `index.html` file that showcases 13 different interactive examples. Everything needed to run the demo is included:\n\n- **Tailwind CSS** - Loaded via CDN for styling\n- **Stimulus.js** - Imported via ES modules from unpkg\n- **LiveController** - Custom Stimulus controller that implements the reactive system\n- **Interactive Examples** - 11 demonstrations of reactive UI patterns\n\n## Features\n\n- **Declarative Reactive Bindings** - Use `live:text`, `live:class`, `live:show`, etc. to bind state to DOM elements\n- **Computed States** - Define derived state using template scripts\n- **Two-way Data Binding** - Automatic synchronization between form inputs and state\n- **Conditional Rendering** - Show/hide elements based on state\n- **Dynamic Styling** - Apply CSS classes and styles reactively\n- **State Management** - Clean, predictable state updates with automatic UI synchronization\n\n## Usage\n\n### Live States\n\nDefine reactive state properties that can be bound to form inputs or calculated dynamically.\n\n**Form Input Binding** - Use `data-live-state=\"propertyName\"` on inputs:\n\n```html\n\u003cinput type=\"text\" data-live-state=\"username\" value=\"john\" /\u003e\n\u003cinput type=\"checkbox\" data-live-state=\"isActive\" /\u003e\n\u003cselect data-live-state=\"theme\"\u003e\n  \u003coption value=\"light\"\u003eLight\u003c/option\u003e\n  \u003coption value=\"dark\"\u003eDark\u003c/option\u003e\n\u003c/select\u003e\n```\n\n**Computed States** - Use `\u003cscript type=\"text/template\" data-live-computed=\"propertyName\"\u003e` with JavaScript expressions:\n\n```html\n\u003cscript type=\"text/template\" data-live-computed=\"fullName\"\u003e\n  state.firstName + ' ' + state.lastName\n\u003c/script\u003e\n\u003cscript type=\"text/template\" data-live-computed=\"isValid\"\u003e\n  state.username.length \u003e 3 \u0026\u0026 state.email.includes('@')\n\u003c/script\u003e\n```\n\n**HTML Template Literals** - When a computed property starts with `\u003c`, it's automatically treated as an HTML template literal:\n\n```html\n\u003c!-- Simple HTML templates with interpolation --\u003e\n\u003cscript type=\"text/template\" data-live-computed=\"userCard\"\u003e\n  \u003cdiv class=\"user-card\"\u003e\n    \u003ch3\u003e${state.userName}\u003c/h3\u003e\n    \u003cp\u003eStatus: ${computed.userStatus}\u003c/p\u003e\n  \u003c/div\u003e\n\u003c/script\u003e\n\n\u003c!-- Complex templates with conditional content --\u003e\n\u003cscript type=\"text/template\" data-live-computed=\"notificationHtml\"\u003e\n  \u003cdiv class=\"notification ${computed.notificationType}\"\u003e\n    \u003cstrong\u003e${computed.title}\u003c/strong\u003e\n    \u003cp\u003e${state.message}\u003c/p\u003e\n    ${computed.showTimestamp ? `\u003csmall\u003e${computed.timestamp}\u003c/small\u003e` : ''}\n  \u003c/div\u003e\n\u003c/script\u003e\n```\n\n\u003e **Important:** Within computed property definitions, use `state.` to reference base state properties and `computed.` to reference other computed properties. In your HTML templates, use `computed.` to reference computed properties and `state.` for base state properties.\n\n```html\n\u003c!-- In computed definitions --\u003e\n\u003cscript type=\"text/template\" data-live-computed=\"greeting\"\u003e\n  'Hello, ' + state.firstName + ' ' + state.lastName\n\u003c/script\u003e\n\u003cscript type=\"text/template\" data-live-computed=\"formalGreeting\"\u003e\n  computed.greeting + '! Welcome to our application.'\n\u003c/script\u003e\n\n\u003c!-- In HTML templates --\u003e\n\u003ch1 live:text=\"computed.greeting\"\u003eHello\u003c/h1\u003e\n\u003cp live:text=\"computed.formalGreeting\"\u003eWelcome\u003c/p\u003e\n```\n\n### Live Attributes\n\nBind state to DOM elements using special `live:*` attributes that automatically update when state changes.\n\n| Attribute         | Description                   | Example                                                  |\n| ----------------- | ----------------------------- | -------------------------------------------------------- |\n| `live:text`       | Sets element text content     | `live:text=\"state.count\"`                                |\n| `live:html`       | Sets element innerHTML        | `live:html=\"state.content\"`                              |\n| `live:class`      | Conditionally applies classes | `live:class=\"{ 'active': state.isActive }\"`              |\n| `live:style`      | Sets CSS styles               | `live:style=\"{ color: state.textColor }\"`                |\n| `live:show`       | Shows/hides element           | `live:show=\"state.isVisible\"`                            |\n| `live:disabled`   | Enables/disables element      | `live:disabled=\"!state.isValid\"`                         |\n| `live:attributes` | Sets multiple HTML attributes | `live:attributes=\"{ src: state.url, alt: state.title }\"` |\n\n**Examples:**\n\n```html\n\u003c!-- Text and HTML content --\u003e\n\u003ch1 live:text=\"state.title\"\u003eDefault Title\u003c/h1\u003e\n\u003cdiv live:html=\"computed.htmlContent\"\u003e\u003c/div\u003e\n\n\u003c!-- Conditional styling --\u003e\n\u003cbutton\n  live:class=\"{\n  'btn-primary': computed.isActive,\n  'btn-secondary': !computed.isActive,\n  'disabled': computed.isLoading\n}\"\n\u003e\n  Submit\n\u003c/button\u003e\n\n\u003c!-- Dynamic styles --\u003e\n\u003cdiv\n  live:style=\"{\n  width: computed.progress + '%',\n  backgroundColor: state.color\n}\"\n\u003e\u003c/div\u003e\n\n\u003c!-- Show/hide and enable/disable --\u003e\n\u003cdiv live:show=\"computed.showDetails\"\u003eDetails content\u003c/div\u003e\n\u003cbutton live:disabled=\"!computed.isValid\"\u003eSave\u003c/button\u003e\n\n\u003c!-- Multiple attributes at once --\u003e\n\u003cimg\n  live:attributes=\"{\n  src: computed.imageUrl,\n  alt: computed.imageDescription,\n  title: computed.imageTitle\n}\"\n/\u003e\n\u003cinput\n  live:attributes=\"{\n  placeholder: computed.placeholderText,\n  'data-tooltip': computed.helpText,\n  'aria-label': computed.accessibilityLabel\n}\"\n/\u003e\n```\n\n**State Updates** - Trigger state changes using the `live#update` action:\n\n```html\n\u003cbutton data-action=\"click-\u003elive#update\" data-live-update-param=\"state.count++\"\u003eIncrement\u003c/button\u003e\n\u003cbutton data-action=\"click-\u003elive#update\" data-live-update-param=\"state.isVisible = !state.isVisible\"\u003eToggle\u003c/button\u003e\n```\n\n## Browser Support\n\nThis project uses modern JavaScript features including:\n\n- ES6 Modules\n- Proxy objects\n- MutationObserver\n- Template literals\n\nSupported in all modern browsers (Chrome 61+, Firefox 60+, Safari 12+, Edge 79+).\n\n## Security Considerations\n\nWhen implementing reactive UI systems that interpolate user input, it's important to consider security implications. This demo evaluates dynamic code using `new Function()`, which is safer and more performant than `eval()` but still carries risks with untrusted input.\n\nFor production applications planning to interpolate user input, it is recommended to restrict JavaScript execution to a safe subset using libraries like [jse-eval](https://www.npmjs.com/package/jse-eval), which provides secure expression evaluation without full JavaScript access. Always validate and sanitize user input before incorporating it into reactive expressions to prevent cross-site scripting (XSS) and code injection vulnerabilities.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomasbrus%2Fstimulus-reactive-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomasbrus%2Fstimulus-reactive-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomasbrus%2Fstimulus-reactive-ui/lists"}