<link rel="stylesheet" href="/fonts.css" />
On this page New / Options Register / Has / Components ValidateAll RenderPage RenderFragment String helpers Context variants ServeComponent ServePageComponent Mount WithDataMiddleware RegisterFunc RegisterDirective Missing prop handling ParseFile / Component Renderer Registry Directive interface DirectiveBinding / Context StyleCollector ScopeID / ScopeCSS Error types Sentinel errors

Go API Reference

Complete reference for every exported symbol in the htmlc package. Import path: github.com/dhamidi/htmlc.

Creating an Engine

New

func New(opts Options) (*Engine, error)

Creates an Engine from opts. If opts.ComponentDir is set the directory is walked recursively and all *.vue files are registered before the engine is returned.

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

Options

type Options struct {
    ComponentDir string
    Reload       bool
    FS           fs.FS
    Directives   DirectiveRegistry
    Debug        bool
}
FieldDescription
ComponentDir Directory walked recursively for *.vue files. Each file is registered by its base name without extension (Button.vueButton). When two files share the same base name the last one in lexical order wins.
Reload When true the engine checks the modification time of every registered file before each render and re-parses changed files automatically. Use during development; leave false in production.
FS When set, all file reads and directory walks use this fs.FS instead of the OS filesystem. ComponentDir is interpreted as a path within the FS. Useful with //go:embed. Hot-reload requires the FS to also implement fs.StatFS.
Directives Custom directives available to all components rendered by this engine. Keys are directive names without the v- prefix. Built-in directives cannot be overridden.
Debug When true the rendered HTML is annotated with HTML comments describing component boundaries, expression values, and slot contents. Development use only.

Rendering

RenderPage

func (e *Engine) RenderPage(w io.Writer, name string, data map[string]any) error

Renders the named component as a full HTML page and writes the result to w. Scoped styles are collected from the entire component tree and injected as a <style> block immediately before the first </head> tag. If no </head> is found the style block is prepended to the output.

Use RenderPage for page components that include <!DOCTYPE html>, <html>, <head>, and <body>.

var buf bytes.Buffer
err := engine.RenderPage(&buf, "HomePage", map[string]any{
    "title": "Welcome",
})

RenderFragment

func (e *Engine) RenderFragment(w io.Writer, name string, data map[string]any) error

Renders the named component as an HTML fragment and prepends the collected <style> block to the output. Does not search for a </head> tag. Use for partial page updates such as HTMX responses or turbo-frame updates.

String helpers

func (e *Engine) RenderPageString(name string, data map[string]any) (string, error)
func (e *Engine) RenderFragmentString(name string, data map[string]any) (string, error)

Convenience wrappers around RenderPage and RenderFragment that return the result as a string instead of writing to an io.Writer.

Context variants

func (e *Engine) RenderPageContext(ctx context.Context, w io.Writer, name string, data map[string]any) error
func (e *Engine) RenderFragmentContext(ctx context.Context, w io.Writer, name string, data map[string]any) error

Like RenderPage and RenderFragment but accept a context.Context. The render is aborted and ctx.Err() is returned if the context is cancelled or its deadline is exceeded.

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
err := engine.RenderPageContext(ctx, w, "HomePage", data)

HTTP Integration

ServeComponent

func (e *Engine) ServeComponent(name string, data func(*http.Request) map[string]any) http.HandlerFunc

Returns an http.HandlerFunc that renders name as a fragment on every request. The data function is called per-request to build the template scope; it may be nil. Data middleware registered via WithDataMiddleware is applied after the data function. Sets Content-Type: text/html; charset=utf-8.

mux.HandleFunc("GET /search-results", engine.ServeComponent("SearchResults", func(r *http.Request) map[string]any {
    return map[string]any{"query": r.URL.Query().Get("q")}
}))

ServePageComponent

func (e *Engine) ServePageComponent(name string, data func(*http.Request) (map[string]any, int)) http.HandlerFunc

Returns an http.HandlerFunc that renders name as a full HTML page. The data function returns both the template scope and the HTTP status code to send. A status code of 0 is treated as 200. If data is nil a 200 OK response with no template data is used.

mux.HandleFunc("GET /post/{id}", engine.ServePageComponent("PostPage", func(r *http.Request) (map[string]any, int) {
    post, err := db.GetPost(r.PathValue("id"))
    if err != nil {
        return map[string]any{"error": err.Error()}, http.StatusNotFound
    }
    return map[string]any{"post": post}, http.StatusOK
}))

Mount

func (e *Engine) Mount(mux *http.ServeMux, routes map[string]string)

Registers multiple component routes on mux at once. Keys are http.ServeMux patterns (e.g. "GET /{$}", "GET /about"); values are component names. Each component is served as a full page via ServePageComponent with no data function.

engine.Mount(mux, map[string]string{
    "GET /{$}":    "HomePage",
    "GET /about":  "AboutPage",
    "GET /blog":   "BlogPage",
})

WithDataMiddleware

func (e *Engine) WithDataMiddleware(fn func(*http.Request, map[string]any) map[string]any) *Engine

Adds a function that augments the data map on every HTTP-triggered render. Multiple middleware functions are called in registration order; later middleware can overwrite keys set by earlier ones. Returns the engine for chaining.

Data middleware applies only to the top-level render scope and is not automatically propagated into child component scopes. Pass shared values via RegisterFunc if child components need them.

engine.WithDataMiddleware(func(r *http.Request, data map[string]any) map[string]any {
    data["currentUser"] = userFromRequest(r)
    data["csrfToken"]   = csrf.Token(r)
    return data
})

Component Management

Register

func (e *Engine) Register(name, path string) error

Manually adds a component from path to the engine's registry under name, without requiring a directory scan. Useful for programmatically generated components or files outside ComponentDir.

Has

func (e *Engine) Has(name string) bool

Reports whether name is a registered component.

Components

func (e *Engine) Components() []string

Returns the names of all registered components in sorted order. Automatic lowercase aliases added by the engine are excluded.

ValidateAll

func (e *Engine) ValidateAll() []ValidationError

Checks every registered component for unresolvable child component references. Returns one ValidationError per problem; an empty slice means all components are valid. Call once at application startup to surface missing-component issues early.

if errs := engine.ValidateAll(); len(errs) != 0 {
    for _, e := range errs {
        log.Println(e)
    }
    os.Exit(1)
}

Customization

RegisterFunc

func (e *Engine) RegisterFunc(name string, fn func(...any) (any, error)) *Engine

Adds a per-engine function available in all template expressions. The function is callable from templates as name(). Engine functions have lower priority than user-provided data keys. They are propagated automatically into every child component's scope.

engine.RegisterFunc("formatDate", func(args ...any) (any, error) {
    t, ok := args[0].(time.Time)
    if !ok {
        return "", fmt.Errorf("formatDate: expected time.Time")
    }
    return t.Format("Jan 2, 2006"), nil
})

RegisterDirective

func (e *Engine) RegisterDirective(name string, dir Directive)

Adds a custom directive under name (without the v- prefix). Replaces any previously registered directive with the same name. Panics if dir is nil.

engine.RegisterDirective("tooltip", &TooltipDirective{})

WithMissingPropHandler

func (e *Engine) WithMissingPropHandler(fn MissingPropFunc) *Engine

Sets the function called when any component has a missing prop. The default behaviour renders a visible [missing: <name>] placeholder. Use ErrorOnMissingProp for strict error behaviour or SubstituteMissingProp for legacy placeholder text.

// Abort rendering on any missing prop
engine.WithMissingPropHandler(htmlc.ErrorOnMissingProp)

// Substitute a legacy placeholder string
engine.WithMissingPropHandler(htmlc.SubstituteMissingProp)

Low-level API

ParseFile

func ParseFile(path, src string) (*Component, error)

Parses a .vue Single File Component from the string src. path is used only for error messages and the scope attribute hash. Returns a *Component whose Template field is a parsed HTML node tree. Only the top-level <template>, <script>, and <style> sections are extracted; <template> is required.

Component

type Component struct {
    Template *html.Node // root of the parsed template node tree
    Script   string     // raw <script> content (empty if absent)
    Style    string     // raw <style> content (empty if absent)
    Scoped   bool       // true when <style scoped> was present
    Path     string     // source file path passed to ParseFile
    Source   string     // raw source text (for error location reporting)
    Warnings []string   // non-fatal issues found during parsing
}

Holds the parsed representation of a .vue Single File Component. The Warnings field may contain notices about auto-corrected self-closing component tags.

Component.Props

func (c *Component) Props() []PropInfo

Walks the template AST and returns all top-level variable references the template uses. Identifiers starting with $ and v-for loop variables are excluded.

type PropInfo struct {
    Name        string   // identifier name
    Expressions []string // template expressions in which it appears
}

Registry

type Registry map[string]*Component

Maps component names to their parsed components. Keys may be PascalCase or kebab-case. Most callers use Engine, which builds and maintains a Registry automatically.

Renderer

func NewRenderer(c *Component) *Renderer

Creates a Renderer for c. Use the builder methods below to configure it before calling Render.

Renderer is the low-level rendering primitive. Most callers should use Engine via RenderPage or RenderFragment. Use NewRenderer when you need fine-grained control over style collection or registry attachment.

Builder methodDescription
WithStyles(sc *StyleCollector) *Renderer Sets the StyleCollector that receives this component's style contribution.
WithComponents(reg Registry) *Renderer Attaches a component registry, enabling component composition.
WithMissingPropHandler(fn MissingPropFunc) *Renderer Sets the handler called when a template prop is absent from the scope.
WithDirectives(dr DirectiveRegistry) *Renderer Attaches a custom directive registry.
WithContext(ctx context.Context) *Renderer Attaches a context.Context; the render is aborted on cancellation.
WithFuncs(funcs map[string]any) *Renderer Attaches engine-registered functions so they are available in expressions and propagated to all child renderers.

