Subscribe to access all episodes. View plans →
Published May 13, 2024
Follow along with the episode starter on GitHub
Here we have a table that has a couple different Widgets for sale. Our application is using Phoenix 1.7 and the table is styled with some pretty basic TailwindCSS, which ships by default with new Phoenix 1.7 applications.
As we continue to build out our application, we don’t want to spend a lot of time designing and implementing components. And while Phoenix 1.7 does come with a CoreComponents
module, which helps you get started with components for things like modals, tables, and forms, let’s bring in a more comprehensive solution with Petal Components. Petal Components is a set of open source, HEEX components that make it easy to build beautiful web applications. They have great documentation and, while we cover them in episode 145 - that was with a Phoenix 1.6 application, which didn’t have the CoreComponents
module.
Let’s get started. The first thing we’ll need to do is go to Hex and copy the petal_components
config. We’ll then open our proejct’s Mixfile and add it to our list of dependencies.
mix.exs
...
defp deps do
[
...
{:petal_components, "~> 1.9"},
...
]
end
...
Then we’ll go to the command line and get our new dependency with mix deps.get
.
$ mix deps.get
...
New:
petal_components 1.9.2
Great, now we need to configure our application to use petal_components
. Let’s open our tailwind.config.js
file and we need to configure the path to our petal components folder in the content
section, so let’s do that.
Then let’s update the color palette for our application. You can choose different colors, but I’ll paste in a few different ones from TailwindCSS’s default color palette. To use these we’ll need to require them above.
assets/tailwind.config.js
const colors = require("tailwindcss/colors");
...
content: [
...
"../deps/petal_components/**/*.*ex",
],
...
colors: {
primary: colors.blue,
secondary: colors.pink,
success: colors.green,
danger: colors.red,
warning: colors.yellow,
info: colors.sky,
gray: colors.gray,
}
...
Great, now let’s open our app.css
and import the petal components stylesheet.
assets/css/app.css
...
@import "../../deps/petal_components/assets/default.css";
...
Now we need to import the components. Let’s open the teacher_web.ex
module. And in the html_helpers
function we’ll add use PetalComponents
.
lib/teacher_web.ex
...
defp html_helpers do
quote do
...
use PetalComponents
end
end
...
With that let’s start up our server.
$ mix phx.server
...
And when we do we get an error. This is because there’s a naming conflict - both Petal Components and the Phoenix generated CoreComponents
module implement components with the same name.
There are a couple ways to fix this. We could delete the core_components.ex
module here, but then generators like mix phx.gen.live
won’t work properly. We could also go back to our teacher_web.ex
module and if we don’t want to use all of the Petal Components we could update this it to only include the ones we want.
lib/teacher_web.ex
...
defp html_helpers do
quote do
...
import PetalComponents.{Accordion, Icon}
end
end
...
While this works, I’d like our application here to have the option of using any of the Petal Components or the CoreComponents
. To do that let’s namespace our Petal Component modules. We’ll define a module PC
, and then by using defdelegate
we can essentially forward functions in our PC
module to their corresponding PetalComponent
function.
Here we’re delegating the accordion
function in our PC
module to the accordion
function in the PetalComponents.Accordion
module. I’ll go ahead and paste in the rest of the components. Great, now we can use any of the Petal Components via our PC
module.
lib/teacher_web.ex
...
defp html_helpers do
quote do
...
defmodule PC do
defdelegate accordion(assigns), to: PetalComponents.Accordion
defdelegate alert(assigns), to: PetalComponents.Alert
defdelegate avatar(assigns), to: PetalComponents.Avatar
defdelegate badge(assigns), to: PetalComponents.Badge
defdelegate breadcrumbs(assigns), to: PetalComponents.Breadcrumbs
defdelegate button(assigns), to: PetalComponents.Button
defdelegate icon_button(assigns), to: PetalComponents.Button
defdelegate card(assigns), to: PetalComponents.Card
defdelegate container(assigns), to: PetalComponents.Container
defdelegate dropdown(assigns), to: PetalComponents.Dropdown
defdelegate form_label(assigns), to: PetalComponents.Form
defdelegate field(assigns), to: PetalComponents.Field
defdelegate icon(assigns), to: PetalComponents.Icon
defdelegate input(assigns), to: PetalComponents.Input
defdelegate a(assigns), to: PetalComponents.Link
defdelegate spinner(assigns), to: PetalComponents.Loading
defdelegate modal(assigns), to: PetalComponents.Modal
defdelegate pagination(assigns), to: PetalComponents.Pagination
defdelegate progress(assigns), to: PetalComponents.Progress
defdelegate rating(assigns), to: PetalComponents.Rating
defdelegate slide_over(assigns), to: PetalComponents.SlideOver
defdelegate table(assigns), to: PetalComponents.Table
defdelegate td(assigns), to: PetalComponents.Table
defdelegate tr(assigns), to: PetalComponents.Table
defdelegate th(assigns), to: PetalComponents.Table
defdelegate tabs(assigns), to: PetalComponents.Tabs
defdelegate h1(assigns), to: PetalComponents.Typography
defdelegate h2(assigns), to: PetalComponents.Typography
defdelegate h3(assigns), to: PetalComponents.Typography
defdelegate h4(assigns), to: PetalComponents.Typography
defdelegate h5(assigns), to: PetalComponents.Typography
defdelegate p(assigns), to: PetalComponents.Typography
defdelegate prose(assigns), to: PetalComponents.Typography
defdelegate ol(assigns), to: PetalComponents.Typography
defdelegate ul(assigns), to: PetalComponents.Typography
end
end
end
...
All we need to do now is update our Widget table to use the Petal Components. Let’s open the widget_live/index.html.heex
template and first let’s update our h1
to use a component. If we hadn’t used the PC
module namespace, our component would look like this <.h1>...</.h1>
. To use the component we’ll just need to prefix it with the PC
module namespace like this: <PC.h1></PC.h1>
. Then let’s do the same for our table.
Now for our link here, let’s actually change this to be a button component. If we look at the Petal Components docs, we see the different options we can use with the <.button>
component.
We’ll update this to be a button component. Let’s use a link_type="live_redirect"
and update navigate
to to
.
Template path: lib/teacher_web/live/widget_live/index.html.heex
<PC.h1>Widget Shop</PC.h1>
<PC.table rows={@widgets} id="widgets-table">
<:col :let={widget} label="Name">
<%= widget.name %>
</:col>
<:col :let={widget}>
<PC.button link_type="live_redirect" to={~p"/widgets/#{widget}"}>Show</PC.button>
</:col>
</PC.table>
With those changes done let’s start up the server.
$ mix phx.server
...
Great! There aren’t any errors, so let’s go back to the browser. And perfect - we see our page is now being styled with Petal Components and our button component works. Our application is now configured to use Petal Components.