Guide / 04 — Templates

Components

Components are .stew files with names starting with an uppercase letter. They can be organized in any project subfolder (except the root). They receive structured Props and can expose a slot to inject content.

Creating a component

The component/Card.stew file:

<goscript>
    // Declare component props
    type CardProps struct {
        Title       string
        Description string
    }
</goscript>

<div class="p-6 bg-white rounded-2xl border border-stone-200 shadow-sm">
    <h2 class="text-xl font-bold mb-2">{{ props.Title }}</h2>
    <p class="text-stone-500 text-sm">{{ props.Description }}</p>

    <!-- Optional slot to inject content -->
    <slot />
</div>
Rule: Even if a component has no props, the type XxxProps struct {} structure must be declared (empty). The compiler needs it to generate the Go signature.

Usage

To use a component in a page or another component, you must first import it within a <goscript> block. Then, use its name as an HTML tag.

<!-- pages/@page.stew -->
<goscript>
    import "../components/Card.stew"
</goscript>

<!-- Auto-closing (no slot) -->
<Card Title="My title" Description="My description" />

<!-- With slot: content between opening and closing tags -->
<Card Title="Actions" Description="Action buttons">
    <div class="mt-4 flex gap-2">
        <button class="px-4 py-2 bg-stone-900 text-white rounded-lg">Validate</button>
        <button class="px-4 py-2 border rounded-lg">Cancel</button>
    </div>
</Card>

Typed Components (Props)

To pass data to a component, define a structure named {Name}Props. The compiler will inject a props variable of this type into the component's scope.

<!-- components/User.stew -->
<goscript>
    type UserProps struct {
        Name string
        Age  int
    }
</goscript>

<div>
    <p>Name: {{ props.Name }}</p>
    <p>Age: {{ props.Age }}</p>
</div>
Note on typing: The Stew compiler automatically converts HTML attributes (strings) to simple Go types (int, bool, float) defined in your Props struct during the call.

Dynamic props

Props can receive Go expressions via the {{ }} syntax:

{{ each data.Products as p, i }}
    <Card Title="{{ p.Name }}" Description="{{ p.Sku }}" />
{{ end }}

Nested components

A component can import and use other components just like a page does:

<!-- component/Card.stew -->
<goscript>
    import "./Badge.stew"

    type CardProps struct {
        Title string
        Badge string
    }
</goscript>

<div class="...">
    <Badge Label="{{ props.Badge }}" />
    <h2>{{ props.Title }}</h2>
    <slot />
</div>

What the compiler generates

For Card.stew, the compiler generates Card.go:

// Code generated by Stew-Lang. DO NOT EDIT.
package component

import (
    "io"
    github.com/ZiplEix/stew/sdk/stew"
)

type CardProps struct {
    Title       string
    Description string
}

func Card(w io.Writer, data stew.PageData, props CardProps, slot func()) {
    // HTML rendering before the slot
    w.Write([]byte(`<div class="p-6 ...">`))
    w.Write([]byte(html.EscapeString(fmt.Sprint(props.Title))))
    // ...
    if slot != nil { slot() } // Slot injection
    w.Write([]byte(`</div>`))
}