{"id":16400104,"url":"https://github.com/jayrbolton/functional-frontend-architecture","last_synced_at":"2026-02-28T07:04:02.048Z","repository":{"id":145317209,"uuid":"50222980","full_name":"jayrbolton/functional-frontend-architecture","owner":"jayrbolton","description":"A functional architecture for building UI with FRP (flyd) and Virtual Dom (snabbdom)","archived":false,"fork":false,"pushed_at":"2016-01-26T06:20:40.000Z","size":3386,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-04T23:43:00.662Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"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/jayrbolton.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}},"created_at":"2016-01-23T04:14:16.000Z","updated_at":"2016-03-30T03:24:43.000Z","dependencies_parsed_at":"2023-04-20T16:07:46.770Z","dependency_job_id":null,"html_url":"https://github.com/jayrbolton/functional-frontend-architecture","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayrbolton%2Ffunctional-frontend-architecture","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayrbolton%2Ffunctional-frontend-architecture/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayrbolton%2Ffunctional-frontend-architecture/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayrbolton%2Ffunctional-frontend-architecture/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jayrbolton","download_url":"https://codeload.github.com/jayrbolton/functional-frontend-architecture/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240306769,"owners_count":19780682,"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","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":"2024-10-11T05:26:45.053Z","updated_at":"2026-02-28T07:03:57.022Z","avatar_url":"https://github.com/jayrbolton.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Functional Frontend Architecture\n\nThis is an example of a simple but robust functional frontend architecture, based on the Elm Architecture but further simplified, using only two libs:\n\n- [flyd](https://github.com/paldepind/flyd) for Functional Reactive Programming\n- [snabbdom](https://github.com/paldepind/snabbdom) for Virtual DOM\n\nMore optional bonuses:\n\n- with plain js data structures, make use of [Ramda](ramdajs.com)\n- To have immutable data and vdom-thunking, use [Immutable.js](https://facebook.github.io/immutable-js/docs/)\n\n# High-Level Overview\n\nMaking a UI module:\n\n- The app consists of independent UI modules\n- UI modules can be nested and combined. They are hierarchical, similar to the DOM.\n- Each module exports 2 basic components\n  - View: a main 'view' function that takes a state and renders a Virtual DOM Tree\n  - Init function: takes some configuration data and returns a state stream\n\nTesting your UI:\n\n- Given your module's state stream, view function, and event streams\n  - Easily unit-test your pure view functions by passing in mocked-up state and querying the resulting HTML\n  - Easily unit-test your pure update functions by passing in a mockup state and data and checking the resulting state\n  - Easily integration-test your full UI by pushing test data onto your module's event streams and querying the resulting DOM stream\n\n## Some advantages of this architecture\n\n- Much easier to quickly understand in my opinion than many other similar architectures\n- Only two libs, and you can very easily use immutable/mori/etc and make use of vdom 'thunking'\n- Very easy to test\n- Mostly purely functional\n\n# Examples\n\n## Counter\n\nA single-module, very simple example to get you started with the idea. The user clicks one of three buttons and watches the number change in the view.\n\n[View the source](examples/counter/index.es6)\n\n# init function\n\nUI modules export an init function, which is a kind of constructor that can\ntake configuration data. It is also responsible for initializind the default\nstate, the various event streams, and returning the state stream.\n\n# scanMerge-ing events together\n\nUse `flyd/module/scanMerge` to combine events from your view into a single state stream.\n\nThe streams on the left can be any flyd stream, and the functions on the right are state-updater functions and take the current state and a value from the stream, and return a new state.\n\n```js\nlet state$ = flyd.immediate(flyd_scanMerge([\n  [addTodo$,      (state, formObj) =\u003e state.set('todos', state.get('todos').add(formObj))]\n, [removeTodo$,   (state, idx)     =\u003e state.set('todos', state.get('todos').delete(idx))]\n, [toggleTodo$,   (state, idx)     =\u003e state.setIn(['todos', idx, 'finished'], !state.getIn(['todos', idx, 'finished']))]\n, [editTodo$,     (state, data)    =\u003e state.setIn(['todos', data.idx, 'name'], data.name)]\n], defaultState))\n```\n\n`flyd.immediate` is good to use so that your `defaultState` is pushed to your\nstate stream immediately and rendered on pageload, rather than waiting around\nfor an event to occur.\n\n# How to nest modules\n\nIf you have a parent module and want to embed child modules, you can use flyd/module/lift:\n\n```js\n// Embed any number of child module's state streams into the state stream for a parent module:\n// (assume $parentState is already initialized)\nlet parentState$ = flyd.lift(\n  (s1, s2, s3, parent) =\u003e parent.set('child1', s1).set('child2', s2).set('child3', s3)\n, parentState$\n, childModuleState1$\n, childModuleState2$\n, childModuleState3$\n)\n```\n\n# Render a module onto the page\n\nTo render your module to the page, use the snabbdom patch function in combination with flyd.\n\n```\nimport snabbdom from 'snabbdom'\nlet patch = snabbdom.init([\n  require('snabbdom/modules/class')\n, require('snabbdom/modules/props')\n, require('snabbdom/modules/style')\n, require('snabbdom/modules/eventlisteners')\n])\n\nlet container = document.querySelector('#container')\nlet vnode$ = flyd.map(view, state$)\nlet dom$ = flyd.scan(patch, container, vnode$)\n\n\n// inline:\n// flyd.scan(patch, document.querySelector('#container'), flyd.map(view, state$))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjayrbolton%2Ffunctional-frontend-architecture","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjayrbolton%2Ffunctional-frontend-architecture","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjayrbolton%2Ffunctional-frontend-architecture/lists"}