{"id":15413585,"url":"https://github.com/kidkarolis/moonwave","last_synced_at":"2026-04-06T03:35:41.040Z","repository":{"id":137837597,"uuid":"112076047","full_name":"KidkArolis/moonwave","owner":"KidkArolis","description":"🌗 A small web application framework.","archived":false,"fork":false,"pushed_at":"2018-03-05T13:33:11.000Z","size":69,"stargazers_count":14,"open_issues_count":6,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-11T09:42:15.837Z","etag":null,"topics":["framework","javascript","preact","react","web"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/KidkArolis.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2017-11-26T11:12:30.000Z","updated_at":"2019-01-30T03:58:14.000Z","dependencies_parsed_at":"2023-04-17T05:35:33.639Z","dependency_job_id":null,"html_url":"https://github.com/KidkArolis/moonwave","commit_stats":{"total_commits":18,"total_committers":1,"mean_commits":18.0,"dds":0.0,"last_synced_commit":"358b5ba81a772703fec5a20a5a0931591ff060fe"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/KidkArolis/moonwave","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KidkArolis%2Fmoonwave","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KidkArolis%2Fmoonwave/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KidkArolis%2Fmoonwave/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KidkArolis%2Fmoonwave/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KidkArolis","download_url":"https://codeload.github.com/KidkArolis/moonwave/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KidkArolis%2Fmoonwave/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31458838,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"online","status_checked_at":"2026-04-06T02:00:07.287Z","response_time":112,"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":["framework","javascript","preact","react","web"],"created_at":"2024-10-01T16:58:06.158Z","updated_at":"2026-04-06T03:35:41.013Z","avatar_url":"https://github.com/KidkArolis.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🌗 Moonwave\n\nMoonwave is a small framework for building web applications with JavaScript.\n\nMoonwave is 100 lines of code, because it combines an existing set of composable libraries.\n\n* [react](https://reactjs.org/) / [preact](https://preactjs.com/) / custom - component rendering.\n* [tiny-atom](https://qubitproducts.github.io/tiny-atom) - state management.\n* [space-router](https://github.com/KidkArolis/space-router) - routing.\n\nEach of these libraries is independent and can be useful on their own. Moonwave is just one, simple way to combine the three for common cases.\n\nIf Moonwave doesn't quite fit your needs as your application grows, you can combine the three libraries, or others, in your own way and use Moonwave as a reference implementation.\n\n## Usage\n\n    yarn add moonwave\n\n## Example\n\n```js\nconst Preact = require('preact')\nconst { moonwave } = require('moonwave/preact')\n\n// each route maps to a Preact component\nconst routes = [\n  ['/', MainView]\n]\n\n// actions is how we update application state\nconst actions = {\n  update: (get, split, title) =\u003e {\n    split({ title })\n  }\n}\n\n// evolve is a flexible hook for implementing custom\n// action strategies, such as modularising or namespacing\n// this implementation calls the actions we declared above\nconst evolve = (get, split, action) =\u003e {\n  actions[action.type](get, split, action.payload)\n}\n\n// the preact component receives full application state\n// and a `split` function for dispatching actions or updates\n// use ConnectAtom component to map state/split to props\nfunction MainView ({ state, split }) {\n  return (\n    \u003cdiv\u003e\n      \u003ch1\u003eTitle: {state.title}\u003c/h1\u003e\n      \u003cinput type='text' value={state.title} onInput={update} /\u003e\n    \u003c/div\u003e\n  )\n\n  function update (e) {\n    split('update', e.target.value)\n  }\n}\n\n// assemble all the pieces\nmoonwave()\n  .state({ title: 'Default title' })\n  .evolve(evolve)\n  .routes(routes)\n  .mount(document.body)\n```\n\nThis example application uses `preact` and will compile to around `6KB`. The individual components are around:\n\n```\npreact        3.5KB\ntiny-atom     0.5KB\nspace-router  1.5KB\nexample-app   0.5KB\n-------------------\n              6.0KB\n```\n\n### API\n\n#### const app = moonwave(options)\n\nCreate an app. Available options are:\n\n* `store.merge` - custom state merge strategy, default implementation is `(state, update) =\u003e Object.assign({}, state, update)`.\n* `store.debug` - a debug hook. Set to `debug: require('moonwave/log')` for console logger or `debug: require('moonwave/devtools')` for integration with Redux dev tools.\n* `router.mode` - one of `history`, `hash`, `memory`. Default is `history`.\n* `router.interceptLinks` - whether clicks on links are automatically handled by the router. Default is `true`.\n* `router.qs` - custom query string parser. Object of shape { parse, stringify }.\n\nSee [tiny-atom docs](https://github.com/QubitProducts/tiny-atom) for more information on the store. See [space-router docs](https://github.com/KidkArolis/space-router) for more information on the router.\n\n#### app.state()\n\nProvide initial state.\n\n```js\napp.state({})\napp.state({ count: 0 })\napp.state(Immutable.Map({})) // note: in this case, you'll need to provide a custom store.merge function\n```\n\n#### app.evolve()\n\nProvide a function of signature `(get, split, action)` that will receive all actions that were `split` by the app.\n\n**Note**: think of `split` as `dispatch` if you're familiar with that.\n\n```js\napp.evolve((get, split, action) =\u003e {\n  actions[action.type](get, split, action.payload)\n})\n\napp.evolve(async (get, split, action) =\u003e {\n  switch (action.type) {\n    case 'increment':\n      split({ count: get().count + 1 })\n      break\n    case 'decrement':\n      split({ count: get().count - 1 })\n      break\n    case 'fetch':\n      split({ loading: true })\n      const res = await axios.get('/data')\n      split({ items: res.data, loading: false })\n      break;\n  }\n})\n```\n\n#### app.actions()\n\nProvide a map of actions. Can be used instead of the evolve for common cases.\n\n```js\napp.actions({\n  increment: (get, split, x) =\u003e {\n    split({ count: get().count + x })\n  },\n  decrement: (get, split, x) =\u003e {\n    split({ count: get().count - x })\n  }\n})\n```\n\nIf both `app.actions()` and `app.evolve()` is used, the `actions` provided is passed as the 4th argument to the evolve function.\n\n#### app.routes()\n\nProvide an array of routes. An optional second argument can be used for a custom [`onTransition`](https://github.com/KidkArolis/space-router#startontransition) implementation.\n\n```js\n// if you don't need routing\napp.routes([\n  ['*', SinglePage]\n])\n\n// common case\napp.routes([\n  ['/', Home],\n  ['/Space', Space],\n  ['/Ocean', Ocean]\n])\n\n// nested routes\napp.routes([\n  ['', Shell, [\n    ['/inbox', Inbox],\n    ['/account', Account, [\n      ['password', Password]\n    ]],\n    ['*', NotFound]\n  ]]\n])\n\n// async route loading\napp.routes([\n  ['/', { load: () =\u003e System.import('./pages/Index') }],\n  ['/Space', { load: () =\u003e System.import('./pages/Space') }]\n], async function onTransition (route, data) {\n  await Promise.all(data.map(async d =\u003e {\n    if (!d.Component) d.Component = await d.load()\n  }))\n  atom.split({ route })\n})\n```\n\nTo navigate around your application programmatically, you `split` an action like so:\n\n```js\nfunction MyApp ({ split }) {\n  return \u003cform onSubmit={onSubmit}\u003e...\u003c/form\u003e\n\n  function onSubmit () {\n    const id = 1\n    split('navigate', {\n      // path\n      path: `/space/${id}`,\n      // query params\n      query: { angle: 0 },\n      // push vs replace the url\n      replace: false\n    })\n  }\n}\n```\n\n#### app.mount()\n\nMount the app to a DOM element. Initialise the store, router and render the app into DOM. Defaults to `document.body`\n\n```js\napp.mount(document.getElementById('root'))\n```\n\n#### app.unmount()\n\nStop the router, unrender the app from DOM.\n\n```js\napp.unmount()\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkidkarolis%2Fmoonwave","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkidkarolis%2Fmoonwave","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkidkarolis%2Fmoonwave/lists"}