{"id":13507485,"url":"https://github.com/kbrw/reaxt","last_synced_at":"2025-05-14T22:07:39.793Z","repository":{"id":25276116,"uuid":"28701749","full_name":"kbrw/reaxt","owner":"kbrw","description":"Use React template into your Elixir application for server rendering","archived":false,"fork":false,"pushed_at":"2024-11-05T09:39:47.000Z","size":374,"stargazers_count":374,"open_issues_count":10,"forks_count":37,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-14T19:54:30.437Z","etag":null,"topics":["elixir","javascript","react","server-side-rendering","webpack"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kbrw.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":"2015-01-02T02:55:17.000Z","updated_at":"2024-11-05T09:38:49.000Z","dependencies_parsed_at":"2024-05-01T16:19:32.404Z","dependency_job_id":"e42ff6b9-96be-42a1-8a06-d08243b45a9b","html_url":"https://github.com/kbrw/reaxt","commit_stats":null,"previous_names":["awetzel/reaxt"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbrw%2Freaxt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbrw%2Freaxt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbrw%2Freaxt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbrw%2Freaxt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kbrw","download_url":"https://codeload.github.com/kbrw/reaxt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254235696,"owners_count":22036963,"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":["elixir","javascript","react","server-side-rendering","webpack"],"created_at":"2024-08-01T02:00:34.965Z","updated_at":"2025-05-14T22:07:34.776Z","avatar_url":"https://github.com/kbrw.png","language":"Elixir","readme":"Reaxt\n=====\n\nUse your [React](http://facebook.github.io/react/) components into your elixir application, using [webpack](http://webpack.github.io/) compilation, so :\n\n- An isomorphic ready library (SEO/JS are now nice together), but with Elixir on the server side\n- Just a Library, with a minimum constraint about your application organization and layout :\n  - use any javascript compiled language\n  - use any javascript routing logic or library\n  - you can use JS React rendered component only for parts of your webpage\n- Nice fluent dev workflow, with :\n  - combined stacktrace : elixir | javascript\n  - hot loading on both server and browser\n  - NPM/Webpack as the only config for respectively dependencies/compilation\n  - A cool UI to have an overview of your compiled javascript application\n  - You do not have to think about the server side Javascript configuration, \n    just write a webpack conf for the browser, and it is ready to use.\n\nTODO List :\n\n- [ ] Handle Source map in server side for combined stacktrace generation\n- [ ] Add Source map in client side \n- [ ] Currently the compiler compile in parallel server entry and client\n      entries, but server side compilation does not handle *cacheable* and slow\n      down very much the compilation\n- [ ] handle css loader for `reaxt/style` loader, currently it is ignored in\n      server side, which is a problem for URL remapping in CSS.\n\n## Usage ##\n\nSee https://github.com/awetzel/reaxt-example for a ready to use example\napplication, but lets look into details and requirements.\n\nIn your mix.exs, add the dependency and the custom compiler for webpack: \n- Add the `:reaxt` dependency to your project.deps and application.applications\n- Add `compilers: [:reaxt_webpack] ++ Mix.compilers` to your project, (`:reaxtWebpack` for elixir \u003c v1.0.3)\n\nIn your config/config.exs, link the reaxt application to the\napplication containing the JS web app\n- `config :reaxt,:otp_app,:yourapp`\n\nCreate the good directory and file layout:\n- `MIXROOT/web`\n- `MIXROOT/web/package.json` containing your app NPM dependencies\n- `MIXROOT/web/webpack.config.js` containing only the client side\n  logic, use \"reaxt/style\" instead of \"style\" loader to load your css.\n  A typical output path is `../priv/static`.\n- `MIXROOT/web/components` containing modules exporting React components\n\nIn your elixir code generating HTML :\n- add `WebPack.header` in the `\u003chead\u003e`\n- add a script with src `/your/public/path/\u003c%= WebPack.file_of(:entry_name) %\u003e` \n\nThen render your server side HTML :\n\n```elixir\n# if web/components/thefile exports the react component\nReaxt.render!(:thefile,%{it: \"is\", the: \"props\"})\n\n# if web/components/thefile exports an object containing a react component\n# at the key \"component_key\"\n\nReaxt.render!({:thefile,:component_key},%{it: \"is\", the: \"props\"})\n```\n\nThe function return a `%{html: html,css: css,js_render: js_render}`, you have to add in the html :\n- the css `\u003cstyle\u003e\u003c%= render.css %\u003e\u003c/style\u003e`\n- the html in an identified block (`\u003cdiv id=\"myblockid\"\u003e\u003c%= render.html %\u003e\u003c/div\u003e`)\n- the client side rendering call with `\u003cscript\u003e\u003c%= render.js_render %\u003e(\"myblockid\")\u003c/script\u003e`\n\nFor example, if you want a page entirely generated by the react\ncomponent exported at `web/components/app.js`, then in your elixir web server, send :\n\n```elixir\nEEx.eval_string(\"\"\"\n  \u003chtml\u003e\n  \u003chead\u003e \u003c%= WebPack.header %\u003e\n    \u003cstyle\u003e\u003c%= render.css %\u003e\u003c/style\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"content\"\u003e\u003c%= render.html %\u003e\u003c/div\u003e\n    \u003cscript src=\"/public/\u003c%= WebPack.file_of(:main) %\u003e\"\u003e\u003c/script\u003e\n    \u003cscript\u003e\u003c%= render.js_render %\u003e(\"content\")\u003c/script\u003e\n  \u003c/body\u003e\n  \u003c/html\u003e\n\"\"\",render: Reaxt.render!(:app,%{my: \"props\"}))\n```\n\nFinally, you have to serve files generated by webpack :\n```elixir\nplug Plug.Static, at: \"/public\", from: :yourapp\n```\n\nThen `iex -S mix` and enjoy, but the best is to come.\n\n## Custom Plug : Live reloading and WebPack web UI\n\nWhen you serve files generated by webpack, use the plug\n`WebPack.Plug.Static` instead of `Plug.Static`, it contains \n an elixir implementation of\n [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server),\n and a [nice UI](http://webpack.github.io/analyse/).\n\n```elixir\n  if Mix.env == :dev do \n    use Plug.Debugger\n    plug WebPack.Plug.Static, at: \"/public\", from: :myweb\n  else\n    plug Plug.Static, at: \"/public\", from: :myweb\n  end\n```\n\nThen go to http://yourhost/webpack to see a beautiful summary of\nyour compiled js application.\n\nThen configure in your application configuration :\n- `config :reaxt,:hot,true` to enable that:\n  - server and client side JS will be compiled each time you change files\n  - server side renderers will be restarted at each compilation \n  - client browser page will be reloaded, a desktop notification will be triggered\n  - The `/webpack` UI will be automatically reloaded if it is on your browser\n- `config :reaxt,:hot,:client` to enable the same hot loading, but\n  with webpack module hot loading on browser to avoid full page reload\n  - use the webpack loader `react-hot-loader` to load your\n    component to enable automatic browser hot reloading of your components\n  - the `reaxt/style` loader for your css enable hot reloading of your css\n\n## Dynamic Handler and customize rendering (useful with react-router)\n\nSee a full example in [reaxt-example](https://github.com/awetzel/reaxt-example/blob/master/web/components/my_router.js)\n\nReaxt provides facilities to easily customize the rendering process at the\nserver and the client side : this is done by attaching `reaxt_server_render`\nand/or `reaxt_client_render` to the module or object referenced by the first\nargument of `Reaxt.render!(`.\n\n- `reaxt_server_render(arg,render)` will \n  - take `arg` from the second argument of `Reaxt.render`, \n  - have to execute `render(component,param)` when the wanting handler and props\n    are determined. `param` is any stringifyable object passed to client rendering\n- `reaxt_client_render(props,render,param)` have to render the\n  good selected component on the client side. \n  - `props` is the initial props used in server rendering,\n  - `render` is function you have to call to make the client react rendering\n  - `param` is the deserialized version of the third parameter of the callback in `reaxt_server_render`\n\nTo understand how they work, let's look at the default implementation\nof these functions (what happened when they are not implemented).\n\n```javascript\n// default server rendering only take the exported module as the\n// handler to render and the argument as the props\ndefault_reaxt_server_render = function(arg,render){\n  render(\u003cthis {...arg}/\u003e,null)\n}\n// default client rendering only take the exported module as the\n// handler to render, the param is ignored\ndefault_reaxt_client_render = function(props,render,param){\n  render(\u003cthis {...props}/\u003e)\n}\n```\n\nNow let's see an example usage of these functions : react-router\nintegration (`Reaxt.render` second argument is the Path):\nSee a full example in [reaxt-example](https://github.com/awetzel/reaxt-example/blob/master/web/components/my_router.js)\n\n```elixir\nReaxt.render!(:router_handler,full_path(conn))\n```\n\n```javascript\nvar App = require(\"./app\")\nvar Router = require(\"react-router\")\nvar Routes = require(\"./routes\")\nmodule.exports = {\n  reaxt_server_render: function(path,render){\n    Router.run(Routes, path,function (Handler, state) {\n      render(\u003cHandler/\u003e)\n    })\n  },\n  reaxt_client_render: function(props,render){\n    Router.run(Routes,Router.HistoryLocation,function(Handler,state){\n      render(\u003cHandler {...props}/\u003e)\n    })\n  }\n}\n```\n\n## Error management\n\nJS exceptions and stacktraces during rendering are converted into\nElixir one with a fake stacktrace pointing to the generated javascript file.\n\nThis is really nice because you can see javascript stacktrace in the `Plug.Debugger` UI on exception.\n\n## Global Configuration\n\nYou can define a term in Elixir using Application `env`\n`global_config` which will be available globally in the server and\nthe client side with `require('reaxt/config')`.\n\n```elixir\nconfig :reaxt,:global_config, %{\n  some_config1: \"value1\",\n  some_config2: \"value2\"\n}\n```\n\nThis configuration is static once the `:reaxt` application has started. So if\nyou want to change this configuration at runtime, you need to reload all\n`:reaxt` renderer with `Reaxt.reload`. Remember : this is a costly reload, do\nnot use it to maintain a state at real time but only for configuration purpose.\n\n```elixir\nApplication.put_env :reaxt, :global_config, %{\n  some_config1: \"value3\",\n  some_config2: \"value4\"\n}\nReaxt.reload\n```\n\nThen in your javascript component, you can use this config using : \n\n```javascript\nrequire('reaxt/config').some_config1\n```\n\n## Perfs and pool management\n\nThe NodeJS renderers are managed in a pool (to obtain \"multicore\" JS rendering), so :\n\n- `config :reaxt,:pool_size` configure the number of worker running permanently\n- `config :reaxt,:pool_max_overflow` configure the maximum extension of the\n  pool when query happens an it is full\n\nA clever configuration could be : \n\n```elixir\nconfig :reaxt,:pool_size, if(Mix.env == :dev, do: 1, else: 10)\n```\n\nFor minification, remember that webpack compilation is launched by Mix, so you\ncan use `process.env.MIX_ENV` in your webpack config.\n\n```elixir\n{\n  externals: { react: \"React\" },\n  plugins: (function(){\n    if(process.env.MIX_ENV == \"prod\") \n      return [new webpack.optimize.UglifyJsPlugin({minimize: true})]\n    else\n      return []\n  })()\n}\n```\n\n# CONTRIBUTING\n\nHi, and thank you for wanting to contribute.\nPlease refer to the centralized informations available at: https://github.com/kbrw#contributing\n\n","funding_links":[],"categories":["Build Tools"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbrw%2Freaxt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkbrw%2Freaxt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbrw%2Freaxt/lists"}