View Source Server Side Rendering (SSR)

Disclaimer SSR is not a simple topic and there is a lot of issue than can arise depending on what React components you are using. This is a simple implementation that works for the components and library I have tested.

Project setup

SSR requires Node.js to render the javascript on server side. Add nodejs to your mix file.

defp deps do
  [
    {:nodejs, "~> 3.1"},
    ...
  ]
end

Add NodeJs.Supervisor to your application.ex

def start(_type, _args) do
  children = [
    ...
    {NodeJS.Supervisor, [path: LiveReact.SSR.NodeJS.server_path(), pool_size: 4]},
  ]
end

A server file is needed copy the server.mjs file into your assets

curl https://raw.githubusercontent.com/mrdotb/live-react/main/assets/copy/server.mjs > assets/server.mjs

Edit config/config.exs to add a second esbuild process

config :esbuild,
  version: "0.17.11",
  client: [
    args: ~w(
        js/app.js
        --chunk-names=[name]-[hash]
        --splitting
        --format=esm
        --bundle
        --target=es2020
        --main-fields=module,main,exports
        --outdir=../priv/static/assets
        --external:/fonts/* --external:/images/*
      ),
    cd: Path.expand("../assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ],
  server: [
    args: ~w(
      js/server.mjs
      --bundle
      --platform=node
      --target=node19
      --outdir=../priv/react
    ),
    cd: Path.expand("../assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ]

In your config/dev.exs edit the watchers to include a watch on the server esbuild

config :live_react_examples, LiveReactExamplesWeb.Endpoint,
  watchers: [
    client_esbuild: {Esbuild, :install_and_run, [
      :client, ~w(--sourcemap=inline --watch),
    ]},
    server_esbuild: {Esbuild, :install_and_run, [
      :server, ~w(--sourcemap=inline --watch),
    ]}
  ]

Lastly edit the aliases in mix.exs

defp aliases do
[
  setup: ["deps.get", "assets.setup", "cmd npm install --prefix assets", "assets.build"],
  "assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"],
  "assets.build": ["tailwind live_react_examples", "esbuild client", "esbuild server"],
  "assets.deploy": [
    "tailwind live_react_examples --minify",
    "esbuild client --minify",
    "esbuild server --minify",
    "phx.digest"
  ]
]
end

For deployment follow the SSR deployment guide