PrevelteKit

Manual

API reference for PrevelteKit. Each section shows the API and a code example.

Stores

Reactive state containers. When a store value changes, all bound DOM elements update automatically.

// Create a store with an initial value
count := p.New(0)              // *Store[int]
name := p.New("hello")         // *Store[string]
dark := p.New(false)           // *Store[bool]

// Read and write
count.Get()                    // returns current value
count.Set(5)                   // set new value
count.Update(func(v int) int { // transform current value
    return v + 1
})

// Subscribe to changes
count.OnChange(func(v int) {
    // called whenever count changes
})

Elements & Binding

Build DOM trees with typed element functions. Embed stores directly — they become live text nodes.

// Typed element functions with reactive store interpolation
p.P("Count: ", p.Strong(count))

// Two-way binding for inputs
p.Input(p.Attr("type", "text")).Bind(name)       // *Store[string]
p.Input(p.Attr("type", "text")).Bind(age)        // *Store[int]
p.Input(p.Attr("type", "checkbox")).Bind(dark)    // *Store[bool]
p.Textarea().Bind(notes)                         // *Store[string]

// Dynamic attributes
p.Div("content").Attr("data-theme", theme)

// Conditional attributes (additive for same attribute name)
p.Div("content").AttrIf("class",
    p.Cond(func() bool { return dark.Get() }, dark), "active")

// Raw HTML rendering (not escaped)
p.BindAsHTML(rawHTML)

// Static raw HTML (entities, inline markup)
p.RawHTML("© 2024")

Events

Attach event handlers with .On(). Chain modifiers for common patterns.

// Click handler
p.Button("Click").On("click", handler)

// Form submit with preventDefault
p.Form(...).On("submit", handler).PreventDefault()

// Stop event bubbling
p.Button("Inner").On("click", handler).StopPropagation()

// Inline handler
p.Button("+5").On("click", func() {
    count.Update(func(v int) int { return v + 5 })
})

Conditionals

Show or hide content reactively with p.If(). Supports ElseIf and Else chains.

// Simple if
p.If(p.Cond(func() bool { return count.Get() > 0 }, count),
    p.P("Positive"),
)

// If / ElseIf / Else
p.If(p.Cond(func() bool { return score.Get() >= 90 }, score),
    p.P("Grade: A"),
).ElseIf(p.Cond(func() bool { return score.Get() >= 80 }, score),
    p.P("Grade: B"),
).Else(
    p.P("Grade: F"),
)

// p.Cond(predicateFn, ...dependencyStores)

Lists

Reactive lists with p.NewList(). Render with p.Each().

// Create a reactive list
items := p.NewList[string]("Apple", "Banana", "Cherry")

// Mutate — triggers re-render
items.Append("Date")
items.RemoveAt(0)
items.Set([]string{"Mango", "Papaya"})
items.Clear()

// Reactive length
items.Len()  // *Store[int] — updates automatically

// Render each item
p.Each(items, func(item string, i int) p.Node {
    return p.Li(p.Itoa(i), ": ", item)
}).Else(
    p.P("No items"),
)

Components

Components are Go structs implementing Render() p.Node. Use p.Comp() to embed them.

// Define a component
type Badge struct {
    Label *p.Store[string]
}

func (b *Badge) Render() p.Node {
    return p.Span(p.Attr("class", "badge"), b.Label)
}

// Scoped CSS
func (b *Badge) Style() string {
    return `.badge{background:#007bff;color:#fff}`
}

// Use a component (props are struct fields)
p.Comp(&Badge{Label: p.New("New")})

// Component with slot (child content)
p.Comp(&Card{Title: p.New("Hello")},
    p.P("Slot content here"),
)

// Inside the component, render slot content:
p.Slot()

// Callback props for component events
type Button struct {
    Label   *p.Store[string]
    OnClick func()
}

// Shared stores: pass the same *Store to multiple components
theme := p.New("light")
p.Comp(&Header{Theme: theme})
p.Comp(&Sidebar{Theme: theme})
// both components read/write the same store

