LiveCapture

View Source

<img align="right" width="96" height="96"

 alt="LiveCapture logo"
 src="images/logo-256.svg">

Zero-boilerplate storybook for LiveView components

Explore component libraries · Documentation · Hex


Need help with LiveCapture integration or your LiveView project? Hire me


Features

  • 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

Quick start

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}
end

Mount 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
end

Capture 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
  """
end

Explore the main capture patterns in example.ex

Capture patterns

Calling use MyAppWeb.LiveCapture makes three macros available inside your module:

  • capture/0 to simply capture a component
  • capture/1 to capture a component with attributes and state variants
  • capture_all/0 to automatically capture all HEEx components inside the file

Simple capture with capture/0

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}"

Set attribute values with capture/1

: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}"

Multiple variants of the same component

: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}"

Components with slots

[!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"..."

Large components with a complex state structure

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
end

To 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
end

It'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
end

Components with scripts and styles that require CSP nonce attribute

Some 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
             }

Root Layouts with custom conn assigns

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"}]
end

License

Copyright (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.