defmodule Phoenix.Component do @moduledoc ~S''' API for function components. A function component is any function that receives an assigns map as argument and returns a rendered struct built with [the `~H` sigil](`Phoenix.LiveView.Helpers.sigil_H/2`). Here is an example: defmodule MyComponent do use Phoenix.Component # Optionally also bring the HTML helpers # use Phoenix.HTML def greet(assigns) do ~H"""

Hello, <%= assigns.name %>

""" end end The component can be invoked as a regular function: MyComponent.greet(%{name: "Jane"}) But it is typically invoked using the function component syntax from the `~H` sigil: ~H""" """ If the `MyComponent` module is imported or if the function is defined locally, you can skip the module name: ~H""" <.greet name="Jane" /> """ Similar to any HTML tag inside the `~H` sigil, you can interpolate attributes values too: ~H""" <.greet name={@user.name} /> """ You can learn more about the `~H` sigil [in its documentation](`Phoenix.LiveView.Helpers.sigil_H/2`). ## `use Phoenix.Component` Modules that define function components should call `use Phoenix.Component` at the top. Doing so will import the functions from both `Phoenix.LiveView` and `Phoenix.LiveView.Helpers` modules. `Phoenix.LiveView` and `Phoenix.LiveComponent` automatically invoke `use Phoenix.Component` for you. You must avoid defining a module for each component. Instead, we should use modules to group side-by-side related function components. ## Assigns While inside a function component, you must use `Phoenix.LiveView.assign/3` and `Phoenix.LiveView.assign_new/3` to manipulate assigns, so that LiveView can track changes to the assigns values. For example, let's imagine a component that receives the first name and last name and must compute the name assign. One option would be: def show_name(assigns) do assigns = assign(assigns, :name, assigns.first_name <> assigns.last_name) ~H"""

Your name is: <%= @name %>

""" end However, when possible, it may be cleaner to break the logic over function calls instead of precomputed assigns: def show_name(assigns) do ~H"""

Your name is: <%= full_name(@first_name, @last_name) %>

""" end defp full_name(first_name, last_name), do: first_name <> last_name Another example is making an assign optional by providing a default value: def field_label(assigns) do assigns = assign_new(assigns, :help, fn -> nil end) ~H""" """ end ## Slots Slots is a mechanism to give HTML blocks to function components as in regular HTML tags. ### Default slots Any content you pass inside a component is assigned to a default slot called `@inner_block`. For example, imagine you want to create a button component like this: <.button> This renders inside the button! It is quite simple to do so. Simply define your component and call `render_slot(@inner_block)` where you want to inject the content: def button(assigns) do ~H""" """ end In a nutshell, the contents given to the component is assigned to the `@inner_block` assign and then we use `Phoenix.LiveView.Helpers.render_slot/2` to render it. You can even have the component give a value back to the caller, by using `let`. Imagine this component: def unordered_list(assigns) do ~H""" """ end And now you can invoke it as: <.unordered_list let={entry} entries={~w(apple banana cherry)}> I like <%= entry %> You can also pattern match the arguments provided to the render block. Let's make our `unordered_list` component fancier: def unordered_list(assigns) do ~H""" """ end And now we can invoke it like this: <.unordered_list let={%{entry: entry, gif_url: url}}> I like <%= entry %>. ### Named slots Besides `@inner_block`, it is also possible to pass named slots to the component. For example, imagine that you want to create a modal component. The modal component has a header, a footer, and the body of the modal, which we would use like this: <.modal> <:header> This is the top of the modal. This is the body - everything not in a named slot goes to @inner_block. <:footer> The component itself could be implemented like this: def modal(assigns) do ~H""" """ end If you want to make the `@header` and `@footer` optional, you can assign them a default of an empty list at the top: def modal(assigns) do assigns = assigns |> assign_new(:header, fn -> [] end) |> assign_new(:footer, fn -> [] end) ~H"""