{"id":13746099,"url":"https://github.com/fn-fx/fn-fx","last_synced_at":"2026-03-17T23:34:46.304Z","repository":{"id":26307743,"uuid":"29755722","full_name":"fn-fx/fn-fx","owner":"fn-fx","description":"A Functional API around JavaFX / OpenJFX.","archived":false,"fork":false,"pushed_at":"2019-08-10T23:26:29.000Z","size":418,"stargazers_count":387,"open_issues_count":32,"forks_count":32,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-10-25T08:02:50.234Z","etag":null,"topics":["clojure","dom","gui","gui-programming","javafx","openjfx"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fn-fx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-23T22:14:23.000Z","updated_at":"2025-08-02T11:03:02.000Z","dependencies_parsed_at":"2022-07-27T08:02:15.732Z","dependency_job_id":null,"html_url":"https://github.com/fn-fx/fn-fx","commit_stats":null,"previous_names":["halgari/fn-fx"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fn-fx/fn-fx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fn-fx%2Ffn-fx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fn-fx%2Ffn-fx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fn-fx%2Ffn-fx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fn-fx%2Ffn-fx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fn-fx","download_url":"https://codeload.github.com/fn-fx/fn-fx/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fn-fx%2Ffn-fx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30635181,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T22:38:22.569Z","status":"ssl_error","status_checked_at":"2026-03-17T22:38:11.804Z","response_time":56,"last_error":"SSL_read: 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":["clojure","dom","gui","gui-programming","javafx","openjfx"],"created_at":"2024-08-03T06:00:46.775Z","updated_at":"2026-03-17T23:34:46.256Z","avatar_url":"https://github.com/fn-fx.png","language":"Clojure","funding_links":[],"categories":["Clojure"],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.com/fn-fx/fn-fx.svg?branch=master)](https://travis-ci.com/fn-fx/fn-fx)\n[![JavaFX build on Clojars](https://img.shields.io/clojars/v/fn-fx/fn-fx-javafx.svg)](https://clojars.org/fn-fx/fn-fx-javafx)\n[![OpenJFX build on Clojars](https://img.shields.io/clojars/v/fn-fx/fn-fx-openjfx11.svg)](https://clojars.org/fn-fx/fn-fx-openjfx11)\n\u003c!-- [![Dependencies Status](https://versions.deps.co/fn-fx/fn-fx/status.svg)](https://versions.deps.co/fn-fx/fn-fx) --\u003e\n\n**Assistance / contributions welcome!**  Please see the [contribution guide](https://github.com/fn-fx/fn-fx/blob/master/.github/CONTRIBUTING.md) for more details.\n\n# fn(fx)\nThis library provides a functional, declarative wrapper around JavaFX / OpenJFX. The goals are to provide a \"Virtual DOM\"\ninterface over the OOP mutability JavaFX / OpenJFX embraces.\n\n# Rationale\nWhile the web has taken over many aspects of GUI programming that normally would have been implemented in JavaFX / OpenJFX, it's\nstill important to recognize that a certain amount of complexity is involved in adopting a web based GUI. Programmers must\nnow write in several other languages, setup web servers, and handle network data transfers, when all that was required\nwas a GUI to some backend process. Sometimes a desktop UI really is the simplest option.\n\nHowever, clojure developers have traditionally shied away from adopting technologies such as Swing and JavaFX / OpenJFX for fear\nof delving into the mess of mutability that is GUI programming.\n\nThis is the niche that fn(fx) attempts to fill: providing a functional interface over JavaFX / OpenJFX.\n\n# Basic Overview\nfn(fx) requires that users express their UI via data, and calls to a function known as \"ui\". This function constructs\na quasi-immutable datastructure that can easily be diffed against other components. We say \"quasi-immutable\", since\nsome of the fields on the structure are mutated, but only once, from nil to a known value, never from a value\nto another value. This tree of components can then be handled by several functions:\n\n* `(fn-fx.fx-dom/render component event-callback)` - This function takes a virtual dom (component tree) and\nrenders it, returning an opaque structure that can be used to later update the UI with a new virtual dom.\n`event-callback` is a function that will be handed events from the UI, more on that later.\n* `(fn-fn.fx-dom/update-dom prev-state new-dom)` - Given a value returned from a previous call to `render`\nor `update-dom` this function will diff `new-dom` against the dom used to create `prev-state` the resulting diff\nwill be used to make minimal changes to the UI where required.\n\n## Event handling\nEvents are data, and are attached to components where `EventHandler` instances would normally be used. Thus creating\na button with the property `:on-action {:event :clicked!}` would result in a button that sent `{:event :clicked!}` to\nthe event-callback handed to the initial call to `render`.\n\n## User components\nThe `defui` macro generates a \"user component\" that is not a UI component, but a rendering function, and an\noptional differ function. The render method on this component is only invoked when the properties to the component\nchange. `defui` is most often used to optimize re-rendering as whole sections of the UI can be ignored during rendering\nand diffing if the properties of the component haven't changed since the last render cycle.\n\n# Usage\n\n## Tested Versions\n\nBefore using `fn-fx`, please make sure you're using a JRE version that has been [tested](https://travis-ci.com/fn-fx/fn-fx)\nand is known to work.  Here's the current test matrix:\n\n|                |  JRE 1.8 (Oracle) | JRE 1.8 (OpenJDK) | JRE 11 (Oracle) | JRE 11 (OpenJDK) |\n|           ---: |   :---:           |  :---:            |  :---:          |  :---:           |\n| Clojure 1.7.0  | ❌\u003csup\u003e1\u003c/sup\u003e    | ❌\u003csup\u003e1,2\u003c/sup\u003e  | ❌\u003csup\u003e1\u003c/sup\u003e | ❌\u003csup\u003e1\u003c/sup\u003e   |\n| Clojure 1.8.0  | ✅                | ❌\u003csup\u003e2\u003c/sup\u003e    | ✅             | ✅               |\n| Clojure 1.9.0  | ✅                | ❌\u003csup\u003e2\u003c/sup\u003e    | ✅             | ✅               |\n| Clojure 1.10.0 | ✅                | ❌\u003csup\u003e2\u003c/sup\u003e    | ✅             | ✅               |\n\n\u003csup\u003e1\u003c/sup\u003e For now we've decided to only test back as far as Clojure 1.8.0.  If anyone needs this tested on older versions of Clojure, PRs are welcome!\n\n\u003csup\u003e2\u003c/sup\u003e Currently, [there is no easy way to obtain OpenJFX for OpenJDK 1.8](https://github.com/fn-fx/fn-fx/issues/71), so it is not supported.  If anyone has ideas on how to easily add this support, please comment on issue #71.\n\n## A Note on JavaFX vs OpenJFX\n\nJavaFX was included in Oracle JRE versions 1.7u6 through 10, but has never been bundled in any version of OpenJDK, nor is\nit bundled in any edition of the JRE (Oracle or OpenJDK) from version 11 onward.  In JRE 11 and up, these capabilities are\ninstead provided by a separate library called [OpenJFX](https://openjfx.io/) that is not part of the default JRE installation.\n\nAlthough we've attempted to hide this compatibility mess from the library user to the maximum extent possible, we were\nforced to provide multiple `fn-fx` artifacts in Clojars:\n1. **`fn-fx-javafx`** - for code that targets JREs that bundle JavaFX (i.e. Oracle JRE versions 1.7u6 through 10)\n2. **`fn-fx-openjfx##`** - for code that targets JREs that do not bundle JavaFX (i.e. OpenJDK and Oracle JRE versions 11 and up).  Note that \"##\" is a specific number (currently only \"11\" is provided, yielding `fn-fx-openjfx11`)\n\nAlthough these artifacts are code-identical, they have different upstream dependencies that are JRE-version-specific, so\nplease make sure you select the correct artifact based on the version of the JRE that will be used **at both development\nand runtime**.\n\nThe regrettably [tight coupling between OpenJFX and JRE versions](http://mail.openjdk.java.net/pipermail/openjfx-discuss/2018-October/000061.html)\nmakes it more challenging for the project to maintain both forward and backward compatibility across JRE editions and versions,\nbut our intent is to maintain the broadest practical compatibility, at least until JRE 11+ is widely deployed.  Whether this continues\nto be done via multiple artifacts, or some other mechanism is an open question.  If you have suggestions / comments / preferences\non this, please [let us know](https://github.com/fn-fx/fn-fx/issues/new?template=Support_question.md)!\n\n## Trying the Library Out\n\n### With `lein-try`\n\nKicking the tyres is a snap with the handy [`lein-try`](https://github.com/avescodes/lein-try) plugin:\n\n```shell\n$ lein try fn-fx/fn-fx-openjfx11 \"0.5.0-SNAPSHOT\"   # If you're running JRE 11 (Oracle or OpenJDK)\n$ lein try fn-fx/fn-fx-javafx \"0.5.0-SNAPSHOT\"      # If you're running on an Oracle JRE, versions 1.7u6 to 10\n```\n\n### With a Placeholder Project\n\nOr, if you'd rather not use `lein-try`, you could create a new folder and put a `project.clj` file in it, something like this:\n\n```clojure\n(defproject your-name/my-first-fn-fx-project \"0.1.0-SNAPSHOT\"\n  :description      \"My first fn(fx) project\"\n  :min-lein-version \"2.8.1\"\n  :dependencies     [[org.clojure/clojure \"1.9.0\"]\n                     ; Pick one, and only one, of the following dependencies:\n                     [fn-fx/fn-fx-openjfx11 \"0.5.0-SNAPSHOT\"]    ; If you're running JRE 11 (Oracle or OpenJDK)\n                     [fn-fx/fn-fx-javafx \"0.5.0-SNAPSHOT\"]       ; If you're running on an Oracle JRE, versions 1.7u6 to 10\n])\n```\n\nThen start a REPL from that directory:\n\n```shell\n$ lein repl\nnREPL server started on port 55554 on host 127.0.0.1 - nrepl://127.0.0.1:55554\nREPL-y 0.3.7, nREPL 0.2.12\nClojure 1.9.0\nOpenJDK 64-Bit Server VM 11.0.1+13\n    Docs: (doc function-name-here)\n          (find-doc \"part-of-name-here\")\n  Source: (source function-name-here)\n Javadoc: (javadoc java-object-or-class-here)\n    Exit: Control+D or (exit) or (quit)\n Results: Stored in vars *1, *2, *3, an exception in *e\n\nuser=\u003e \n```\n\n### Using the Clojure Command Line Tools\n\nNot yet implemented, though this is being tracked as [issue #49](https://github.com/fn-fx/fn-fx/issues/49).  PRs welcome!\n\n## Making API Calls from the REPL\n\nOnce you're in a REPL you can make standard `fn-fx` API calls:\n\n```clojure\nuser=\u003e (require '[fn-fx.fx-dom :as dom])\nnil\nuser=\u003e (require '[fn-fx.controls :as ui])\nnil\nuser=\u003e ; Do awesome GUI stuff here!\n```\n\n[API documentation is published here](https://fn-fx.github.io/fn-fx/), but note that these are currently *\\*cough\\**\n\"limited\" due to a lack of docstrings in the code.  Issue #s [21](https://github.com/fn-fx/fn-fx/issues/21),\n[26](https://github.com/fn-fx/fn-fx/issues/26), [27](https://github.com/fn-fx/fn-fx/issues/27), and\n[28](https://github.com/fn-fx/fn-fx/issues/28) go into more detail on this, and any additional comments / feedback / PRs\nfor documentation are welcome!\n\n## Running the Examples\n\nNote that [the examples](https://github.com/fn-fx/fn-fx/tree/master/examples) are not deployed to Clojars, so to run\nthose you'll need to clone the project locally, and run a REPL from within the cloned directory:\n\n```\n$ cd \u003cdirectory where you like to put your GitHub clones\u003e\n$ git clone https://github.com/fn-fx/fn-fx.git\nCloning into 'fn-fx'...\nremote: Enumerating objects: 47, done.\nremote: Counting objects: 100% (47/47), done.\nremote: Compressing objects: 100% (27/27), done.\nremote: Total 550 (delta 23), reused 39 (delta 20), pack-reused 503\nReceiving objects: 100% (550/550), 301.67 KiB | 4.02 MiB/s, done.\nResolving deltas: 100% (264/264), done.\n$ cd fn-fx\n$ lein repl\nnREPL server started on port 52005 on host 127.0.0.1 - nrepl://127.0.0.1:52005\nREPL-y 0.3.7, nREPL 0.2.12\nClojure 1.9.0\nOpenJDK 64-Bit Server VM 11.0.1+13\n    Docs: (doc function-name-here)\n          (find-doc \"part-of-name-here\")\n  Source: (source function-name-here)\n Javadoc: (javadoc java-object-or-class-here)\n    Exit: Control+D or (exit) or (quit)\n Results: Stored in vars *1, *2, *3, an exception in *e\n\nuser=\u003e\n```\n\nOnce you have a REPL up within the cloned directory, the examples may be run as follows:\n\n* **[01 Hello world](https://github.com/fn-fx/fn-fx/blob/master/examples/getting_started/01_hello_word.clj)**: `(require '[getting-started.01-hello-word :as hello]) (hello/-main)`\n* **[02 Form](https://github.com/fn-fx/fn-fx/blob/master/examples/getting_started/02_form.clj)**: `(require '[getting-started.02-form :as form]) (form/-main)`\n* **[Shapes 3D](https://github.com/fn-fx/fn-fx/blob/master/examples/other_examples/shapes_3d.clj)**: `(require '[other-examples.shapes-3d :as shapes-3d]) (shapes-3d/-main)`\n* **[Todo](https://github.com/fn-fx/fn-fx/blob/master/examples/other_examples/todo.clj)**: `(require '[other-examples.todo :as todo]) (todo/-main)`\n* **[Menu Bar](https://github.com/fn-fx/fn-fx/blob/master/examples/other_examples/menubar.clj)**: `(require '[other-examples.menubar :as menu]) (menu/-main)`\n* **[WebView](https://github.com/fn-fx/fn-fx/blob/master/examples/other_examples/webview.clj)**: `(require '[other-examples.webview :as webview]) (webview/-main)`\n\nEach example will open a window that can be closed at any time (and reopened by calling `-main` again).\n\n# License\nCopyright (c) 2016 Timothy Baldridge. All rights reserved.\nThe use and distribution terms for this software are covered by the\nEclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)\nwhich can be found in the file epl-v10.html at the root of this distribution.\nBy using this software in any fashion, you are agreeing to be bound by\nthe terms of this license.\nYou must not remove this notice, or any other, from this software.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffn-fx%2Ffn-fx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffn-fx%2Ffn-fx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffn-fx%2Ffn-fx/lists"}