The LiveView socket for Phoenix Endpoints.
Overview
The Phoenix.LiveView.Socket module provides the Phoenix Socket implementation for LiveView. It can be mounted directly in your endpoint or extended to create custom user sockets with additional channels.
Socket Configuration
Basic Setup
Mount the socket directly in your endpoint:
socket "/live", Phoenix.LiveView.Socket,
websocket: [connect_info: [session: @session_options]]
Custom User Socket
To share a transport connection between regular Phoenix channels and LiveView:
defmodule MyAppWeb.UserSocket do
use Phoenix.LiveView.Socket
# Define your channel routes
channel "room:*", MyAppWeb.RoomChannel
channel "user:*", MyAppWeb.UserChannel
# Optional: Override connect/3 for custom logic
def connect(params, socket, connect_info) do
# Custom authentication or setup
{:ok, assign(socket, :user_id, params["user_id"])}
end
# Optional: Override id/1 for custom socket identification
def id(socket) do
"user_socket:#{socket.assigns.user_id}"
end
end
Then mount your custom socket in the endpoint:
socket "/live", MyAppWeb.UserSocket,
websocket: [connect_info: [session: @session_options]]
Runtime Configuration
If you need session options to be set at runtime, use an MFA tuple:
socket "/live", MyAppWeb.UserSocket,
websocket: [connect_info: [session: {__MODULE__, :runtime_opts, []}]]
def runtime_opts() do
Keyword.put(@session_options, :domain, host())
end
Socket Struct
The LiveView socket has the following structure:
Fields
The Phoenix endpoint module
The current LiveView module
The PID of the parent LiveView, if any
The PID of the root LiveView process
The socket assigns containing application data
Private socket data not sent to the client
Redirect information if the socket has been redirected
host_uri
URI.t() | :not_mounted_at_router
The host URI or indicator that view is not mounted at router
The PID of the transport process
Whether the socket is sticky (used for stateful components)
Channels
The socket automatically defines two channel routes:
"lv:*" - Phoenix.LiveView.Channel (LiveView connections)
"lvu:*" - Phoenix.LiveView.UploadChannel (File upload connections)
Callbacks
connect/3
Called when the socket connects.
connect(params, socket, connect_info)
Connection parameters from the client
socket
Phoenix.Socket.t()
required
The socket struct
Additional connection information (session, peer data, etc.)
result
{:ok, Phoenix.Socket.t()} | :error
Returns {:ok, socket} to accept the connection or :error to reject it
Default implementation stores connect_info in socket private:
def connect(_params, socket, connect_info) do
{:ok, put_in(socket.private[:connect_info], connect_info)}
end
id/1
Returns the socket identifier for the connection.
socket
Phoenix.Socket.t()
required
The socket struct
The socket ID used for presence tracking and socket management
Default implementation returns the live_socket_id from session:
def id(socket) do
socket.private.connect_info[:session]["live_socket_id"]
end
Using the Socket Module
When you use Phoenix.LiveView.Socket, the following are automatically added:
use Phoenix.Socket - Base Phoenix Socket behavior
- Channel definitions for LiveView and Upload channels
- Default
connect/3 callback (can be overridden)
- Default
id/1 callback (can be overridden)
Example Custom Socket
defmodule MyAppWeb.UserSocket do
use Phoenix.LiveView.Socket
# Add custom channels
channel "notifications:*", MyAppWeb.NotificationChannel
# Custom connect logic
def connect(params, socket, connect_info) do
case authenticate(params, connect_info) do
{:ok, user_id} ->
{:ok, assign(socket, :user_id, user_id)}
:error ->
:error
end
end
# Custom socket identification
def id(socket) do
"user_socket:#{socket.assigns.user_id}"
end
defp authenticate(params, connect_info) do
# Custom authentication logic
# ...
end
end
Type Definitions
t()
The socket struct type:
@type t :: %Phoenix.LiveView.Socket{
id: binary(),
endpoint: module(),
view: module(),
parent_pid: nil | pid(),
root_pid: pid(),
router: module(),
assigns: assigns(),
private: map(),
redirected: nil | tuple(),
host_uri: URI.t() | :not_mounted_at_router,
transport_pid: pid() | nil,
sticky?: boolean()
}
assigns()
The assigns type:
@type assigns :: map() | assigns_not_in_socket()