Introducing ReactPhoenix - Render React.js components in Phoenix views

April 20, 2017 4 minutes read

Check out the package at https://github.com/geolessel/react-phoenix

While I love Elixir and Phoenix, my current daily, pay-the-bills language at Planning Center is Ruby and Rails. We create a lot of React.js components as well and I really like working with React. So it is an obvious transition for me to want to use React components in my Phoenix applications as well.

Every time I started a new Phoenix application, it took me quite a while to remember how to get everything set up in Phoenix in order to display a React component. Beyond that, most of the tutorials I found that dealt with React and Brunch (vs Webpack) only rendered a single component. I’m more interested in React sprinkles in my app and the global rendering on every page load wasn’t working for me. I saw a hole in the current offerings of packages dealing with Phoenix and React and I decided to fill it.

ReactPhoenix

I created a package named react_phoenix to make it so much easier to get React components into your Phoenix views. It is heavily inspired by the react-rails gem that I use every day. I wanted something with as little installation pains as possible and that got out of my way when all I wanted to do was render a component in a view. Here’s how it works.

Prerequisites

To begin with, your Phoenix app should already have React installed as a dependency. Beyond that, I recommend that you have a couple of Babel presets installed and active as well in order for brunch to know how to compile those javascript component files.

> npm install react babel-preset-react babel-preset-env --save

Activate the presets in babel by adding this to your brunch-config.js file.

exports.config = {
  // ...
  
  plugins: {
    babel: {
      presets: ["env", "react"],
      ignore: [/web\/static\/vendor/]
    }
  },
  
  // ...
}

Installation

I tried to make the README for the package detailed as I could in this part, but I’ll mirror it here. I should state that as of this writing (and the official public unveiling), the package is at version 0.3.0, which is what these steps pertain to. It is unlikely this part will change much in the future, but just in case, you may want to check that README if things don’t seem to be working. Also, we will be using Phoenix 1.2. I haven’t tested this in Phoenix 1.3 quite yet.

Step 1

Add ReactPhoenix as a dependency in your mix.exs file.

def deps do
  [
    {:react_phoenix, "~> 0.3.0"}
  ]
end

then run mix deps.get.

Step 2

Add the included javascript helper to our package.json file (in the dependencies section).

{
  "dependencies": {
    "phoenix": "file:deps/phoenix",
    "phoenix_html": "file:deps/phoenix_html",

    "react-phoenix": "file:deps/react_phoenix" <-- ADD THIS!
  }
}

then run npm install.

Step 3

Import the javascript file into our web/static/js/app.js bundle.

import "react-phoenix"

Step 4 (optional)

Sometimes it’s nice to be able to call just the function name and not the full name of the function that includes the module name. In order to do so, edit your web/web.ex file and add an import for ReactPhoenix.

def view do
  quote do
    use Phoenix.View, root: "web/templates"

    import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]

    use Phoenix.HTML

    import MyPhoenixApp.Router.Helpers
    import MyPhoenixApp.ErrorHelpers
    import MyPhoenixApp.Gettext

    import ReactPhoenix # <-- ADD THIS!
  end
end

Let’s render some React!

Now that we have everything set up, let’s render some React components. Create a simple component we can use to make sure things are hooked up correctly. In web/static/js/components/hello.jsx:

Create a component

import React from "react"

export default class Hello extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      <h1>Hello {this.props.name}</h1>
    )
  }
}

import the component

Let our javascript environment know that this component exists and we’d like to make it available in web/static/js/app.js.

import Hello from "./components/hello"
window.Components = {
  Hello
}

Render the component

Use ReactPhoenix to render this component in a Phoenix view:

<%= react_component("Components.Hello", %{name: "React"}) %>
# or, if you haven't added import ReactPhoenix in web/web.ex
<%= ReactPhoenix.react_component("Components.Hello", %{name: "React"}) %>

Here we are telling ReactPhoenix that we’d like to render a component registered as Components.Hello (which we did in app.js) and pass it props of name: "React".

View the results

And that’s it! You should be able to reload your view and see your React component lovingly rendered with the specified props.

A little video version

I took a quick video of me setting up a new Phoenix app and getting React in it with ReactPhoenix. I go from mix phoenix.new to getting a React component rendered in a view in 4 minutes (including typos!).

The future

I’d love to keep expanding this and making it better. I’ve already got it working in a Phoenix app that does server-side React component rendering (using a couple of other packages and the target_id option in ReactPhoenix.react_component/3) but there are ways I can make this even better. PLEASE reach out to me with any issues or suggestions you have to make it better, easier, more robust, faster, whatever. I’d love to hear them.

You can check out the hex docs at https://hexdocs.pm/react_phoenix/readme.html and see the code on GitHub at https://github.com/geolessel/react-phoenix.

Get in touch with me on twitter at @geolessel and @geo in the Elixir slack group. I also have a newsletter that I’d be grateful if you signed up for below. I have some fun ideas brewing that I’d love to keep you informed about in the future. Thanks!

Updated: