Components
Components are the core building blocks of LiveCable applications. They encapsulate server-side state, behavior, and rendering logic.
Component Structure
A LiveCable component consists of two parts:
- Component Class - Ruby class that defines state and behavior
- Component View - ERB template that renders the component
Creating Components
Component Class (app/live/todo_list.rb):
module Live
class TodoList < LiveCable::Component
reactive :todos, -> { [] }
reactive :new_todo, -> { "" }
actions :add_todo, :remove_todo, :toggle_todo
def add_todo(params)
return if params[:text].blank?
todos << {
id: SecureRandom.uuid,
text: params[:text],
completed: false
}
self.new_todo = ""
end
def remove_todo(params)
todos.reject! { |todo| todo[:id] == params[:id] }
end
def toggle_todo(params)
todo = todos.find { |t| t[:id] == params[:id] }
todo[:completed] = !todo[:completed] if todo
end
end
endComponent View (app/views/live/todo_list.html.live.erb):
<div class="todo-list">
<h2>My Todos</h2>
<form live-form="add_todo">
<input type="text" name="text" value="<%= new_todo %>" placeholder="What needs to be done?">
<button type="submit">Add</button>
</form>
<ul>
<% todos.each do |todo| %>
<li live-key="<%= todo[:id] %>">
<input type="checkbox"
<%= 'checked' if todo[:completed] %>
live-action="change->toggle_todo"
live-value-id="<%= todo[:id] %>">
<span class="<%= 'completed' if todo[:completed] %>">
<%= todo[:text] %>
</span>
<button live-action="remove_todo"
live-value-id="<%= todo[:id] %>">
Delete
</button>
</li>
<% end %>
</ul>
</div>Component Locations
Component Namespacing
All LiveCable components must be defined inside the Live:: module so they can be safely loaded from a string name.
- Component classes go in
app/live/(e.g.,Live::Counter→app/live/counter.rb) - Views go in
app/views/live/(e.g.,app/views/live/counter.html.live.erb) - For namespaced components:
Live::Chat::Message→app/live/chat/message.rbandapp/views/live/chat/message.html.live.erb
Rendering Components
Basic Rendering
Use the live helper to render a component:
<%# Simple usage %>
<%= live('counter', id: 'my-counter') %>
<%# With default values %>
<%= live('counter', id: 'my-counter', count: 10) %>Component IDs
Each component instance needs a unique ID. The ID is used to:
- Identify the component's ActionCable stream
- Maintain WebSocket connections across Stimulus reconnects
<%# Unique IDs for multiple instances %>
<%= live('counter', id: 'header-counter') %>
<%= live('counter', id: 'sidebar-counter') %>Rendering Component Instances Directly
If you already have a component instance, use render directly:
<%
@counter = Live::Counter.new('my-counter', count: 10)
%>
<%= render(@counter) %>Component Templates
Component templates must start with a root element — LiveCable raises an error at render time if no opening tag is found. It automatically injects the required attributes into that root element:
<div>
<!-- Your component HTML here -->
</div>The root element automatically receives:
live-id- The component's unique IDlive-component- The component class stringlive-actions- JSON array of whitelisted actionslive-defaults- JSON object of default values (first render only)
You can add any HTML attributes to your root element:
<%# With CSS classes %>
<div class="p-4 bg-white rounded-lg shadow">
<h2>Counter: <%= count %></h2>
</div>
<%# With additional Stimulus controllers %>
<div data-controller="dropdown">
<%# This will have data-controller="live dropdown" after transformation %>
</div>
<%# With any HTML attributes %>
<div id="my-counter" class="flex items-center"
aria-label="Counter widget">
<h2>Counter: <%= count %></h2>
</div>Automatic Controller Addition
The LiveCable Stimulus controller is automatically added to the root element. If you specify data-controller="dropdown", it becomes data-controller="live dropdown".