Skip to content

Components

hop implements a strongly typed component system.

Defining and using components

Components are defined by placing them at the outermost scope of a .hop file. To render a component inside another component, simply refer to it by name.

hop
// 1. Define a component called Greeting
<Greeting {subject: String}>
  <div>Hello {subject}!</div>
</Greeting>

// 2. Define another component
<AnotherComponent>
  // 3. Render the Greeting component
  <Greeting {subject: "world"} />
</AnotherComponent>

Importing components

In hop, every .hop file corresponds to a module.

The import declaration imports a component from another module.

hop
import user_list::{
  UserList,
}

<Index>
  <UserList {users: ["jane", "tobi"]} />
</Index>

All components are visible to other modules by default. To disallow other modules from importing a component, prefix it with Internal, e.g. <InternalButton>.

Styling components

hop uses Tailwind 4 for styling. The hop compiler compiles Tailwind to CSS natively, so no external dependencies need to be installed.

To add CSS classes to an HTML element, use the class attribute.

hop
<Component {css: String}>
  <div class="p-2 bg-green-300">
    // ...
  </div>
</Component>

If you have multiple CSS classes it is idiomatic to break them up into multiple strings and use the classes! macro to concatenate them.

hop
<div class={
  classes!(
    "inline-flex",
    "items-center",
    "justify-center",
    "rounded-full",
    "border",
    "px-2",
    "py-0.5",
    "text-xs",
    "font-medium",
    "w-fit",
    "whitespace-nowrap",
    "overflow-hidden",
  )
}>

Passing children to a component

To make a component accept children, declare a parameter called children.

hop
// 1. Define a component that accepts children
<Button {children: HTMLNode}>
  <button class="border p-2 flex gap-2">
    {children}
  </button>
</Button>

<Component>
  // 2. Render the Button component
  <Button>
    <IconBack /> Go back
  </Button>
</Component>

Iterating over data

The <for> tag renders its content once for each item in an array.

hop
<Component {items: Array[String]}>
  <for {item in items}>
    <div>{item}</div>
  </for>
</Component>

Conditional rendering

The <if> tag renders its content when a condition is true.

hop
<Component {year: Int}>
  <if {year < 2000}>
    <div>The year is in a previous millenium</div>
  </if>
</Component>

The <match> tag is used to match an expression to a certain form.

hop
<Component {name: Option[String]}>
  <match {name}>
    <case {Some(n)}>
      Name is {n}
    </case>
    <case {None}>
      Unknown name
    </case>
  </match>
</Component>