react_phoenix v0.4.2 ReactPhoenix.ServerSide

Functions providing server-side rendering of React components.

What? Why?

Server-side rendering of React components can be importantant for things such as Search Engine Optimization. In short, we have our Phoenix controller gather the HTML that would be generated by a React component (including utilizing any props required) and pass that directly into the view template. Once there, we can render the HTML string directly on initial page load so that loading is fast and data is there when a user visits your page (or a search engine crawler finds it).

Configuration

In order to get this working correctly, you need to take a few extra steps beyond the setup for client-side rendering. Add :react_phoenix to your applications list.

mix.exs

def application do
  [mod: {YourApp, []},
   applications: [..., :react_phoenix]
  ]
end

We eventually will need to compile your React components down into commonjs files so our renderer can understand it. Choose a directory for those compiled components to live in (and remember it because you’ll need it in a few different spots) and let ReactPhoenix know about it. You do that in your config/config.exs file:

config/config.exs

config :react_phoenix,
  compiled_path: Path.join(["priv", "static", "js", "components"])

This tells our renderer to look for the component in the priv/static/js/components directory of our project.

Custom react-stdio location

If you are using a standard Phoenix 1.2 or 1.3 app structure, you can skip this section.

You may need to specify the route of your react-stdio module if your node_modules directory does not live in the root directory of your project (if your ReactPhoenix project lives within an umbrella project, for example).

To do that, add a react_stdio_path config option in your config/config.exs file.

config :react_phoenix,
  compiled_path: Path.join(["priv", "static", "js", "components"]),
  # defaults to node_modules/.bin/react-stdio
  react_stdio_path: Path.join(["path_to_your_app", "node_modules", ".bin", "react-stdio"])

Then run mix deps.clean react_phoenix && mix deps.get.

Set up the watcher

Configure your Endpoint to watch for new component files and compile them down as it finds them (but only in dev). If you have a standard Phoenix setup, you likely already have at least one watcher set up for your Endpoint. Be careful not to overwrite anything you currently have configured, but to add a new watcher.

config/dev.exs

config :your_app, YourApp.Endpoint,
  watchers: [
    node: [                                  # This is likely already there
      "node_modules/brunch/bin/brunch", "watch", "--stdin",
      cd: Path.expand("../", __DIR__)
    ],
    node: [                                  # We need to add this new one
      "node_modules/babel-cli/bin/babel.js", "--watch",
      
      # where your  React components are saved (before compilation)
      "web/static/js/components",
      
      # where you'd like to store them after compilation.
      # This MUST match your compiled_path config from config/config.exs.
      "--out-dir", "priv/static/js/components",
      "--presets=env,react"
    ]
  ]

Add the same command to your deploy script (after brunch build --production).

package.json

{
  // ...
  "scripts": {
    "deploy": "brunch build --production; babel web/static/js/components --out-dir priv/static/js/components --presets=env,react",
    "watch": "brunch watch --stdin"
  },
  // ...
}

Usage

In your Phoenix controller, you can get the rendered HTML and pass that on to your views.

def YourApp.PageController do
  use YourApp.Web, :controller

  def index(conn, _params) do
    people = ["Joe", "Robert", "Other Joe"]
    html = ReactPhoenix.ServerSide.react_component("hello", %{people: people})
    render(conn, "index.html", react_html: html, people: people)
  end
end

Once you are in your view, you can render that HTML and then mount the component client-side with the target_id option. See ReactPhoenix.ClientSide for more information.

<div id="hello"><%= @react_html %></div>
<%= ReactPhoenix.ClientSide.react_component("Components.Hello", %{people: @people}, target_id: "hello") %>

Summary

Functions

Generate a String that contains the HTML of a rendered React component

Functions

react_component(filename, props \\ %{})

Generate a String that contains the HTML of a rendered React component.

It returns a String containing HTML that is suitable for passing to a Phoenix view for rendering. filename should be, along with the path_prefix configured for your application, where your compiled React component can be found (minus the .js filename extension).

For example, if you configured your app with config :react_pheonix, compiled_path: "priv/static/js/components" and you have a component file compiled there with the name of hello_react.js, you would pass "hello_react" as filename.