Skip to content

andreogle/ElixirReactStarter

Repository files navigation

ElixirReactStarter

ElixirReactStarter

A production-ready Phoenix + Inertia.js (React, SSR) starter — authentication, real-time, i18n, theming, testing, CI, and Docker, already wired and tested.

CI

Why this exists

I want to build web apps using Phoenix. LiveView is great, but I'm familiar with and prefer the rich React ecosystem. In the past, I would have reached for a frontend framework (e.g. Next.js), with an Elixir backend API. This has numerous downsides, including having to manage and sync deployments, migrations, versions, additional infrastructure etc. all of which compound complexity exponentially. Inertia.js provides a way of acting as the glue between Phoenix and React (or Vue or Svelte), so you don't have to deal with the complexities of separate repos.

This template project serves as a starting point for a lot of my web applications, so that I don't have to duplicate a lot of the setup work. It's fully featured (see Features below). Feel free to use it however you want or submit a PR if you find some way of improving it.

It's provided as-is, with no guarantees or warranties of any kind — use it at your own risk.

Features

  • Authentication — email + password with link-based confirmation and password reset, Argon2 hashing, sliding sessions, account management, and no email enumeration.
  • Real-time — token-authenticated Phoenix Channels with React hooks that survive Inertia navigation.
  • Internationalizationgettext on the server and react-i18next on the client; English + Spanish with locale detection.
  • Theming — light / dark / system with no flash of the wrong theme.
  • AccessibilityRadix UI primitives, Biome a11y linting in CI, and axe-core auditing in dev.
  • EmailSwoosh (HTML + text) via Mailjet in production, with an in-browser mailbox in dev.
  • Security — rate-limited auth endpoints, Secure cookies, CSRF protection, and HTTPS/HSTS in production.
  • Background jobsOban, configured and ready.
  • TestingExUnit with a coverage gate, ex_machina factories, and a Playwright E2E suite.
  • Tooling & CImise-pinned toolchain, Biome / Credo / Dialyzer, and GitHub Actions.
  • Deployment — multi-stage Docker release with server-side rendering.
  • Documentationex_doc guides and module reference.

Tech stack

Getting started

Prerequisites: mise (pins the toolchain) and a running PostgreSQL.

Starting a new project from this template? Rename it to your own name first:

scripts/rename_project.sh YourAppName   # PascalCase

It rewrites every module, the OTP app name, file/directory paths, and configs (both PascalCase and snake_case forms), then rebuilds and runs mix precommit to verify. Use --dry-run to preview the changes first.

# 1. Install the pinned Erlang, Elixir, and Node (versions live in mise.toml)
mise trust && mise install

# 2. Install deps, create + migrate the database, build assets
mix setup

# 3. Start the server
mix phx.server   # or: iex -S mix phx.server

Visit localhost:4000. With shell activation enabled (eval "$(mise activate zsh)" in ~/.zshrc), mise switches to the project's toolchain automatically when you cd in.

Testing

mix test                       # unit + integration, with coverage gate (mix test --cover)
mix precommit                  # format, credo, biome, i18n, tests — run before every commit
npm --prefix assets run e2e    # Playwright E2E (needs a running server; see the guide)

See the End-to-End Testing guide for the Playwright setup.

Documentation

Searchable developer guides and the full module reference are generated with ex_doc:

mix docs

In development they're also served at /dev/docs. Topic guides live in docs/ — e.g. the End-to-End Testing guide.

Deployment

SECRET_KEY_BASE must be generated once and kept stable across restarts — a changing value invalidates every session and signed cookie. Generate it a single time with mix phx.gen.secret and store it in your secrets manager or host config, then build and run the release image (reading the stored values from the environment):

docker build -t elixir_react_starter .
docker run -p 4000:4000 \
  -e DATABASE_URL="ecto://USER:PASS@HOST/DB" \
  -e MAILJET_API_KEY="$MAILJET_API_KEY" \
  -e MAILJET_SECRET="$MAILJET_SECRET" \
  -e PHX_HOST="example.com" \
  -e SECRET_KEY_BASE="$SECRET_KEY_BASE" \
  elixir_react_starter

config/runtime.exs requires DATABASE_URL, MAILJET_API_KEY, MAILJET_SECRET, and SECRET_KEY_BASE and will refuse to boot without them. PHX_HOST defaults to example.com but should always be set in production (it's used for absolute URLs in emails). PORT (default 4000), POOL_SIZE, ECTO_IPV6, and DNS_CLUSTER_QUERY are optional.

Database migrations run via the release: bin/migrate. See Phoenix's deployment guides for hosting specifics.

Liveness probe

GET /health returns {"status":"ok"} — cheap, no database calls, no external lookups. The image's HEALTHCHECK polls it via the embedded Node binary, and it's a sensible target for Kubernetes liveness/readiness probes too.

Bumping Erlang / Elixir / Node

Runtime versions are pinned in two places that must agree: mise.toml (dev and CI toolchain, via mise) and the ARG declarations at the top of the Dockerfile (release build). mix precommit runs scripts/check-tool-versions.sh as its first step and will fail if those drift — so when bumping a runtime version, update both files in the same commit.

Conventions

Project architecture, invariants, and coding conventions are documented in CLAUDE.md — start there before making changes.

License

Released under the WTFPL (see the LICENSE file) — do what the fuck you want.

About

Production-ready Phoenix 1.8 starter: Inertia.js + React SSR frontend, email/password auth, realtime channels, i18n, and light/dark theming.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors