{"id":30898157,"url":"https://github.com/palexdev/hotswapfx","last_synced_at":"2025-09-09T01:06:55.328Z","repository":{"id":307354390,"uuid":"1029245023","full_name":"palexdev/HotSwapFX","owner":"palexdev","description":null,"archived":false,"fork":false,"pushed_at":"2025-07-31T08:36:28.000Z","size":16574,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-27T09:41:31.813Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/palexdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-30T18:33:24.000Z","updated_at":"2025-08-01T09:49:53.000Z","dependencies_parsed_at":"2025-07-30T21:35:06.472Z","dependency_job_id":"ba388d8c-30a1-4741-9cf6-09d307c11907","html_url":"https://github.com/palexdev/HotSwapFX","commit_stats":null,"previous_names":["palexdev/hotswapfx"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/palexdev/HotSwapFX","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palexdev%2FHotSwapFX","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palexdev%2FHotSwapFX/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palexdev%2FHotSwapFX/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palexdev%2FHotSwapFX/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/palexdev","download_url":"https://codeload.github.com/palexdev/HotSwapFX/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palexdev%2FHotSwapFX/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274231918,"owners_count":25245855,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2025-09-09T01:06:53.107Z","updated_at":"2025-09-09T01:06:55.308Z","avatar_url":"https://github.com/palexdev.png","language":"Java","readme":"\u003cdiv align=\"left\" style=\"position: relative;\"\u003e\n\u003cimg src=\"assets/logo.svg\" align=\"right\" width=\"30%\" style=\"margin: -20px 0 0 20px; background: white; border-radius: 12px;\"\u003e\n\u003ch1\u003eHotSwapFX\u003c/h1\u003e\n\u003cp align=\"left\"\u003e\n\t\u003cem\u003e\u003ci\u003eFaster JavaFX development with HotSwap\u003c/i\u003e\u003c/em\u003e\n\u003c/p\u003e\n\u003cp align=\"left\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/license/palexdev/HotSwapFX?style=default\u0026logo=opensourceinitiative\u0026logoColor=white\u0026color=0080ff\" alt=\"license\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/last-commit/palexdev/HotSwapFX?style=default\u0026logo=git\u0026logoColor=white\u0026color=0080ff\" alt=\"last-commit\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/languages/top/palexdev/HotSwapFX?style=default\u0026color=0080ff\" alt=\"repo-top-language\"\u003e\n    \u003cimg src=\"https://img.shields.io/maven-central/v/io.github.palexdev/hotswapfx\" alt=\"maven-central-version\"\u003e\n\u003c/p\u003e\n\u003cp align=\"left\"\u003e\u003c!-- default option, no dependency badges. --\u003e\n\u003c/p\u003e\n\u003cp align=\"left\"\u003e\n\t\u003c!-- default option, no dependency badges. --\u003e\n\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cbr clear=\"right\"\u003e\n\n## 🔗 Table of Contents\n\n- [🙌 Showcase](#-showcase)\n- [📍 Overview, Features \u0026 Limitations](#-overview-features--limitations)\n- [🚀 Getting Started](#-getting-started)\n    - [⚙️ Dependencies](#-dependencies)\n    - [🔨 Building \u0026 Running](#-building--running)\n    - [⭐ Usage](#-usage)\n- [📌 Project Roadmap](#-project-roadmap)\n- [🔰 Contributing](#-contributing)\n- 📢 [Acknowledgments](#-acknowledgments)\n\n---\n\n## 🙌 Showcase\n\n![showcase](assets/showcase.gif)\n\n## 📍 Overview, Features \u0026 Limitations\n\n`HotSwapFX` is a small Java library aimed at assisting you during the development of your JavaFX apps. It allows to modify your views and reload them without the need to recompile and restart the whole app. It's intended to be used in a development environment, not in production, as the at its core it just watches the classpath and listens for events on the files on it, triggering the reload under certain conditions.\n\nThe service is designed to be as flexible as possible. Views are registered on the service by a unique id. When a class file changes, all the views of that class are reloaded. So, by default, the service works only on `.class` files. However, there's a pretty cool hook mechanism that allows you to either add:\n\n- `Early hooks`: these are notified as soon as something changes on the classpath, it could be anything. This is very useful if, for example, you want to reload a view when an asset or a stylesheet changes. It's a bit like [CSSFX](https://github.com/McFoggy/cssfx) but on steroids (although a little slower and more tedious as you can see from the showcase video).\n- `Late hooks`: these are notifies only when `.class` files change, and only if the class is a JavaFX Node. This can be useful if you want to reload a view when its children change. This example is also already implemented by the library, see [ChildrenTracker](https://github.com/palexdev/HotSwapFX/blob/main/src/main/java/io/github/palexdev/hotswapfx/ChildrenTracker.java).\n\nAbout the components, there's plenty of config options for them too:\n\n- You can specify how to instantiate them (by default uses reflection and the no-args constructor)\n- You can specify the logic to copy the state from the old Node to the new one\n- You can specify how to replace the old view in the scenegraph (there's a default here too, check [HotSwappable](https://github.com/palexdev/HotSwapFX/blob/main/src/main/java/io/github/palexdev/hotswapfx/HotSwappable.java))\n\n\u003cbr \u003e\n\nUnfortunately, it's not all sunshine and roses, there are some annoying limitations and quirks:\n\n1) I noticed that for some reason, sometimes the view would be desynchronized with the class changes. For example, if you changed a label's color to `red`, you would not see the change on reload. Then if you changed it to `blue`, you would see it `red`. I could not figure out the actual culprit of this, but I found out that delaying the reload by a slow amount of time (200ms by default, can be configured) made the mechanism much more stable and reliable.\n2) This is more an implementation detail than anything else, but still a bit annoying. Java ClassLoaders cache the classes they load. This is no good for this project, because when a `.class` file changes, and we reload it to instantiate the new component, it would still load the old class. The solution is as easy as using a new ClassLoader every time a class has to be loaded, reading the bytes from the file and redefining the class. The quirk here, is that comparison via `=` or `.equals()` between classes does not work anymore. Even if the classes have the same fully qualified name, they are still different to Java. So, to solve this, the library wraps the classes in a record which overrides the comparison to check the fully qualified name.\n3) In general limitations due to the JVM design, see [HotSwap](https://www.jetbrains.com/help/fleet/hotswap-java.html). The article mentions the [DCEVM](https://github.com/dcevm/dcevm) (you can find newer versions [here](https://github.com/JetBrains/JetBrainsRuntime)) project which should expand the Java's HotSwap capabilities but I did not test it (also because this projects requires Java 24 and there's no such release yet, probably once Java 25 is out).\n\n---\n\n## 🚀 Getting Started\n\n### ⚙️ Dependencies\n\n`HotSwapFX` is a small library, but still uses some external libraries, mostly yo make the implementation easier:\n\n1) [Directory Watcher](https://github.com/palexdev/directory-watcher): used to watch file changes on the class path. It's a nice wrapper for the unconvenient Java `WatchService` API. A personal fork of the amazing work done by [(Greg Methvin)](https://github.com/gmethvin).\n2) [Joor](https://github.com/jOOQ/jOOR): a nice fluent API for Java reflection. `HotSwapFX` by default uses reflection to instantiate the new views.\n3) [TinyLog](https://tinylog.org/): I love it, it's the ultimate logging library for me. Easy to use and configure, how any printing library should be.\n\n### 🔨 Building \u0026 Running\n\n**Using `gradle`** \u0026nbsp; [\u003cimg align=\"center\" src=\"https://img.shields.io/badge/Gradle-02303A.svg?style={badge_style}\u0026logo=gradle\u0026logoColor=white\" /\u003e](https://gradle.org/)\nTo build the project:\n\n```sh\n❯ gradle build\n```\n\nTo run the showcase app (remember to run in debug mode):\n\n```\n❯ gradle showcase\n```\n\n### ⭐ Usage\n\n**Using `gradle`** \u0026nbsp; [\u003cimg align=\"center\" src=\"https://img.shields.io/badge/Gradle-02303A.svg?style={badge_style}\u0026logo=gradle\u0026logoColor=white\" /\u003e](https://gradle.org/)\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation 'io.github.palexdev:hotswapfx:\u003cversion\u003e'\n}\n```\n\n**Using `maven`** \u0026nbsp; [\u003cimg align=\"center\" src=\"https://img.shields.io/badge/Maven-C71A36.svg?style={badge_style}\u0026logo=apachemaven\u0026logoColor=white\" /\u003e](https://maven.apache.org/)\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.palexdev\u003c/groupId\u003e\n    \u003cartifactId\u003ehotswapfx\u003c/artifactId\u003e\n    \u003cversion\u003exx.x.x\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n\n\n\n---\n## 📌 Project Roadmap\n\nFor now, I don't have many ideas. I expect some bug fixes or minor refactors/convenience additions. I'm open to suggestions! 🙌\n\n---\n\n## 🔰 Contributing\n\n- **💬 [Join the Discussions](https://github.com/palexdev/HotSwapFX/discussions)**: Share your insights, provide feedback, or ask questions.\n- **🐛 [Report Issues](https://github.com/palexdev/HotSwapFX/issues)**: Submit bugs found or log feature requests for the `HotSwapFX` project.\n- **💡 [Submit Pull Requests](https://github.com/palexdev/HotSwapFX/blob/main/CONTRIBUTING.md)**: Review open PRs, and submit your own PRs.\n\n\u003cdetails closed\u003e\n\u003csummary\u003eContributing Guidelines\u003c/summary\u003e\n\n1. **Fork the Repository**: Start by forking the project repository to your github account.\n2. **Clone Locally**: Clone the forked repository to your local machine using a git client.\n   \n   ```sh\n   git clone https://github.com/palexdev/HotSwapFX\n   ```\n3. **Create a New Branch**: Always work on a new branch, giving it a descriptive name.\n   ```sh\n   git checkout -b new-feature-x\n   ```\n4. **Make Your Changes**: Develop and test your changes locally.\n5. **Commit Your Changes**: Commit with a clear message describing your updates.\n   ```sh\n   git commit -m 'Implemented new feature x.'\n   ```\n6. **Push to github**: Push the changes to your forked repository.\n   ```sh\n   git push origin new-feature-x\n   ```\n7. **Submit a Pull Request**: Create a PR against the original project repository. Clearly describe the changes and their motivations.\n8. **Review**: Once your PR is reviewed and approved, it will be merged into the main branch. Congratulations on your contribution!\n\u003c/details\u003e\n\n\u003cdetails closed\u003e\n\u003csummary\u003eContributor Graph\u003c/summary\u003e\n\u003cbr\u003e\n\u003cp align=\"left\"\u003e\n   \u003ca href=\"https://github.com{/palexdev/HotSwapFX/}graphs/contributors\"\u003e\n      \u003cimg src=\"https://contrib.rocks/image?repo=palexdev/HotSwapFX\"\u003e\n   \u003c/a\u003e\n\u003c/p\u003e\n\u003c/details\u003e\n---\n\n## **📢 Acknowledgments**\n\nShoutout to [Mauro de Wit](https://github.com/mfdewit) for showcasing this [hot reload](https://github.com/mfdewit/javafx-hot-reload) concept for JavaFX. It got me really curious about it and wanted to explore the idea further.\n\n---\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalexdev%2Fhotswapfx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpalexdev%2Fhotswapfx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalexdev%2Fhotswapfx/lists"}