Slots are placeholders declared by a component that you can fill up with custom content.
In order to declare a slot, you must use the
slot name, options
name- is the name of the slot.
options- a keyword list of options for additional customization.
required- declares the slot as required. Default is
props- the list of custom properties that should be passed to the associated slotable content.
as- defines the slot assign name. Useful if you want to reuse the slot name as a prop.
Rendering content with
Slots are similar to properties as they are exposed as part of the component's public API. The main difference is that while properties are passed as attributes, slots are injected inside the component's body.
Sometimes it’s useful to specify a fallback content that should be rendered when no content is provided for a slot.
By defining any children inside
<slot>...</slot>, that content becomes the default content.
Slots defined using
<slot/> are automatically registered into the component's metadata.
That means you don't have to explicitly declare them using the
slot function. However,
since a slot is also part of the public API, it's advisable to declare it
so you can add proper documentation to it. Additionally, if you want Surface to statically
validate required slots and slot props, you also need to declare them.
The following updated version of the
Hero component explicitly declares the default slot.
Now, if the user tries to use a
Hero without defining any content, a proper
missing required slot "default" error will be raised at compile-time.
In the previous example, we defined a component with a single default slot. But what
if you need to define multiple slots? A classical example of such requirement is the
component. A card usually has three distinct areas, a header, a footer and the
In order to create a component that can represent a card, we need to use named slots. Let's take a look at how it works.
And finally our
Card component defining all three slots:
Pay attention that defining a
<slot/> without a name is the same as defining it as
Instead of using
<template slot="...">, you might want to define a custom component to
hold the slot's content. In our case, we can define a
<Footer> and a
component, setting the
:slot option as the name of the slot in the parent card.
To use them, we don't have to change anything in the
Card component. We just need to replace each
with the appropriate
There are cases when it's necessary to pass information from the child's scope to the corresponding slot content that is being injected by the parent. Using slot props, Surface gives you an extra layer of encapsulation as it allows you to expose only the pieces of data that the parent needs, keeping everything else in the child's scope private to the parent.
Imagine you're developing a new component that you need to show some ratings.
It should provide predefined buttons to increment/decrement its value but you want
to make the rendering of the value itself customizable so you can, let's say, show
it as a number in one page and as a list of stars in another. You also want to
define a property for the
Let's see the code:
Now let's create two instances of our
Rating component, each one rendering its
There are cases when you don't need to render any of the children of a specific component. You just want to use them as a list of values that can be retrieved so you can provide a more declarative way to configure that component.
Imagine you want to define a
Grid component but instead of defining a property to pass
the columns definitions, you want to extract that information directly from the component's body.
In order to achieve that, you can define a
Column component and use the
:slot option to
inform that any instance will be bound to a parent slot.
By doing that, the component will no longer be rendered automatically. The list of children belonging to the same slot will be grouped and become available to the parent as an assign. The parent then decides what should be done with each individual group (slot).
Here's an example:
|The Dark Side of the Moon||Pink Floyd||March 1, 1973|
|OK Computer||Radiohead||June 16, 1997|
|Disraeli Gears||Cream||November 2, 1967|
|Physical Graffiti||Led Zeppelin||February 24, 1975|
Here are the
By defining a named slot
cols, we instruct Surface to create a new assign named
@cols that will hold a list containing all children that belong to the slot
Note: As you can see, the
Columncomponent does not render anything. It just holds the provided values for its properties. All the rendering is done by the parent
Binding slot props to generators
Imagine that instead of passing the field related to the column, you want to define some markup that should be rendered for each column. This would give us much more flexibility to render the items. Here's an example of what we could do.
Notice that we're not passing a regular list to the property
items anymore, instead, we are
passing a generator that defines a variable called
album. That variable will hold
the value of each item in the list that will be passed back to the column's scope by the
Note: Currently, Surface only support generators defining a single variable. Optional filter expressions are also supported. The concept of generators and filters is the same used by comprehensions in Elixir. For more information, see the section Generators and filters in the Elixir official documentation.
Here's the updated version of the
Let's take a closer look at the two important changes we made in our
colsslot now declares a slot prop
itembut instead of just defining the name of the prop (as we did for our
Ratingcomponent), we bound the value of that prop to each value (item) produced by the generator
<slot>to render each column's content passing the current item.