Skip to content

tinywasm/components

Repository files navigation

TinyWasm Components

A catalog of reusable, efficient, and WebAssembly-ready UI components for the TinyWasm ecosystem.

Features

  • One way to build: a component is just Render() (+ optional Init(ctx)). No OnMount/OnUpdate/OnUnmount, no manual Update() — impossible to wire wrong.
  • Fine-grained reactivity: dynamic state lives in typed signals; changing one patches only the bound DOM node — no whole-element re-render, no Virtual DOM.
  • No generics: concrete typed signals (SignalString/SignalBool/SignalNodes), matching the ecosystem's "cero any, cero map" rule — readable, TinyGo-friendly.
  • SSR/CSR Split: CSS and heavy assets are server-side only; WASM binaries remain tiny.
  • Fluent API: Uses tinywasm/dom for type-safe, declarative UI construction.
  • Theme Integration: Consumes canonical tokens from tinywasm/dom.
  • Explicit Registration: Only pay for what you use (tree-shakeable).

Installation

go get github.com/tinywasm/components

To enable the default theme, inject dom.ThemeCSS into your page <head> once via your site builder.

Quick Start

1. Import Components

import (
    "github.com/tinywasm/components/actionbutton"
    "github.com/tinywasm/components/contentcard"
    . "github.com/tinywasm/dom"
)

2. Instantiate and Render

func main() {
    // Create a button
    btn := &actionbutton.ActionButton{
        Text: "Click Me",
        Variant: "primary",
        OnClick: func(e Event) {
            println("Button clicked!")
        },
    }

    // Render to the DOM
    Append("app", btn)
}

The Component API (build your own)

A component implements only Render() *Element, plus an optional Init(ctx Ctx) that runs once before the first render. Dynamic state lives in a signal; you change it with Set, and the bound DOM updates itself — you never call Update().

Start here — the simplest reactive component. A signal, bound to text, changed on click:

import (
    . "github.com/tinywasm/dom"
    . "github.com/tinywasm/html"
)

type LikeButton struct {
    Element                  // value-embed, never *Element
    label *SignalString      // dynamic state is a signal, not a plain field
}

func (c *LikeButton) Init(_ Ctx) { c.label = NewString("Like") } // starting state

func (c *LikeButton) Render() *Element {
    return Button().
        BindText(c.label).                                   // show the signal
        On("click", func(Event) { c.label.Set("Liked ♥") })  // change it → the button updates itself
}

Three ideas: state is a signal, BindText shows it, Set changes it. That's the whole loop.

Growing up — computed text + show/hide. When a value is computed from state, pass a function to BindTextFunc (no dependency list — it auto-detects the signals you read). Toggle() flips a bool:

type Disclosure struct {
    Element
    open *SignalBool
}
func (c *Disclosure) Init(_ Ctx) { c.open = NewBool(false) }

func (c *Disclosure) Render() *Element {
    return Div(
        Button().
            BindTextFunc(func() string { if c.open.Get() { return "Hide" }; return "Show" }).
            On("click", func(Event) { c.open.Toggle() }),
        Show(c.open, func() *Element { return Div("details…") }), // mounts/unmounts reactively
    )
}

Signals (no generics — concrete typed cells):

Type Create For
*SignalString NewString("") text, attribute values, two-way input
*SignalBool NewBool(false) class/attr toggles, Show conditions (Toggle() to flip)
*SignalNodes NewNodes(...) keyed lists of rendered rows

Bindings (each patches only its own DOM spot):

  • Show a signal directly: BindText, BindAttr, BindClass, BindAttrBool, Bind (two-way input, cursor/IME safe), BindChildren (keyed list), Autofocus, Show(cond, render).
  • Show a computed value (function, auto-tracked — no deps list): BindTextFunc, BindAttrFunc, BindClassFunc, BindAttrBoolFunc. (DeriveString/DeriveBool for a named shared computed.)

For async/teardown, set signals from a goroutine in Init and use ctx.OnCleanup(fn).

See Component Skill Guide for the full standard.

Available Components

See Component Catalog for full documentation.

  • ActionButton: Primary/secondary actions with variants.
  • ContentCard: Content container with header/body/footer.
  • DataTable: Data tables with headers and rows.
  • NavBar: Navigation menu with icon support.
  • Dialog: Modal dialog overlays.
  • ThemeToggle: Theme switcher (light/dark/auto).

Forms

Forms are NOT part of tinywasm/components. Use github.com/tinywasm/form directly — it is the standard form library for the tinywasm ecosystem.

Development

See Component Skill Guide to learn how to build your own components.

Prerequisites

  • Go 1.25+
  • TinyGo (for WASM compilation)

Running Tests

Use gotest (the tinywasm runner — WASM tests run against a real DOM), never go test:

go install github.com/tinywasm/devflow/cmd/gotest@latest
gotest

About

HTML components for the go web framework tinywasm

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages