{"id":13726315,"url":"https://github.com/utkarshkukreti/reaml","last_synced_at":"2025-04-14T19:44:06.527Z","repository":{"id":36854456,"uuid":"201886570","full_name":"utkarshkukreti/reaml","owner":"utkarshkukreti","description":"A React binding for (OCaml | ReasonML) + BuckleScript with compile time enforcement of the \"Rules of Hooks\". Live Examples: https://reaml.netlify.com","archived":false,"fork":false,"pushed_at":"2023-01-06T02:17:46.000Z","size":3860,"stargazers_count":98,"open_issues_count":20,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-28T08:04:48.978Z","etag":null,"topics":["bucklescript","ocaml","preact","react","reasonml"],"latest_commit_sha":null,"homepage":"","language":"OCaml","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/utkarshkukreti.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":"2019-08-12T08:12:12.000Z","updated_at":"2024-11-05T00:06:19.000Z","dependencies_parsed_at":"2023-01-17T06:00:37.986Z","dependency_job_id":null,"html_url":"https://github.com/utkarshkukreti/reaml","commit_stats":null,"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utkarshkukreti%2Freaml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utkarshkukreti%2Freaml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utkarshkukreti%2Freaml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utkarshkukreti%2Freaml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/utkarshkukreti","download_url":"https://codeload.github.com/utkarshkukreti/reaml/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248949822,"owners_count":21188157,"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":["bucklescript","ocaml","preact","react","reasonml"],"created_at":"2024-08-03T01:02:59.032Z","updated_at":"2025-04-14T19:44:06.501Z","avatar_url":"https://github.com/utkarshkukreti.png","language":"OCaml","funding_links":[],"categories":["OCaml"],"sub_categories":[],"readme":"# Reaml (React + ML) ![Build](https://github.com/utkarshkukreti/reaml/workflows/Build/badge.svg) ![Test](https://github.com/utkarshkukreti/reaml/workflows/Test/badge.svg)\n\n\u003e A React binding for (OCaml | ReasonML) + BuckleScript with compile time\n\u003e enforcement of the [\"Rules of Hooks\"](https://reactjs.org/docs/hooks-rules.html)\n\u003e ([How?](#how-are-the-rules-of-hooks-enforced-at-compile-time)).\n\n\u003e [Live Examples with Code](https://reaml.netlify.com) |\n\u003e [Starter](https://github.com/utkarshkukreti/reaml-starter) |\n\u003e [Opinionated Starter](https://github.com/utkarshkukreti/reaml-opinionated-starter)\n\n## Overview\n\nThe \"Hello, World!\" of Reaml looks like this:\n\n```ocaml\nmodule R = Reaml\n\nlet main = R.h1 [ R.id \"hello\" ] [ R.string \"Hello, world!\" ]\n\nlet () = main |\u003e R.renderTo \"main\"\n```\n\nThe code above renders `\u003ch1 id=\"hello\"\u003eHello, World!\u003c/h1\u003e` into the first\nelement on the page matching the selector `main`.\n\nNote: If you want to use ReasonML syntax instead of OCaml, check out the\n[last section](#reasonml) for the equivalent ReasonML code.\n\n---\n\nComponents are defined using a syntax extension\n`[@reaml.component \"DisplayNameOfComponent\"]` applied to `let`s of one\nargument functions:\n\n```ocaml\nmodule R = Reaml\n\nmodule Counter = struct\n  type props = { initial : int }\n\n  let[@reaml.component \"Counter\"] make { initial } =\n    R.div [] [R.int initial]\nend\n```\n\nComponents are initialized using simple function calls:\n\n```ocaml\nlet main = Counter.make { initial = 0 }\n```\n\nHooks are invoked using a `[@reaml]` annotation on `let` expressions:\n\n```ocaml\nlet[@reaml.component \"Counter\"] make { initial } =\n  let[@reaml] count, setCount = R.useState initial in\n  R.div [] [R.int initial]\n```\n\nHere's a full example of a Counter with two buttons, one to increment and one to\ndecrement the value ([full source](examples/Counter.ml)):\n\n```ocaml\nmodule Counter = struct\n  type props = { initial : int }\n\n  let[@reaml.component \"Counter\"] make { initial }=\n    let[@reaml] count, setCount = R.useState initial in\n    R.div\n      []\n      [ R.button [ R.onClick (fun _ -\u003e setCount (count - 1)) ] [ R.string \"-\" ]\n      ; R.string \" \"\n      ; R.int count\n      ; R.string \" \"\n      ; R.button [ R.onClick (fun _ -\u003e setCount (count + 1)) ] [ R.string \"+\" ]\n      ]\nend\n```\n\nCustom hooks are created using a syntax extension `[@reaml.hook]` applied to\n`fun`s of one argument. Here's a custom hook that wraps `useReducer`, invoking\nany action dispatched twice instead of once.\n\n```ocaml\nlet[@reaml.hook] useDoubleReducer (reducer, initialValue) =\n  let[@reaml] state, dispatch = R.useReducer reducer initialValue in\n  let dispatchTwice action =\n    dispatch action;\n    dispatch action\n  in\n  state, dispatchTwice\n```\n\nThese custom hooks are called in the same manner as the built-in hooks -- using\n`let[@reaml]`:\n\n```ocaml\nlet[@reaml.component \"CustomHooks\"] make () =\n  let reducer state action = state + action in\n  let[@reaml] state, dispatch = useDoubleReducer (reducer, 0) in\n  R.button [ R.onClick (fun _ -\u003e dispatch 2) ] [ R.int state ]\n```\n\nFull example [here](examples/CustomHooks.ml).\n\n## Quick Start\n\n    $ git clone https://github.com/utkarshkukreti/reaml-starter\n    $ cd reaml-starter\n    $ yarn install\n    $ yarn build\n\nThis will build `/src/Main.ml` into the `/dist/` directory which you can run by\nopening `/index.html` in your browser.\n\nFeel free to copy code from [examples](/examples) into `src/Main.ml` and\nrecompile.\n\n## How are the Rules of Hooks enforced at compile time?\n\nReaml uses an OCaml syntax extension to enforce them.\n\nThis requires annotating components with `[@reaml.component]`, custom hooks\nwith `[@reaml.hook]`, and every use of a hook with `let[@reaml]`.\n\nFor `[@reaml.hook]`, the syntax extension appends a dummy argument to the\nfunction, the value of which must be of type `Reaml.undefined` (represented as\nplain `undefined` in JS).\n\nFor both `[@reaml.hook]` and `[@reaml.component]`, the syntax extension\ntraverses the top level `let` expressions and rewrites `let[@reaml]`\nexpressions, appending the `undefined` value to the function call on the right.\n\nIf you call a hook without `let[@reaml]`, you will get a type check error due to\na missing argument.\n\nAfter all this is done, the syntax extension traverses the whole program and\nchecks whether any of these annotations were not processed and throws an error\nif it finds any because it means the annotation was incorrectly used.\n\nFor more examples, check out the files under [/examples](examples).\nA live demo of all the examples is available\n[here](https://reaml.netlify.com).\n\n## ReasonML\n\nHere's the Hello World example in ReasonML:\n\n```reason\nmodule R = Reaml;\n\nlet main = R.h1([R.id(\"hello\")], [R.string(\"Hello, world!\")]);\n\nmain |\u003e R.renderTo(\"main\");\n```\n\nHere's an example of ReasonML code which uses all the three annotations that\nthis library uses:\n\n```reason\nmodule R = Reaml;\n\n[@reaml.hook]\nlet useDoubleReducer = ((reducer, initialValue)) =\u003e {\n  let[@reaml] (state, dispatch) = R.useReducer(reducer, initialValue);\n  let dispatchTwice = action =\u003e {\n    dispatch(action);\n    dispatch(action);\n  };\n\n  (state, dispatchTwice);\n};\n\n[@reaml.component \"CustomHooks\"]\nlet make = () =\u003e {\n  let reducer = (state, action) =\u003e state + action;\n  let[@reaml] (state, dispatch) = useDoubleReducer((reducer, 0));\n  R.button([R.onClick(_ =\u003e dispatch(1))], [R.int(state)]);\n};\n\nlet main = make();\nmain |\u003e R.renderTo(\"main\");\n```\n\nFor more guidance on how to translate OCaml code into ReasonML, try pasting the\nOCaml code in the [Try ReasonML](https://reasonml.github.io/en/try) page or read\n[this guide](https://reasonml.github.io/docs/en/comparison-to-ocaml).\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Futkarshkukreti%2Freaml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Futkarshkukreti%2Freaml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Futkarshkukreti%2Freaml/lists"}