{"id":15059511,"url":"https://github.com/dwyl/phoenix-elm-starter","last_synced_at":"2025-10-05T13:37:21.821Z","repository":{"id":38312191,"uuid":"485875264","full_name":"dwyl/phoenix-elm-starter","owner":"dwyl","description":"🌱 A quick starter for building a Phoenix + Elm (PETE Stack) project with Esbuild compilation, live reloading, etc.","archived":false,"fork":false,"pushed_at":"2025-09-16T09:54:36.000Z","size":652,"stargazers_count":32,"open_issues_count":3,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-16T11:58:01.959Z","etag":null,"topics":["elixir","elixir-lang","elixir-phoenix","elm","elm-lang","example","phoenix","phoenix-framework","starter","starter-kit"],"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/dwyl.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-04-26T16:59:54.000Z","updated_at":"2025-09-16T09:54:33.000Z","dependencies_parsed_at":"2023-02-15T03:00:59.658Z","dependency_job_id":"33e800b7-f27d-4334-ab52-e5f889c43ba3","html_url":"https://github.com/dwyl/phoenix-elm-starter","commit_stats":{"total_commits":155,"total_committers":6,"mean_commits":"25.833333333333332","dds":0.1548387096774193,"last_synced_commit":"fc1a46c947901a4a7ef3a23a6314cc9efed2dcb5"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dwyl/phoenix-elm-starter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Fphoenix-elm-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Fphoenix-elm-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Fphoenix-elm-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Fphoenix-elm-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dwyl","download_url":"https://codeload.github.com/dwyl/phoenix-elm-starter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Fphoenix-elm-starter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278464316,"owners_count":25991176,"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-10-05T02:00:06.059Z","response_time":54,"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":["elixir","elixir-lang","elixir-phoenix","elm","elm-lang","example","phoenix","phoenix-framework","starter","starter-kit"],"created_at":"2024-09-24T22:44:43.902Z","updated_at":"2025-10-05T13:37:21.786Z","avatar_url":"https://github.com/dwyl.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Phoenix + Elm _Starter_\n\nA **starter kit** \nfor adding \nan **`Elm` frontend**\nto a **`Phoenix` Web App**lication. \n\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/dwyl/phoenix-elm-starter/ci.yml?label=build\u0026style=flat-square\u0026branch=main)\n[![codecov.io](https://img.shields.io/codecov/c/github/dwyl/phoenix-elm-starter/main.svg?style=flat-square)](http://codecov.io/github/dwyl/phoenix-elm-starter?branch=main)\n[![Hex.pm](https://img.shields.io/hexpm/v/phoenix?color=brightgreen\u0026style=flat-square)](https://hex.pm/packages/phoenix)\n[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/dwyl/phoenix-elm-starter/issues)\n[![HitCount](https://hits.dwyl.com/dwyl/phoenix-elm-starter.svg)](https://github.com/dwyl/phoenix-elm-starter)\n\n\n\u003c/div\u003e\n\n## _Why?_\n\nTo build a more **advanced UI/UX**,\nthere is **_nothing_ better than `Elm`**.\n\n\u003c!--\nFor _browser-based_ apps\nrequiring quick iteration speed,\nexcellent long-term maintainability\nand pixel-perfect UI,\nyou've come to the right place!\n--\u003e\n\n\u003c!--\nBoth the \"user\" and developer \nexperience are unrivaled.\nThe promise of \n[\"no runtime exceptions\"](https://softwareengineering.stackexchange.com/questions/337295/benefit-of-having-no-runtime-errors),\n_real_ Type safety \nand an incredible \n[\"friendly\" compiler](https://elm-lang.org/news/compilers-as-assistants)\nare no-brainer from an engineering \nperspective. \nWe've searched a long time\nand nothing else comes close.\n--\u003e \n\n\u003cbr /\u003e\n\n## What?\n\nA step-by-step guide to getting `Elm` \nworking in a `Phoenix` app\nwith live reloading.\n\nBy the end of this guide you will understand\nhow all the pieces fit together.\n\nLatest `Phoenix`, \nlatest `Elm` \nand `esbuild`; \nthe fastest build system available.\n\n\u003cbr /\u003e\n\n## How?\n\n### Prerequisites\n\n_Before_ you start,\nmake sure you have the following installed:\n\n1. `Elixir`: https://elixir-lang.org/install.html \u003cbr /\u003e\n   New to `Elixir`, see: https://github.com/dwyl/learn-elixir\n2. `Phoenix`: https://hexdocs.pm/phoenix/installation.html \u003cbr /\u003e\n   New to `Phoenix` see: \n   https://github.com/dwyl/learn-phoenix-framework\n3. `Node.js`: https://nodejs.org/en \n4. `Elm`: https://guide.elm-lang.org/install/elm.html\ne.g: `npm install -g elm@elm0.19.1`\n\n\nOnce you have `Elm` installed, \nrun the following command in your terminal \nto confirm:\n\n```sh\nelm --version\n```\nyou should see:\n```sh\n0.19.1\n```\n\n\u003cbr /\u003e\n\n### Create the Phoenix App\n\nFor this example, we are creating a basic **`Phoenix`** App\nwithout the live dashboard or mailer (email)\nbut with `Ecto` (Postgres database)\nso that we can simulate a real-world app.\n\n\n```sh\nmix phx.new app --no-dashboard --no-mailer\n```\n\n```sh\ncd app\n```\n\n```sh\nmix ecto.setup\n```\n\n```sh\nmix phx.server\n```\n\nOpen your web browser to the URL: http://localhost:4000\n\nYou should see the default **`Phoenix`** home page:\n\n\u003cimg width=\"828\" alt=\"image\" src=\"https://user-images.githubusercontent.com/194400/165493125-0e714185-e268-411f-bb7d-99f7cd0eb8ba.png\"\u003e\n\nSo far so good. 👌 \u003cbr /\u003e\nLet's add **`Elm`**! \n\n## Add `Elm` to the `Phoenix` App\n\n\nChange directory in `/assets`,\nand create a directory called `elm`.\nInside the `/assets/elm` directory,\nrun the following command:\n\n```sh\nelm init\n```\nSee: https://elm-lang.org/0.19.1/init\n\nYou will see the following prompt:\n\n```sh\nHello! Elm projects always start with an elm.json file. I can create them!\n\nNow you may be wondering, what will be in this file? How do I add Elm files to\nmy project? How do I see it in the browser? How will my code grow? Do I need\nmore directories? What about tests? Etc.\n\nCheck out \u003chttps://elm-lang.org/0.19.1/init\u003e for all the answers!\n\nKnowing all that, would you like me to create an elm.json file now? [Y/n]: y\n```\n\nType `y` and `Enter` to continue:\n```sh\nOkay, I created it. Now read that link!\n```\n\nThat will have created a new directory at `/assets/elm/src`\nand an `elm.json` file.\n\n### Create a `Main.elm` file\n\nCreate a new file with the path: \n`/assets/src/Main.elm`\nand add the following contents to it:\n\n```elm\nmodule Main exposing (..)\nimport Html exposing (text)\n\nname = \"Alex\" -- set name to your name!\n\nmain =\n  text (\"Hello \" ++ name ++ \"!\")\n```\n\n### _Manually_ Compile the `Elm` Code\n\n```sh\nelm make elm/src/Main.elm --output=../priv/static/assets/elm.js\n```\nThat results in an un-optimized **`elm.js`** file that is **488kb**\nFor development/testing purposes this is fine;\nwe can optimize/minify it for production later. (see below)\n\nLet's include this file in our `Phoenix` template just to show that it works.\n\n\u003cbr /\u003e\n\n### _Temporarily_ add `elm.js` to `root.html.heex` template\n\n\u003e **Note**: this will not work in production,\n\u003e it's just for basic illustration as a \"_quick win_\".\n\nOpen the \n`lib/app_web/templates/layout/root.html.heex` \nfile\nand add the following lines just before the `\u003c/body\u003e` element:\n\n```html\n\u003cscript type=\"text/javascript\" src={Routes.static_path(@conn, \"/assets/elm.js\")}\u003e\u003c/script\u003e\n\u003cscript\u003e\n    const $root = document.createElement('div');\n    document.body.appendChild($root);\n\n    Elm.Main.init({\n        node: $root\n    });\n\u003c/script\u003e\n```\n\nWith those lines added to the `root.html.heex` file.\n\nRun `mix phx.server` again and refresh your browser: \nhttp://localhost:4000/\n\nYou should see something similar to the following:\n\n![phoenix-elm-hello-alex](https://user-images.githubusercontent.com/194400/165504628-17975e87-447a-4523-8f7c-a4501bf52621.png)\n\nThat **`Hello Alex!`** was rendered by `Elm`. \n\nNow that we know it _can_ work the _hard_ way,\nlet's do it properly!\n\n**`undo`** those changes made in `root.html.heex`\nand save the file.\n\n\u003cbr /\u003e\n\n## Compile `Elm` via `esbuild`\n\nNext we will include the `elm` app \ninto the `esbuild` pipeline\nso that: \n1. We can have a watcher and hot reloader.\n2. `Phoenix` \ncan handle asset compilation \nduring deployment.\n\n### 1. Install `esbuild-plugin-elm`\n\nIn the `/assets/elm` directory,\nrun the following command\nto install \n[`esbuild-plugin-elm`](https://github.com/phenax/esbuild-plugin-elm)\n\n```sh\nnpm install -D esbuild-plugin-elm\n```\n\n### 2. Create the \"initialization\" `index.js` file\n\nCreate a file with the path\n`assets/elm/src/index.js`\nand add the the following code: \n\n```js\nimport { Elm } from './Main.elm';\n\nconst $root = document.createElement('div');\ndocument.body.appendChild($root);\n\nElm.Main.init({\n  node: $root\n});\n```\n\nRef: \n[phenax/esbuild-plugin-elm/example/src/index.js](https://github.com/phenax/esbuild-plugin-elm/blob/main/example/src/index.js)\n\n\n### 3. Create `build.js` file\n\nCreate a new file with the path:\n`assets/elm/build.js`\nand add the following code to it:\n\n```js\nconst esbuild = require('esbuild');\nconst ElmPlugin = require('esbuild-plugin-elm');\n\nconst isProduction = process.env.MIX_ENV === \"prod\"\n\nasync function watch() {\n  const ctx = await esbuild.context({\n    entryPoints: ['src/index.js'],\n    bundle: true,\n    outfile: '../js/elm.js',\n    plugins: [\n      ElmPlugin({\n        debug: true\n      }),\n    ],\n  }).catch(_e =\u003e process.exit(1))\n  await ctx.watch()\n}\n\n\nasync function build() {\n  await esbuild.build({\n    entryPoints: ['src/index.js'],\n    bundle: true,\n    minify: true,\n    outfile: '../js/elm.js',\n    plugins: [\n      ElmPlugin(),\n    ],\n  }).catch(_e =\u003e process.exit(1))\n}\n\nif (isProduction)\n  build()\nelse\n  watch()\n```\n\nRef: \n[phenax/esbuild-plugin-elm/example/build.js](https://github.com/phenax/esbuild-plugin-elm/blob/main/example/build.js)\n\n\n### 4. Add the Build Command / Watcher to `dev.exs`\n\nOpen the `config/dev.exs` file\nand locate the `watchers:` section.\nAdd the following line the list:\n\n```\nnode: [\"./build.js\", \"--watch\", cd: Path.expand(\"../assets/elm\", __DIR__)]\n```\n\n\n### 5. Import the compiled Elm (JS) Code in `app.js`\n\nOpen the `assets/js/app.js`\nfile and add the following lines\nnear the top of the file:\n\n```js\n// import the compiled Elm app:\nimport './index.js';\n```\n\ne.g: \n[app.js#L28](https://github.com/dwyl/phoenix-elm-starter/blob/ee107537c92c1dfd9153710a0d75a5c3936f694f/assets/js/app.js#L28)\n\n\n\u003cbr /\u003e\n\nWith all 3 files saved,\nrun the `Phoenix` server:\n\n```sh\nmix phx.server\n```\n\nYou should see output similar to this:\n\n```sh\n[info] Running AppWeb.Endpoint with cowboy 2.9.0 at 127.0.0.1:4000 (http)\n[info] Access AppWeb.Endpoint at http://localhost:4000\n[watch] build finished, watching for changes...\nSuccess!\n\n    Main ───\u003e /var/folders/f2/3ptgvnsd4kg6v04dt7j1y5dc0000gp/T/2022327-49759-ua0u9f.iqdp.js\n\n[watch] build started (change: \"js/index.js\")\n[watch] build finished\n```\n\nThat confirms that the `Elm` build + watchers are working. 🚀\n\n### 6. Test Live Reloading!\n\nWith the `Phoenix` server running, \nand a browser window open \npointing to the `Phoenix` App: http://localhost:4000\n\n![elm-hello-alex](https://user-images.githubusercontent.com/194400/165526427-d3d56650-ab6e-4d3b-8990-317b59a0b436.png)\n\nOpen the `assets/elm/src/Main.elm` file and\nchange the line:\n\n```elm\nname = \"Alex\"\n```\n\nto:\n\n```elm\nname = \"World\"\n```\n\nWhen you save the file it will automatically reload \nin your web browser and will update the `name` accordingly:\n\n![elm-hello-world](https://user-images.githubusercontent.com/194400/165526558-e6d068c9-42fe-4ba1-aa63-39edfb39d4ac.png)\n\nSo the watcher and live reloading is working!\n\n\u003cbr /\u003e\n\nThis is still _very_ far from being a \"real world\" App.\nBut the \"starter\" is here! \n\n\u003cbr /\u003e\n\n# Todo\n\n1. \"Productionize\" the asset compilation:\nhttps://hexdocs.pm/phoenix/asset_management.html#esbuild-plugins\n\n**`@SimonLab`** if you have time to help extend, please go for it! 🙏\n\n2. Add `Elm` Test!\n\n\u003cbr /\u003e\n\n# _Next_\n\nCreate a `Phoenix` Endpoint that returns `json`\nthat can invoked from `Elm`. \u003cbr /\u003e\ne.g: Return an \n[**inspiring `quote`**](https://github.com/dwyl/quotes)  \u003cbr /\u003e\nBorrow from: https://github.com/dwyl/phoenix-content-negotiation-tutorial\n\n\u003cbr /\u003e\n\n## Recommended / Relevant Reading\n\n+ `esbuild-plugin-elm`:\nhttps://github.com/phenax/esbuild-plugin-elm\n+ Adding a custom watcher to Phoenix:\nhttps://dev.to/contact-stack/adding-a-custom-watcher-to-phoenix-1e10\nthanks to [`@michaeljones`](https://github.com/michaeljones) \n\n\n\n\u003cbr /\u003e\n\n\u003c!--\n## Troubleshooting\n\n```sh\n[info] Running AppWeb.Endpoint with cowboy 2.9.0 at 127.0.0.1:4000 (http)\n[info] Access AppWeb.Endpoint at http://localhost:4000\n[watch] build finished, watching for changes...\n/Users/n/code/phoenix-elm-starter\n-- NO elm.json FILE ------------------------------------------------------------\n\nIt looks like you are starting a new Elm project. Very exciting! Try running:\n\n    elm init\n\nIt will help you get set up. It is really simple!\n\n✘ [ERROR] [plugin elm]\n\n    assets/src/index.js:1:20:\n      1 │ import { Elm } from './Main.elm';\n        ╵                     ~~~~~~~~~~~~\n\n[error] Task #PID\u003c0.525.0\u003e started from AppWeb.Endpoint terminating\n** (stop) :watcher_command_error\n    (phoenix 1.6.7) lib/phoenix/endpoint/watcher.ex:55: Phoenix.Endpoint.Watcher.watch/2\n    (elixir 1.13.3) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2\n    (stdlib 3.17.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3\nFunction: \u0026Phoenix.Endpoint.Watcher.watch/2\n    Args: [\"node\", [\"./assets/build.js\", \"--watch\"]]\n```\n\n\n\n# Below this point is Work-in-Progress!\n\n\u003cbr /\u003e\n\n\n### Minify\n\nFollowing the instructions in:\nhttps://guide.elm-lang.org/optimization/asset_size.html\n\n```\nelm make src/Main.elm --optimize --output=elm.js\n```\n\n\u003e Install https://www.npmjs.com/package/uglify-js\n```\nnpm install uglify-js -g\n```\n\nCompress:\n```\nuglifyjs elm.js --compress 'pure_funcs=\"F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9\",pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output=elm.min.js\n```\n\nOptimized and compressed (minimised) version `elm.min.js` is **107kb**\n\n\n#### GZip\n\nGzipping the file will result in _much_ faster (down)load times on mobile.\n\nFollowing these instructions:\nhttps://superuser.com/questions/161706/command-to-gzip\n\n```\ngzip elm.min.js\n```\n\n\u003e The `gzip` utility is available on Mac/Linux by default.\n\u003e Windows: https://stackoverflow.com/questions/36733176/gzip-command-windows\n\nNow it's **33kb**.\n(488-33)/488 = **93%** bandwidth saving. \n--\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwyl%2Fphoenix-elm-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdwyl%2Fphoenix-elm-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwyl%2Fphoenix-elm-starter/lists"}