Skip to content
/ dom Public

Ultra-minimal DOM & event toolkit for Go (TinyGo WASM-optimized).

License

Notifications You must be signed in to change notification settings

tinywasm/dom

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

39 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

tinywasm/dom

Project Badges

Ultra-minimal DOM & event toolkit for Go (TinyGo WASM-optimized).

tinywasm/dom provides a minimalist, WASM-optimized way to interact with the browser DOM in Go, avoiding the overhead of the standard library and syscall/js exposure. It is designed specifically for TinyGo applications where binary size and performance are critical.

πŸš€ Features

  • JSX-like Declarative View: Concise nesting with Div(H1("Title"), P("..."))
  • Typed Form Elements: Semantic API for forms with Text("email").Required()
  • Void Element Fix: Correctly renders <br>, <img>, <input> without closing tags
  • TinyGo Optimized: Avoids heavy standard library packages to keep WASM binaries <500KB
  • Direct DOM Manipulation: No Virtual DOM overhead. You control the updates.
  • ID-Based Caching: Efficient element lookup and caching strategy
  • Lifecycle Hooks: OnMount, OnUpdate, OnUnmount for fine-grained control

πŸ“¦ Installation

go get github.com/tinywasm/dom

⚑ Quick Start

For a complete example including Elm architecture (Dynamic Components) and Static Components, check the following file:

πŸ‘‰ web/client.go

This file contains the reference implementation used for testing and demonstrations.

🎨 JSX-like Builder API

The API allows concise nesting and typed chaining:

import . "github.com/tinywasm/dom"

Div(
	H1("Welcome"),
	P("Enter your credentials:"),
	Form(
		Email("user_email", "Email address").Required(false),
		Password("pwd").Placeholder("Secret password"),
		Button("Login").Attr("type", "submit"),
	).Action("/login"),
).Class("container")

Available builders:

  • Containers: Div, Span, P, H1-H6, Ul, Ol, Li, Section, Main, Article, Header, Footer, Nav, Aside, Table, Thead, Tbody, Tr, Td, etc.
  • Typed Inputs: Text, Email, Password, Number, Checkbox, Radio, File, Date, Hidden, Search, Tel, Url, Range, Color.
  • Specialized: Form, Select, Textarea, Button, A.
  • Void Elements: Img, Br, Hr.

πŸ”„ Lifecycle Hooks

Components can implement optional lifecycle interfaces:

type MyComponent struct {
	*dom.Element
	data []string
}

// Called after component is mounted to DOM
func (c *MyComponent) OnMount() {
	c.data = fetchData()
	c.Update()
}

// Called after re-render (dom.Update)
func (c *MyComponent) OnUpdate() {
	fmt.Println("Component updated")
}

// Called before component is removed
func (c *MyComponent) OnUnmount() {
	// Cleanup resources
}

πŸ“ Component Interface

All components must implement:

type Component interface {
	GetID() string
	SetID(string)
	RenderHTML() string  // OR Render() *Element
	Children() []Component
}

Two rendering options:

  1. RenderHTML() string - For static components (smaller binary)
  2. Render() *dom.Element - For dynamic components (type-safe, composable)

Components can implement either or both. DOM checks Render() first, falls back to RenderHTML().

🎯 Hybrid Rendering Strategy

Choose the right rendering method for each component:

Component Type Method Benefit
Static (no interactivity) RenderHTML() string Smaller binary, less overhead
Dynamic (interactive, state) Render() *dom.Element Type-safe, composable, fluent API

See the implementation examples in web/client.go to see both approaches in action.

🧩 Nested Components

Components can contain child components:

type MyList struct {
	*dom.Element
	items []dom.Component
}

func (c *MyList) Children() []dom.Component {
	return c.items
}

func (c *MyList) Render() *dom.Element {
	list := dom.Div()
	for _, item := range c.items {
		list.Add(item) // Components can be children
	}
	return list
}

When you call dom.Render("app", myList), the library will:

  1. Render the HTML
  2. Call OnMount() for MyList
  3. Recursively call OnMount() for all items

The same recursion applies to cleanup, ensuring all event listeners are cleaned up when a parent is replaced.

🎯 Event Handling

Event handling is integrated directly into the Builder API via On(eventType, handler).

πŸ”§ Core API

Package Functions

// Rendering
dom.Render(parentID, component)  // Replace parent's content
dom.Append(parentID, component)  // Append after last child
dom.Update(component)            // Re-render in place

// Routing (hash-based)
dom.OnHashChange(handler)        // Listen to hash changes
dom.GetHash()                    // Get current hash
dom.SetHash(hash)                // Set hash

Element Helpers

Embedding *dom.Element provides these methods automatically:

type Counter struct {
	*dom.Element
	count int
}

// Chainable helpers
counter.Update()              // Trigger re-render
counter.GetID()               // Get unique ID
counter.SetID("my-id")        // Set custom ID

πŸ“š Documentation

For more detailed information, please refer to the documentation in the docs/ directory:

  1. Specification & Philosophy: Design goals, architecture, and key decisions.
  2. API Reference: Detailed definition of DOM, Element, and Component interfaces.
  3. Creating Components: Guide to building basic and nested components.
  4. Event Handling: Using the Event interface for clicks, inputs, and forms.
  5. Advanced Patterns: Dynamic lists, decoupling, and performance tips.
  6. Comparison: TinyDOM vs. syscall/js, VDOM, and JS frameworks.

πŸ†• What's New in v0.5.0

  • βœ… Major API Redesign - JSX-like factories (Div(H1("Title")))

  • βœ… Typed Form Elements - Semantic chaining (Email("u").Required())

  • βœ… Internal Privatization - Cleaned up public API (privatized EventHandler, etc.)

  • βœ… Void Element Rendering - Correct HTML for <br>, <img>, <input>

  • βœ… Auto-ID Generation - Simplified IDs without auto- prefix

  • βœ… JSX-like factories - Concise nesting (Div(H1("Title"), P("...")))

  • βœ… Typed Form Elements - Semantic chaining (Email("u").Required())

  • βœ… Void Element Rendering - Correct HTML for <br>, <img>, <input>

  • βœ… Fluent Builder API - Chainable methods (dom.Div().ID("x").Class("y"))

  • βœ… Hybrid rendering - Choose DSL or string HTML per component

  • βœ… Lifecycle hooks - OnMount, OnUpdate, OnUnmount

  • βœ… Auto-ID generation - All components get unique IDs automatically

πŸ“Š Performance

Binary Size (TinyGo WASM):

  • Simple counter app: ~35KB (compressed)
  • Todo list with 10 components: ~120KB (compressed)
  • Full application: <500KB (compressed)

Compared to standard library approach: 60-80% smaller binaries.

License

MIT

About

Ultra-minimal DOM & event toolkit for Go (TinyGo WASM-optimized).

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages