Tsunami’s virtual DOM system lets you build Go-native UI components that render server-side and stream to the GuLiN frontend in real time. Every piece of your app’s UI is aDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/jorgeurtubiam-ship-it/Gulin_ia/llms.txt
Use this file to discover all available pages before exploring further.
VDomElem — a lightweight Go struct that describes a tag, its props, and its children. The engine diffs these trees on each render and sends only the changed patches over WebSocket.
VDom Basics
Elements are plain Go structs defined in thevdom package:
VDomElem directly. Instead you use the vdom.H helper, which mirrors the JSX factory pattern from React. The engine maintains a persistent shadow component tree (ComponentImpl) that survives between renders. On each cycle, it reconciles new VDomElem input against the shadow tree and produces a minimal diff — only changed nodes are serialized and pushed to the frontend.
There are three node patterns:
- Text nodes (
#text) — leaf nodes that hold a string value. - Base / DOM elements — standard HTML tags (
div,span,button, etc.) that holdChildren. - Custom components — user-defined Go functions registered with
app.DefineComponent; they transform into base elements via their render functions.
Creating Elements
vdom.H — the element factory
vdom.H is the primary function for creating virtual DOM elements. It accepts a tag name, a props map, and a variadic list of children:
VDomElem, *VDomElem, slices, booleans, numeric types, or any other value (converted via fmt.Sprint). nil children are silently removed.
Conditional rendering
Usevdom.If to include a child only when a condition is true, and vdom.IfElse for a ternary-style choice:
Rendering lists
vdom.ForEach maps a Go slice to a []any of elements:
key on list items to enable key-based reconciliation. Use .WithKey(key) on the returned element, especially when rendering custom components whose prop types don’t include a key field:
Composing class names
vdom.Classes works like the JavaScript clsx library — it accepts strings, nil, []string, and map[string]bool values and joins the results into a single space-separated class string:
Defining Components
Register a component withapp.DefineComponent. The render function receives typed props and must return a value that can be converted to VDomElem — typically *VDomElem, a VDomElem, or nil:
State: Atoms and Hooks
app.UseLocal — component-local state
UseLocal creates an Atom[T] scoped to the current component instance. It is automatically removed when the component unmounts:
| Method | Description |
|---|---|
atom.Get() T | Read the current value. Registers the component as a dependent. |
atom.Set(newVal T) | Replace the value and schedule a re-render. Cannot be called during render. |
atom.SetFn(fn func(T) T) | Apply a function to a deep copy of the current value. Safe for slices and structs. |
app.UseEffect — side effects
UseEffect queues a function to run after the render cycle. It mirrors React’s useEffect, including dependency-based scheduling and cleanup functions:
nil as deps to run on every render; pass an empty slice []any{} to run only on mount.
app.UseTicker — periodic updates
UseTicker manages a repeating ticker whose goroutine is automatically cancelled when deps change or the component unmounts:
app.UseGoRoutine — long-running background work
UseGoRoutine spawns a goroutine and cancels its context when deps change or the component unmounts:
app.SendAsyncInitiation() whenever background work changes atom state outside of an event handler — this tells the frontend to request a fresh render.
app.UseAfter — one-shot timers
UseAfter fires once after a duration and is cancelled if deps change or the component unmounts:
app.UseRef and app.UseVDomRef
UseRef[T] provides a mutable ref that persists across re-renders without triggering them:
UseVDomRef returns a *vdom.VDomRef that can be attached to a DOM element for direct access (focus, scroll, position tracking):
"ref" prop and queue imperative operations with app.QueueRefOp:
Modal hooks
UseAlertModal and UseConfirmModal integrate with GuLiN’s native modal system:
Global State: DataAtom, ConfigAtom, SharedAtom
For state that lives outside any single component, use the module-level atom constructors:
Built-in UI Components
Table — ui.MakeTableComponent
The tsunami/ui package provides a feature-complete, generic table component with sorting, pagination, row selection, and custom cell renderers. Create a table component for any Go type with:
TableProps[T] — the props accepted by table components:
TableColumn[T] — column definition:
Tailwind CSS
Every Tsunami scaffold includes Tailwind CSS v4 (via@tailwindcss/cli). The scaffold’s tailwind.css defines a full design-token theme aligned with GuLiN’s visual language — dark background, accent greens, semantic color roles, and monospace fonts.
The scaffold’s Tailwind configuration is pre-wired. You apply styles by passing class strings as the
className prop in vdom.H. No Tailwind configuration file needs to be modified for standard usage. Use vdom.Classes(...) to combine multiple conditional class strings cleanly.| Token | Usage | Value |
|---|---|---|
bg-background | App background | rgb(34, 34, 34) |
text-primary | Headings, bold text | rgb(247, 247, 247) |
text-secondary | Body text | rgba(215, 218, 224, 0.7) |
text-muted | Fine / faint text | rgba(215, 218, 224, 0.5) |
text-accent / bg-accent | Accent (green) | rgb(88, 193, 66) |
border-border | Standard border | rgba(255, 255, 255, 0.16) |
bg-panel | Panel / card background | rgba(255, 255, 255, 0.12) |
bg-hoverbg | Hover highlight | rgba(255, 255, 255, 0.16) |
Event Handling
User interactions are sent from the frontend to the Go handler asVDomEvent values. Attach handlers by setting the corresponding prop — onClick, onChange, onKeyDown, onSubmit — to a Go function.
Click events
vdom.VDomEvent argument:
Input / change events
vdom.VDomEvent.TargetValue carries the current value of an input, textarea, or select:
e.TargetChecked:
Keyboard events
VDomFunc lets you capture specific keys and control propagation. Assign it as the onKeyDown prop:
vdom.VDomKeyboardEvent (available via e.KeyData) exposes Key, Code, Shift, Control, Alt, Meta, Cmd, and Option fields.
Form submit events
vdom.VDomFormData (available via e.FormData) provides Fields and Files maps:
Component Lifecycle
Tsunami’s lifecycle closely mirrors React’s, with a few Go-specific differences.Mount
A component mounts the first time it appears in a rendered tree. Its hooks are initialized in declaration order. Effects registered withUseEffect run after the first render.
Update
An update is triggered when:- An atom that the component read during its last render is updated with
SetorSetFn. - A parent re-renders and passes different props.
app.SendAsyncInitiation()is called from a background goroutine.
VDomElem tree against the shadow tree, and sends diffs to the frontend.
Unmount
A component unmounts when it is no longer present in the rendered tree. On unmount:- All
UseEffectcleanup functions are called. - All
UseLocalatoms are removed from the atom store. - Any goroutines started by
UseGoRoutine,UseTicker, orUseAfterhave their contexts cancelled. - The component is removed from the global
ComponentMap.
Returning nil
A component that returns nil from its render function stays mounted — it keeps all its state and hooks — but contributes nothing to the rendered output. This is useful for components that conditionally show UI: