Zero-boilerplate storybook for LiveView components.
Document, preview, and test LiveView components in isolation.
Explore component libraries · Documentation · Hex
Watch the talk from Copenhagen Elixir
Need help with LiveCapture integration or your LiveView project? Hire me
- Render HEEx components with predefined state snapshots
- Quickly test visual quality by switching between different width breakpoints
- Explore component documentation with dynamic state inspection
- Nice DSL with a strong focus on ergonomics and simplicity
Add the :live_capture dependency to the mix.exs and .formatter.exs.
mix.exs
{:live_capture, "~> 0.2"}.formatter.exs
[
import_deps: [:live_capture, ...]
]Define a configuration module.
defmodule MyAppWeb.LiveCapture do
use LiveCapture.Component
breakpoints s: "480px", m: "768px", l: "1279px", xl: "1600px"
root_layout {MyAppWeb.LayoutView, :root}
endMount LiveCapture in router.ex.
Tip
You can mount multiple configuration modules by passing them as a list instead.
import LiveCapture.Router
scope "/" do
live_capture "/live_capture", MyAppWeb.LiveCapture
endCapture your first component story.
Tip
You can place use MyAppWeb.LiveCapture next to use Phoenix.Component in my_app_web.ex.
This makes the capture/0, capture/1, and capture_all/0 macros available in all component files.
use MyAppWeb.LiveCapture
capture()
def my_component(assigns) do
~H"""
My component
"""
endExplore the main capture patterns in example.ex
Calling use MyAppWeb.LiveCapture makes three macros available inside your module:
capture/0to simply capture a componentcapture/1to capture a component with attributes and state variantscapture_all/0to automatically capture all HEEx components inside the file
If you have a component defined with default attributes, you can render it "as is" with a capture/0 call.
Tip
LiveCapture supports two patterns for default attributes: :default and :examples (with :examples taking priority).
attr :name, :string, default: "Main", examples: ["Primary", "Secondary"]
capture()
def my_component(assigns), do: ~H"My component: {@name}":attributes key allows you to override any default values defined with attr macro
attr :name, :string, required: true
capture attributes: %{name: "Main"}
def my_component(assigns), do: ~H"My component: {@name}":variants key allows you to define multiple component state snapshots
attr :name, :string, required: true
capture variants: [
main: %{name: "Main"},
secondary: %{name: "Secondary"},
]
def my_component(assigns), do: ~H"My component: {@name}"Tip
All :inner_block strings are treated as regular HEEx templates.
slot :header
slot :inner_block, required: true
slot :rows do
attr :name, :string
end
capture attributes: %{
header: "This is header slot",
inner_block: "Content of the inner block {1+2}",
rows: [
%{inner_block: "Slot content", name: "Attribute content"}
]
}
def my_component(assigns), do: ~H"..."LiveCapture makes it possible to render live components and capture a visual snapshot of the render/1 function used by LiveView dynamic components.
defmodule MyAppWeb.Profile.ShowLive do
use MyAppWeb, :live_view
use MyAppWeb.LiveCapture
alias MyAppWeb.LiveCaptureFactory
def mount(_, _, socket), do: {:ok, socket}
capture attributes: %{
current_user: LiveCaptureFactory.build(:current_user)
}
def render(assigns) do
~H"""
Example of a large HEEx component with a complex state structure
"""
end
endTo declutter the component code, you can move the definition of complex or recurring state values inside the factory module
defmodule MyAppWeb.LiveCaptureFactory do
alias MyApp.Users
def build(:current_user) do
%Users.User{id: 1, name: "First Last"}
end
endIt's also possible to move the declaration of the whole variant attributes payload inside the factory. You can define and arrange factory modules in a way that fits your project structure best.
defmodule MyAppWeb.LiveCaptureWebFactory do
alias MyAppWeb.Profile
alias MyAppWeb.LiveCaptureFactory
def build(Profile.ShowLive, :main) do
%{
user: LiveCaptureFactory.build(:current_user)
}
end
endSome components render inline scripts and styles that might require a nonce attribute.
def style_nonce(nonces), do: nonces.csp_style_nonce
def script_nonce(nonces), do: nonces.csp_script_nonce
capture attributes: %{
style_nonce: LiveCapture.Attribute.with_csp_nonces(&__MODULE__.style_nonce/1),
script_nonce: LiveCapture.Attribute.with_csp_nonces(&__MODULE__.script_nonce/1)
}
def my_component(assigns), do: ~H"..."Update live_capture/3 with conn assign keys of CSP nonces.
live_capture "/live_capture",
MyAppWeb.LiveCapture,
csp_nonce_assign_key: %{
style: :style_csp_nonce,
script: :script_csp_nonce
}The plugs option can configure a list of plugs that will be called during the component render.
defmodule MyAppWeb.LiveCapture do
use LiveCapture.Component
plugs [MyAppWeb.CustomPlug, {MyAppWeb.CustomPlugWithConfig, key: "value"}]
endCopyright (c) 2026 Boris Kuznetsov me@achempion.com
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.