README.md

# LiveCapture

Increase UI quality of your product by capturing visual states of LiveView components.

## 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 into `mix.exs`.

```elixir
{:live_capture, "~> 0.1"}
```

Configure responsive breakpoints and source applications in `config.exs`.<br/>
Replace `:my_app` with an app name from `mix.exs`.

```elixir
config :live_capture,
  apps: [:my_app],
  breakpoints: [sm: "640px", md: "768px", lg: "1024px", xl: "1280px", "2xl": "1536px"]
```

Mount LiveCapture in `router.ex`.<br/>
Replace `MyAppWeb.LayoutView` with your LiveView layout module.

```elixir
import LiveCapture.Router

scope "/" do
  live_capture("/live_capture", root_layout: {MyAppWeb.LayoutView, :root})
end
```

Capture your first component story

> [!TIP]
> You can place `use LiveCapture.Component` next to `use Phoenix.Component` in `my_app_web.ex`.
> This makes the `capture/0`, `capture/1`, and `capture_all()` macros available in all component files.

```elixir
use LiveCapture.Component

capture()

def my_component(assigns) do
  ~H"""
  My component
  """
end
```

Explore the main capture patterns in [example.ex](https://github.com/achempion/live_capture/blob/main/lib/live_capture/live/component/components/example.ex)

## Capture patterns

With `use LiveCapture.Component`, import three macros into 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

```elixir
attr :name, :string, default: "Main", examples: ["Primary", "Secondary"]

capture()

def my_component(assigns), do: ~H"My component: {@name}"
```

> [!TIP]
> Live capture supports two patterns for default attributes: `:default` and `:examples` (with `:examples` taking priority).

### Set attribute values with `capture/1`

`:attributes` key allows you to override any default values defined with `attr` macro

```elixir
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

```elixir
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

```elixir
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",
  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.

```elixir
defmodule MyAppWeb.Profile.ShowLive do
  use MyAppWeb, :live_view
  use LiveCapture.Component

  alias MyApp.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 definition of complex or recurring state values inside the factory module

```elixir
defmodule MyApp.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 declaration of the whole variant attributes payload insdie the factory.
You can define and arrange factory modules in a way that fits best your project structure.

```elixir
defmodule MyAppWeb.LiveCaptureWebFactory do
  alias MyAppWeb.Profile

  alias MyApp.LiveCaptureFactory

  def build(Profile.ShowLive, :main) do
    %{
      user: LiveCaptureFactory.build(:current_user)
    }
  end
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](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.