Build your own Renderables or Components

A reusable Renderable or Component can be exported as either a constant value or a function.

const Logo = html.div(attr.class('logo'), html.img(attr.src('logo.png')))

In this example, Logo is a reusable component. The content of the component remains the same; it doesn’t change. However, it is still reusable because it doesn’t generate a new DOM unless it is rendered. It can be applied multiple times in different locations, making it versatile across applications.

Using a capitalized name for your component is a common convention to indicate that it is a component.

You can create a component that is still constant and reusable but has dynamic content.

const UserView = Async(fetchUser(), (user) => html.div(user.name))

The rendered content of the component is dynamic and depends on the result of the fetchUser() promise but the promise is executed only once for any instantiation of the component.

Props

More commonly, you'll see components that take a set of props as argument. In this case the component is a function that returns a Renderable.

const UserView = (user: User) => html.div(user.name)

This is a valid component but it is not often what you want in the context of Tempo because it does not allow to update the DOM when the props change. Each application of this component will result in a DOM state that will not change unless the component is re-rendered.

To address this, you want to use Signals instead.

const UserView = (user: Signal<User>) => html.div(user.$.name)

Now, any time the user signal changes, the DOM will be updated. Notice that the $ property is used to access the value of a field wrapped in the signal. It is convenient and type-safe. The equivalent method is user.at('name').

To get the best of both worlds, you can use Value.

const UserView = (user: Value<User>) => html.div(Value.map(user, (user) => user.name))

Value is a union type that can be either a Signal or a T. It is a convenient way to work with signals and values in the same way. Value.map is used to map a Value to a new Value.

Most Renderables in Tempo accept Value instead of Signal or T.

If you want to create components that take multiple arguments or options, you can shape them the way you want.

Next Steps