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.
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
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 dependenciesMIXROOT/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"})
# if you want to use javascript to dynamically find your component and initial props
# then thefile should exports a function(arg,callback)
# which then call callback(handler,props)
# WARNING : your dynamically loaded component must have a displayName of
# "componentFileName.componentProperty" or "componentFileName"
Reaxt.render!(:thefile,args,dyn_handler: true)
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 src="/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.
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
- use the webpack loader
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.
The NodeJS renderers are managed in a pool (to obtain "multicore" JS rendering), so :
config :reaxt,:pool_size
configure the number of worker running permanentlyconfig :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 []
})()
}