Skip to content
vic

kbrw/reaxt

Use React template into your Elixir application for server rendering

kbrw/reaxt.json
{
"createdAt": "2015-01-02T02:55:17Z",
"defaultBranch": "master",
"description": "Use React template into your Elixir application for server rendering",
"fullName": "kbrw/reaxt",
"homepage": "",
"language": "Elixir",
"name": "reaxt",
"pushedAt": "2024-11-05T09:39:47Z",
"stargazersCount": 373,
"topics": [
"elixir",
"javascript",
"react",
"server-side-rendering",
"webpack"
],
"updatedAt": "2025-11-26T15:46:05Z",
"url": "https://github.com/kbrw/reaxt"
}

Use your React components into your elixir application, using webpack compilation, so :

  • An isomorphic ready library (SEO/JS are now nice together), but with Elixir on the server side
  • Just a Library, with a minimum constraint about your application organization and layout :
    • use any javascript compiled language
    • use any javascript routing logic or library
    • you can use JS React rendered component only for parts of your webpage
  • Nice fluent dev workflow, with :
    • combined stacktrace : elixir | javascript
    • hot loading on both server and browser
    • NPM/Webpack as the only config for respectively dependencies/compilation
    • A cool UI to have an overview of your compiled javascript application
    • You do not have to think about the server side Javascript configuration, just write a webpack conf for the browser, and it is ready to use.

TODO List :

  • Handle Source map in server side for combined stacktrace generation
  • Add Source map in client side
  • Currently the compiler compile in parallel server entry and client entries, but server side compilation does not handle cacheable and slow down very much the compilation
  • handle css loader for reaxt/style loader, currently it is ignored in server side, which is a problem for URL remapping in CSS.

See https://github.com/awetzel/reaxt-example for a ready to use example application, but lets look into details and requirements.

In your mix.exs, add the dependency and the custom compiler for webpack:

  • Add the :reaxt dependency to your project.deps and application.applications
  • Add compilers: [:reaxt_webpack] ++ Mix.compilers to your project, (:reaxtWebpack for elixir < v1.0.3)

In your config/config.exs, link the reaxt application to the application containing the JS web app

  • config :reaxt,:otp_app,:yourapp

Create the good directory and file layout:

  • MIXROOT/web
  • MIXROOT/web/package.json containing your app NPM dependencies
  • MIXROOT/web/webpack.config.js containing only the client side logic, use “reaxt/style” instead of “style” loader to load your css. A typical output path is ../priv/static.
  • MIXROOT/web/components containing modules exporting React components

In your elixir code generating HTML :

  • add WebPack.header in the <head>
  • add a script with src /your/public/path/<%= WebPack.file_of(:entry_name) %>

Then render your server side HTML :

# if web/components/thefile exports the react component
Reaxt.render!(:thefile,%{it: "is", the: "props"})
# if web/components/thefile exports an object containing a react component
# at the key "component_key"
Reaxt.render!({:thefile,:component_key},%{it: "is", the: "props"})

The function return a %{html: html,css: css,js_render: js_render}, you have to add in the html :

  • the css <style><%= render.css %></style>
  • the html in an identified block (<div id="myblockid"><%= render.html %></div>)
  • the client side rendering call with <script><%= render.js_render %>("myblockid")</script>

For example, if you want a page entirely generated by the react component exported at web/components/app.js, then in your elixir web server, send :

EEx.eval_string("""
<html>
<head> <%= WebPack.header %>
<style><%= render.css %></style>
</head>
<body>
<div id="content"><%= render.html %></div>
<script unsrc="/public/<%= WebPack.file_of(:main) %>"></script>
<script><%= render.js_render %>("content")</script>
</body>
</html>
""",render: Reaxt.render!(:app,%{my: "props"}))

Finally, you have to serve files generated by webpack :

plug Plug.Static, at: "/public", from: :yourapp

Then iex -S mix and enjoy, but the best is to come.

Custom Plug : Live reloading and WebPack web UI

Section titled “Custom Plug : Live reloading and WebPack web UI”

When you serve files generated by webpack, use the plug WebPack.Plug.Static instead of Plug.Static, it contains an elixir implementation of webpack-dev-server, and a nice UI.

if Mix.env == :dev do
use Plug.Debugger
plug WebPack.Plug.Static, at: "/public", from: :myweb
else
plug Plug.Static, at: "/public", from: :myweb
end

Then go to http://yourhost/webpack to see a beautiful summary of your compiled js application.

Then configure in your application configuration :

  • config :reaxt,:hot,true to enable that:
    • server and client side JS will be compiled each time you change files
    • server side renderers will be restarted at each compilation
    • client browser page will be reloaded, a desktop notification will be triggered
    • The /webpack UI will be automatically reloaded if it is on your browser
  • config :reaxt,:hot,:client to enable the same hot loading, but with webpack module hot loading on browser to avoid full page reload
    • use the webpack loader react-hot-loader to load your component to enable automatic browser hot reloading of your components
    • the reaxt/style loader for your css enable hot reloading of your css

Dynamic Handler and customize rendering (useful with react-router)

Section titled “Dynamic Handler and customize rendering (useful with react-router)”

See a full example in reaxt-example

Reaxt provides facilities to easily customize the rendering process at the server and the client side : this is done by attaching reaxt_server_render and/or reaxt_client_render to the module or object referenced by the first argument of Reaxt.render!(.

  • reaxt_server_render(arg,render) will
    • take arg from the second argument of Reaxt.render,
    • have to execute render(component,param) when the wanting handler and props are determined. param is any stringifyable object passed to client rendering
  • reaxt_client_render(props,render,param) have to render the good selected component on the client side.
    • props is the initial props used in server rendering,
    • render is function you have to call to make the client react rendering
    • param is the deserialized version of the third parameter of the callback in reaxt_server_render

To understand how they work, let’s look at the default implementation of these functions (what happened when they are not implemented).

// default server rendering only take the exported module as the
// handler to render and the argument as the props
default_reaxt_server_render = function(arg,render){
render(<this {...arg}/>,null)
}
// default client rendering only take the exported module as the
// handler to render, the param is ignored
default_reaxt_client_render = function(props,render,param){
render(<this {...props}/>)
}

Now let’s see an example usage of these functions : react-router integration (Reaxt.render second argument is the Path): See a full example in reaxt-example

Reaxt.render!(:router_handler,full_path(conn))
var App = require("./app")
var Router = require("react-router")
var Routes = require("./routes")
module.exports = {
reaxt_server_render: function(path,render){
Router.run(Routes, path,function (Handler, state) {
render(<Handler/>)
})
},
reaxt_client_render: function(props,render){
Router.run(Routes,Router.HistoryLocation,function(Handler,state){
render(<Handler {...props}/>)
})
}
}

JS exceptions and stacktraces during rendering are converted into Elixir one with a fake stacktrace pointing to the generated javascript file.

This is really nice because you can see javascript stacktrace in the Plug.Debugger UI on exception.

You can define a term in Elixir using Application env global_config which will be available globally in the server and the client side with require('reaxt/config').

config :reaxt,:global_config, %{
some_config1: "value1",
some_config2: "value2"
}

This configuration is static once the :reaxt application has started. So if you want to change this configuration at runtime, you need to reload all :reaxt renderer with Reaxt.reload. Remember : this is a costly reload, do not use it to maintain a state at real time but only for configuration purpose.

Application.put_env :reaxt, :global_config, %{
some_config1: "value3",
some_config2: "value4"
}
Reaxt.reload

Then in your javascript component, you can use this config using :

require('reaxt/config').some_config1

The NodeJS renderers are managed in a pool (to obtain “multicore” JS rendering), so :

  • config :reaxt,:pool_size configure the number of worker running permanently
  • config :reaxt,:pool_max_overflow configure the maximum extension of the pool when query happens an it is full

A clever configuration could be :

config :reaxt,:pool_size, if(Mix.env == :dev, do: 1, else: 10)

For minification, remember that webpack compilation is launched by Mix, so you can use process.env.MIX_ENV in your webpack config.

{
externals: { react: "React" },
plugins: (function(){
if(process.env.MIX_ENV == "prod")
return [new webpack.optimize.UglifyJsPlugin({minimize: true})]
else
return []
})()
}

Hi, and thank you for wanting to contribute. Please refer to the centralized informations available at: https://github.com/kbrw#contributing