Routing

Client-side routing with p.NewRouter(). Each route maps to a component and an SSR HTML file.

// Define routes
routes := []p.Route{
    {Path: "/", HTMLFile: "index.html", SSRPath: "/", Component: home},
    {Path: "/about", HTMLFile: "about.html", SSRPath: "/about", Component: about},
}

// Start the router (in OnMount)
router := p.NewRouter(currentComponent, routes, "unique-id")
router.NotFound(func() { currentComponent.Set(nil) })
router.Start()

// Store[Component] holds the active route component
currentComponent *p.Store[p.Component]

// Links: client-side (default) vs server-side
// <a href="/about">About</a>           (SPA navigation)
// <a href="/about" external>About</a>  (full page reload)

// Store[Component] for local tabs (non-router):
activeTab := p.New[p.Component](tab1)
activeTab.WithOptions(tab1, tab2, tab3)
activeTab.Set(tab2)  // switches displayed component

Fetch

Type-safe HTTP requests with automatic JSON encoding/decoding via js struct tags.

// Define response type with js tags
type User struct {
    ID   int    `js:"id"`
    Name string `js:"name"`
}

// GET
go func() {
    user, err := p.Get[User]("https://api.example.com/user/1")
}()

// POST (send body, decode response)
go func() {
    created, err := p.Post[User](url, newUser)
}()

// PUT, PATCH, DELETE
result, err := p.Put[T](url, body)
result, err := p.Patch[T](url, body)
result, err := p.Delete[T](url)

// Advanced: custom headers and abort
signal, abort := p.NewAbortController()
go func() {
    result, err := p.Fetch[T](url, &p.FetchOptions{
        Method:  "GET",
        Headers: map[string]string{"Authorization": "Bearer token"},
        Signal:  signal,
    })
}()
abort()  // cancel the request

Storage

Persist state to localStorage. LocalStore auto-syncs on every Set().

// Auto-persisted store (syncs on every Set)
theme := p.NewLocalStore("theme", "light")
theme.Set("dark")  // automatically saved to localStorage
theme.Store        // *Store[string] — use in any element like any store

// Manual localStorage API
p.SetStorage("notes", "hello")
saved := p.GetStorage("notes")
p.RemoveStorage("notes")
p.ClearStorage()

Timers

Debounce, throttle, setTimeout, and setInterval — all return cleanup functions.

// Debounce: fires after idle period
doSearch, cleanup := p.Debounce(300, func() {
    // fires 300ms after last call
})
doSearch()   // call repeatedly — only last one fires
cleanup()    // cancel pending

// Throttle: max once per interval
onClick := p.Throttle(500, func() {
    // max once per 500ms
})

// SetTimeout: fires once after delay
cancel := p.SetTimeout(2000, func() {
    // fires after 2 seconds
})
cancel()  // cancel before it fires

// SetInterval: fires repeatedly
stop := p.SetInterval(60000, func() {
    // fires every 60 seconds
})
stop()  // stop the interval

Lifecycle

Components can implement lifecycle hooks for setup and teardown.

// OnMount: called when component becomes active
func (c *MyComp) OnMount() {
    if p.IsBuildTime {
        return  // skip side effects during SSR
    }
    // fetch data, start timers, etc.
}

// OnDestroy: called when component is removed (e.g. route change)
func (c *MyComp) OnDestroy() {
    if c.stopTimer != nil {
        c.stopTimer()
    }
}

// New: constructor — create stores, initialize state
func (c *MyComp) New() p.Component {
    return &MyComp{
        Count: p.New(0),
        Name:  p.New(""),
    }
}

// GlobalStyle: CSS applied to entire page (on App component)
func (a *App) GlobalStyle() string { return `body{margin:0}` }

// Style: scoped CSS (auto-prefixed to this component)
func (c *MyComp) Style() string { return `.btn{color:red}` }

// p.IsBuildTime: true during SSR, false in WASM
// Use to guard browser-only code (fetch, timers, DOM access)