# Phlex.Ex
[](https://hex.pm/packages/phlex) [](https://hex.pm/packages/phlex) [](https://github.com/amkisko/phlex.ex/actions/workflows/test.yml)
Object-oriented web views in Elixir. Build HTML, SVG, and other markup views using Elixir's functional programming paradigm with component-based architecture.
**Migrating from the Ruby version?** This Elixir implementation follows the same philosophy as [Phlex](https://www.phlex.fun) but embraces Elixir's functional nature with explicit state passing.
## Features
- **Component-based architecture** - Build reusable HTML and SVG components
- **Type-safe** - Dialyzer support for type checking
- **Phoenix integration** - Works seamlessly with Phoenix and Phoenix LiveView
- **Standalone usage** - Can be used without Phoenix in plain Elixir applications
- **StyleCapsule integration** - Optional CSS scoping support via `style_capsule` package
- **Modern Elixir** - Uses Elixir 1.18+ features and patterns
## Installation
Add `phlex` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:phlex, "~> 0.1.0"}
]
end
```
For Phoenix integration, also ensure you have:
```elixir
{:phoenix, "~> 1.7"},
{:phoenix_live_view, "~> 0.20"}
```
For CSS scoping support, consider adding `style_capsule`:
```elixir
{:style_capsule, "~> 0.7.0"}
```
## Quick Start
### 1. Create a Component
```elixir
defmodule MyAppWeb.Components.Card do
use Phlex.HTML
def view_template(assigns, state) do
div(state, [class: "card"], fn state ->
h1(state, [class: "title"], Map.get(assigns, :title, "Card"))
p(state, [], Map.get(assigns, :content, ""))
end)
end
end
```
### 2. Use in Phoenix LiveView
```elixir
defmodule MyAppWeb.PageLive do
use MyAppWeb, :live_view
def render(assigns) do
Phlex.Phoenix.to_rendered(
MyAppWeb.Components.Card.render(%{title: "Hello", content: "World"})
)
end
end
```
### 3. Optional: Add CSS Scoping with StyleCapsule
For component-scoped CSS, integrate with `style_capsule`:
```elixir
defmodule MyAppWeb.Components.Card do
use Phlex.HTML
@component_styles """
.card { padding: 1rem; border: 1px solid #ccc; }
.title { font-size: 1.5rem; font-weight: bold; }
"""
def view_template(assigns, state) do
# Register styles
capsule_id = Phlex.StyleCapsule.capsule_id(__MODULE__)
StyleCapsule.Phoenix.register_inline(@component_styles, capsule_id)
# Add capsule attribute
attrs = Phlex.StyleCapsule.add_capsule_attr([class: "card"], __MODULE__)
div(state, attrs, fn state ->
h1(state, [class: "title"], Map.get(assigns, :title, "Card"))
end)
end
end
```
Then render styles in your layout:
```heex
<body>
<%= @inner_content %>
<%= raw StyleCapsule.Phoenix.render_all_runtime_styles() %>
</body>
```
## Usage
Phlex components use explicit state passing, following Elixir's functional programming style. The `view_template/2` function receives assigns and a state struct, then returns the updated state.
### Basic HTML Component
```elixir
defmodule MyApp.Components.Card do
use Phlex.HTML
def view_template(assigns, state) do
div(state, [class: "card"], fn state ->
h1(state, [class: "title"], Map.get(assigns, :title, "Default"))
p(state, [], Map.get(assigns, :content, ""))
end)
end
end
MyApp.Components.Card.render(%{title: "Hello", content: "World"})
# => "<div class=\"card\"><h1 class=\"title\">Hello</h1><p>World</p></div>"
```
### SVG Component
```elixir
defmodule MyApp.Components.Icon do
use Phlex.SVG
def view_template(_assigns, state) do
svg(state, [viewBox: "0 0 24 24"], fn state ->
circle(state, [cx: "12", cy: "12", r: "10"])
end)
end
end
```
### Phoenix LiveView Integration
When using Phlex components in LiveView, convert the HTML string to a `Phoenix.LiveView.Rendered` struct:
```elixir
defmodule MyAppWeb.PageLive do
use MyAppWeb, :live_view
def render(assigns) do
Phlex.Phoenix.to_rendered(
MyAppWeb.Components.Card.render(assigns)
)
end
end
```
### StyleCapsule Integration
For component-scoped CSS, Phlex provides helpers through `Phlex.StyleCapsule`. Register your styles and add capsule attributes to scope your CSS automatically:
```elixir
defmodule MyAppWeb.Components.Card do
use Phlex.HTML
@component_styles """
.card { padding: 1rem; }
.title { font-weight: bold; }
"""
def view_template(assigns, state) do
# Register styles with StyleCapsule
capsule_id = Phlex.StyleCapsule.capsule_id(__MODULE__)
StyleCapsule.Phoenix.register_inline(@component_styles, capsule_id, namespace: :app)
# Add data-capsule attribute
attrs = Phlex.StyleCapsule.add_capsule_attr([class: "card"], __MODULE__)
div(state, attrs, fn state ->
h1(state, [class: "title"], Map.get(assigns, :title, "Card"))
end)
end
end
```
Alternatively, use `StyleCapsule.PhlexComponent` for automatic integration:
```elixir
defmodule MyAppWeb.Components.Card do
use StyleCapsule.PhlexComponent
@component_styles """
.card { padding: 1rem; }
.title { font-weight: bold; }
"""
defp render_template(assigns, attrs, state) do
div(state, attrs, fn state ->
h1(state, [class: "title"], Map.get(assigns, :title, "Card"))
end)
end
end
```
## Example Applications
A complete Phoenix example application is available in `examples/phoenix_demo/`. It demonstrates Phlex components, Phoenix LiveView integration, and StyleCapsule CSS scoping.
To run the example:
```bash
cd examples/phoenix_demo
mix deps.get
mix phx.server
```
Then visit http://localhost:4000 to see Phlex in action.
## Development
```bash
# Install dependencies
mix deps.get
# Run tests
mix test
# Run all quality checks
mix quality
# Format code
mix format
# Run code analysis
mix credo --strict
# Run type checking
mix dialyzer
```
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/amkisko/phlex.ex
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
## Requirements
- Elixir >= 1.18
- Phoenix >= 1.7 (optional, for Phoenix integration)
- Phoenix LiveView >= 0.20 (optional, for LiveView integration)
- StyleCapsule >= 0.7.0 (optional, for CSS scoping support)
## Thanks
This project was inspired by and references several excellent projects:
- **[Phlex](https://www.phlex.fun)** ([GitHub](https://github.com/yippee-fun/phlex)) - The original Ruby gem for building object-oriented web views that inspired this Elixir implementation
- **[Phoenix Framework](https://www.phoenixframework.org)** - The web framework that makes building real-time applications in Elixir a joy
- **[Surface](https://surface-ui.org)** ([GitHub](https://github.com/surface-ui/surface)) - A component-based library for Phoenix that provided architectural inspiration
## License
The library is available as open source under the terms of the [MIT License](LICENSE.md).