components

module
v0.4.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2026 License: MIT

README

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.
  • ModalDialog: Centered modal overlay with backdrop.
  • SelectSearch: Searchable dropdown with live filtering.
  • ThemeToggle: Theme switcher (dark/light).

Where components fit

The ecosystem is layered:

tinywasm/components  →  raw reusable pieces (this repo). No layout knowledge.
tinywasm/layout       →  published layout skeletons (platformd shell, rightpanel,
                          crudview) with named slots (Form, Detail, HeadControls, …).
consumer's composition root (e.g. config/layouts)
                       →  preconfigures a layout once, injecting components into
                          its slots. Modules pick a preconfigured layout; they
                          never assemble components by hand.

Two rules keep this layering intact:

  • components never imports tinywasm/layout. The reverse (layout importing a component) is allowed but assembly belongs to the consumer, not this repo.
  • Assembly never lives in modules. A module consumes a preconfigured layout; it does not construct ActionButton/DataTable/etc. by hand inside a slot.

Every cataloged component is slot-ready: it can be dropped into any layout slot as a dom.Component with zero per-component configuration, and it is theme-driven: it inherits branding from RootCSS with no hardcoded colors. See docs/SKILL.md for the slot-readiness contract and docs/CATALOG.md for per-component status.

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

Directories

Path Synopsis
web command
Package targetlist is the selectable record list used by CRUD views.
Package targetlist is the selectable record list used by CRUD views.
web command
Package usermenu is the identity surface of an application shell: who is logged in, at rest, and what they can do about it, on demand.
Package usermenu is the identity surface of an application shell: who is logged in, at rest, and what they can do about it, on demand.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL