Phoenix LiveView Project Structure
LiveView-centric architecture for real-time UIs. Rich interactivity without client-side JavaScript.
Project Directory
my_app/
mix.exs
mix.lock
.formatter.exs
.gitignore
config/
config.exs
dev.exs
prod.exs
test.exs
runtime.exs
lib/
my_app/
Business logic
application.ex
repo.ex
accounts/
accounts.ex
user.ex
catalog/
Example domain context
catalog.ex
product.ex
my_app_web/
endpoint.ex
router.ex
telemetry.ex
live/
LiveView modules
product_live/
Feature-scoped LiveViews
index.ex
List view
show.ex
Detail view
form_component.ex
Reusable form
user_live/
settings.ex
profile.ex
components/
core_components.ex
Buttons, forms, modals
layouts.ex
layouts/
app.html.heex
root.html.heex
controllers/
Non-LiveView routes
page_controller.ex
page_html.ex
priv/
repo/
migrations/
seeds.exs
static/
assets/
js/
app.js
LiveView hooks
hooks/
JS interop
index.js
css/
tailwind.config.js
test/
test_helper.exs
my_app/
catalog_test.exs
my_app_web/
live/
product_live_test.exs
support/
conn_case.ex
data_case.ex
Why This Structure?
LiveView lets you build rich, real-time UIs entirely in Elixir. State lives on the server, changes push over WebSocket, and you write zero JavaScript for most features. The live/ directory organizes LiveViews by feature, each with its own folder for related views and components.
Key Directories
- lib/my_app_web/live/-LiveView modules organized by feature
- live/{feature}/form_component.ex-Reusable LiveComponents
- assets/js/hooks/-JavaScript hooks for client-side interop
- components/core_components.ex-Shared UI components
LiveView Example
defmodule MyAppWeb.ProductLive.Index do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
{:ok, assign(socket, products: Catalog.list_products())}
end
def handle_event("delete", %{"id" => id}, socket) do
Catalog.delete_product!(id)
{:noreply, assign(socket, products: Catalog.list_products())}
end
end
Getting Started
mix phx.new my_app --livecd my_app && mix deps.getmix ecto.createmix phx.gen.live Catalog Product products name:string price:decimalmix phx.server
When To Use This
- Interactive dashboards and admin panels
- Real-time collaborative features
- Forms with instant validation
- Live search and filtering
- Apps where you want to minimize JavaScript
Trade-offs
- Server state-Each user connection holds state in memory
- Latency sensitive-Round-trip to server for every interaction
- JS still needed-Complex animations or offline require hooks
Best Practices
- Use
phx.gen.liveto scaffold CRUD LiveViews - Extract shared UI into
core_components.ex - Keep LiveView modules focused—one per page
- Use
assign_async/3for expensive data fetches - Handle
handle_params/3for URL-driven state