<link rel="stylesheet" href="/fonts.css" />
On this page 1 — Install 2 — Write a component 3 — Create an engine 4 — Render with props 5 — Use slots Component system Go API reference How-to guides

Tutorial

Build your first htmlc component from scratch. This walkthrough takes you from installation to rendering a component with props and slots in about five minutes.

Step 1 — Install htmlc

Add the package to your Go module:

go get github.com/dhamidi/htmlc

The CLI is optional but handy for testing components without writing Go code:

go install github.com/dhamidi/htmlc/cmd/htmlc@latest

Step 2 — Write a component

Create a directory called components/ and add a file named Card.vue:

<!-- components/Card.vue -->
<template>
  <div class="card">
    <h2>{{ title }}</h2>
    <slot>No content provided.</slot>
  </div>
</template>

<style scoped>
.card {
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 1rem;
}
</style>

The {{ title }} interpolation reads the title prop. The <slot> element is a placeholder for content supplied by a parent component; its children are the fallback rendered when no content is provided.

Step 3 — Create an engine

Call htmlc.New with the directory that contains your .vue files. The engine discovers and registers every component automatically.

package main

import (
    "log"

    "github.com/dhamidi/htmlc"
)

func main() {
    engine, err := htmlc.New(htmlc.Options{
        ComponentDir: "./components",
    })
    if err != nil {
        log.Fatal(err)
    }
    _ = engine
}

Step 4 — Render with props

Call RenderFragmentString to render a component to a string. Pass props as a map[string]any.

html, err := engine.RenderFragmentString("Card", map[string]any{
    "title": "Hello, htmlc!",
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(html)

Expected output (style block prepended by the engine):

<style>
.card[data-v-]{border:1px solid #ccc;border-radius:8px;padding:1rem}
</style>
<div class="card" data-v->
  <h2>Hello, htmlc!</h2>
  No content provided.
</div>

The fallback text "No content provided." is rendered because no slot content was passed. Step 5 shows how to supply it.

Step 5 — Use slots

Slot content is supplied through component composition in a .vue template. There is no $slots key in the Go props map; htmlc does not support injecting raw HTML into slots via the data map.

Create a wrapper component that uses Card with slot content:

<!-- components/WelcomeCard.vue -->
<template>
  <Card title="Welcome">
    <p>This paragraph is rendered inside the Card's slot.</p>
  </Card>
</template>

Then render the wrapper from Go:

html, err := engine.RenderFragmentString("WelcomeCard", nil)
if err != nil {
    log.Fatal(err)
}
fmt.Println(html)

Expected output:

<div class="card" data-v->
  <h2>Welcome</h2>
  <p>This paragraph is rendered inside the Card's slot.</p>
</div>

The same pattern applies to named and scoped slots — the parent component uses <template #name> syntax in the .vue file to target specific slots. See the component system reference for named and scoped slot examples.

Dynamic slot content from Go
If you need to inject a dynamic HTML string into a component from Go, use a regular prop with v-html instead of a slot:

<!-- components/Card.vue -->
<div class="card">
  <h2>{{ title }}</h2>
  <div v-html="body"></div>
</div>
html, err := engine.RenderFragmentString("Card", map[string]any{
    "title": "Hello",
    "body":  "<p>Dynamic content from Go</p>",
})