Properties

You can pass information from a parent component down to a child using properties.

In order to declare a property, you must use the prop macro:

prop name, type, options

Where:

  • name - is the name of the property.
  • type - an atom defining the type of the property. See all available types in section "Components Basics > Types available".
  • options - a keyword list of options for additional customization.

Supported options

  • required - declares the property as required. Default is false.
  • default - defines a default value for an optional property.
  • values - the list or range of values suggested for the property.
  • values! - the list or range of all possible values for the property. Unlike values, this option enforces validation of the default value against the given list.
  • from_context - if the prop is not passed or the value is nil, sets the value to the one stored in the context under the given key.
  • accumulate - instructs Surface to group all different values provided for that property into a single list. Default is false, i.e. only the last value is passed.
  • css_variant - Tailwind only. If set to true, instructs the Surface compiler to generate CSS variants based on the related assign's type and value. Default is false. For further information, see "Generating CSS variants ".
Hello, John Doe!
# Defining the component

defmodule Hello do
  use Surface.Component

  @doc "Someone to say hello to"
  prop name, :string, required: true

  def render(assigns) do
    ~F"""
    Hello, {@name}!
    """
  end
end

# Using the component

defmodule Example do
  use Surface.Component

  def render(assigns) do
    ~F"""
    <Hello name="John Doe" />
    """
  end
end

CSS class property

In order to avoid working with string concatenation, which is annoying and error-prone, Surface allows passing keyword lists directly to the class property and improves developer experience by automatically handling conditional classes. Let's see how it works.

Imagine you want to create a button component that sets CSS classes based on the following rules:

  • button - always set
  • is-info - always set
  • is-loading - set if @loading is truthy
  • is-rounded - set if @rounded is truthy

We can define our component like this:

defmodule MyButton do
  use Surface.Component

  prop loading, :boolean
  prop rounded, :boolean

  slot default

  def render(assigns) do
    ~F"""
    <button class={"button", "is-info", "is-loading": @loading, "is-rounded": @rounded}>
      <#slot />
    </button>
    """
  end
end

Let's try it out.

<MyButton loading={@loading} rounded={@rounded}>
  Change my style!
</MyButton>

Note: For regular HTML tags like <button>, the class attribute will be handled automatically as expected. For custom components, you need to instruct Surface to do so by setting the type of the property as :css_class.

Event property

In order to declare an event property, you must use the prop macro and define the type as :event:

prop name, :event, options

Where:

  • name - is the name of the event.
  • options - a keyword list of options for additional customization.

Supported options

  • required - declares the event as required. Default is false.
  • default - defines a default value for an optional event.

Example:

defmodule MyButton do
  use Surface.Component

  @doc "Triggers on click"
  prop click, :event

  slot default

  def render(assigns) do
    ~F"""
    <button class="button" :on-click={@click}>
      <#slot />
    </button>
    """
  end
end