Renderer.Render / RenderString

func (r *Renderer) Render(w io.Writer, scope map[string]any) error
func (r *Renderer) RenderString(scope map[string]any) (string, error)

Evaluates the component's template against scope and writes HTML to w (or returns it as a string). Prop validation and style collection happen here.

Package-level helpers

func Render(w io.Writer, c *Component, scope map[string]any) error
func RenderString(c *Component, scope map[string]any) (string, error)

Convenience wrappers that create a temporary Renderer for c. They do not collect styles or support component composition. Use NewRenderer with WithStyles and WithComponents when those features are needed.

Directives

Directive interface

type Directive interface {
    Created(node *html.Node, binding DirectiveBinding, ctx DirectiveContext) error
    Mounted(w io.Writer, node *html.Node, binding DirectiveBinding, ctx DirectiveContext) error
}

Implemented by custom directive types. Register with Engine.RegisterDirective or pass in Options.Directives. Only the Created and Mounted hooks are called because htmlc renders server-side.

  • Created — called before the element is rendered. Receives a shallow-cloned working node whose Attr slice and Data field may be freely mutated. Return a non-nil error to abort rendering.
  • Mounted — called after the element's closing tag has been written to w. Bytes written to w appear immediately after the element. Return a non-nil error to abort rendering.

DirectiveBinding / DirectiveContext

type DirectiveBinding struct {
    Value     any             // result of evaluating the directive expression
    RawExpr   string          // un-evaluated expression string from the template
    Arg       string          // directive argument after the colon, e.g. "href" in v-bind:href
    Modifiers map[string]bool // dot-separated modifiers, e.g. {"prevent": true}
}
type DirectiveContext struct {
    Registry Registry // component registry the renderer is using
}

DirectiveRegistry

type DirectiveRegistry map[string]Directive

Maps directive names (without the v- prefix) to their implementations. Keys are lower-kebab-case; the renderer normalises names before lookup.

Style Collection

StyleCollector

type StyleCollector struct { /* unexported fields */ }

Accumulates StyleContribution values from one or more component renders into a single ordered list, deduplicating contributions from the same scoped component. Engine creates and manages a StyleCollector automatically on each render call.

func (sc *StyleCollector) Add(c StyleContribution)
func (sc *StyleCollector) All() []StyleContribution
  • Add — appends c, skipping duplicates. Two contributions are duplicates when they share the same composite key (ScopeID + CSS).
  • All — returns all contributions in the order they were added.

StyleContribution

type StyleContribution struct {
    ScopeID string // scope attribute name (e.g. "data-v-a1b2c3d4"), empty for global styles
    CSS     string // stylesheet text, already rewritten by ScopeCSS for scoped components
}

ScopeID / ScopeCSS

func ScopeID(path string) string
func ScopeCSS(css, scopeAttr string) string
  • ScopeID — returns "data-v-" followed by 8 hex digits derived from the FNV-1a 32-bit hash of path.
  • ScopeCSS — rewrites css so that every selector in every non-@-rule has scopeAttr appended to its last compound selector. @-rules are passed through verbatim.

Error Handling

Error types

ParseError

type ParseError struct {
    Path     string          // source file path
    Msg      string          // human-readable description
    Location *SourceLocation // source position, or nil if unknown
}

Returned by ParseFile when a .vue file cannot be parsed.

RenderError

type RenderError struct {
    Component string          // component name being rendered
    Expr      string          // expression that triggered the error (may be empty)
    Wrapped   error           // underlying error
    Location  *SourceLocation // source position, or nil if unknown
}

Returned by render methods when template evaluation fails. Implements Unwrap.

ValidationError

type ValidationError struct {
    Component string // name of the component with the problem
    Message   string // description of the problem
}

One entry per problem in the slice returned by ValidateAll.

SourceLocation

type SourceLocation struct {
    File    string // source file path
    Line    int    // 1-based line number (0 = unknown)
    Column  int    // 1-based column (0 = unknown)
    Snippet string // ~3-line context around the error (may be empty)
}

MissingPropFunc / handlers

type MissingPropFunc func(name string) (any, error)

func ErrorOnMissingProp(name string) (any, error)
func SubstituteMissingProp(name string) (any, error)
  • MissingPropFunc — signature for missing-prop handlers; receive the prop name, return a substitute value or an error.
  • ErrorOnMissingProp — aborts rendering with an error whenever a prop is absent. Use for strict validation.
  • SubstituteMissingProp — returns "MISSING PROP: <name>" as a placeholder string.

Sentinel errors

var ErrComponentNotFound = errors.New("htmlc: component not found")
var ErrMissingProp       = errors.New("htmlc: missing required prop")
  • ErrComponentNotFound — wrapped inside the error returned by render methods when the requested component name is not registered.
  • ErrMissingProp — returned (wrapped) when a required prop is absent and no MissingPropFunc has been set.
if errors.Is(err, htmlc.ErrComponentNotFound) {
    http.NotFound(w, r)
    return
}