{"id":37125076,"url":"https://github.com/eleven-am/pondlive","last_synced_at":"2026-01-14T14:27:39.055Z","repository":{"id":59144886,"uuid":"527271462","full_name":"eleven-am/pondLive","owner":"eleven-am","description":"PondLive is a lightweight, and easy to use serverside only web framework, leveraging the power of PondSocket to create a simple, yet powerful, SPA framework.","archived":false,"fork":false,"pushed_at":"2025-12-27T01:30:19.000Z","size":5535,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-12-28T17:26:20.270Z","etag":null,"topics":["nodejs","responsive","server","single-page-app","socket","socket-programming","websocket","websockets"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eleven-am.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-08-21T17:02:12.000Z","updated_at":"2025-12-27T01:30:17.000Z","dependencies_parsed_at":"2024-12-06T17:22:31.063Z","dependency_job_id":"83cdfdf3-2f4c-46c8-9222-701e50ec2a17","html_url":"https://github.com/eleven-am/pondLive","commit_stats":{"total_commits":213,"total_committers":2,"mean_commits":106.5,"dds":"0.0046948356807511304","last_synced_commit":"c38c4477fddb851ecec8c949b132ce6fa89476ac"},"previous_names":[],"tags_count":70,"template":false,"template_full_name":null,"purl":"pkg:github/eleven-am/pondLive","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleven-am%2FpondLive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleven-am%2FpondLive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleven-am%2FpondLive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleven-am%2FpondLive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eleven-am","download_url":"https://codeload.github.com/eleven-am/pondLive/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleven-am%2FpondLive/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28423063,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T13:30:50.153Z","status":"ssl_error","status_checked_at":"2026-01-14T13:29:08.907Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nodejs","responsive","server","single-page-app","socket","socket-programming","websocket","websockets"],"created_at":"2026-01-14T14:27:37.461Z","updated_at":"2026-01-14T14:27:39.040Z","avatar_url":"https://github.com/eleven-am.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PondLive\n\nPondLive is a Go library for building interactive web interfaces entirely in Go. UI components and logic run on the server, while the browser stays responsive.\n\nTraditional SSR sends HTML once; SPAs push most logic into the browser. PondLive keeps state on the server and uses PondSocket to hold a bidirectional WebSocket open. When state changes, it computes a minimal DOM diff and streams just those patches to the client.\n\n## How It Works\n1. Define views in Go: functions return HTML nodes (`pkg.Node`). For children/props, wrap with `pkg.Component`/`pkg.PropsComponent`.\n2. Initial render: on first request, PondLive renders the component tree to HTML and serves it immediately.\n3. Connect: a lightweight JS runtime connects back over PondSocket.\n4. Interact: browser events flow to Go handlers over the socket.\n5. Re-render \u0026 patch: handlers update state; PondLive re-executes the component, diffs against the previous tree, and streams a minimal patch to update the DOM.\n\n## Core Concepts\n\n### Rendering with Components\nA component is a Go function that accepts a context and returns a tree of HTML nodes. Wrap reusable components with `pkg.Component` (or `pkg.PropsComponent`) so the runtime tracks state; the root passed to `pkg.NewApp` remains a plain function.\n\n```go\nvar Card = pkg.Component(func(ctx *pkg.Ctx, children []pkg.Item) pkg.Node {\n    return pkg.Div(\n        pkg.Class(\"border p-4 rounded shadow\"),\n        pkg.Fragment(children...),\n    )\n})\n\nfunc App(ctx *pkg.Ctx) pkg.Node {\n    return pkg.Div(\n        pkg.Class(\"container mx-auto\"),\n        Card(ctx, pkg.Text(\"I am inside a card!\")),\n    )\n}\n```\n\n### Server-Side State\nState lives in memory on the server for the session. Hooks like `UseState` read and update it; calling the setter triggers a re-render.\n\n```go\ncount, setCount := pkg.UseState(ctx, 0)\nsetCount(count + 1) // triggers re-render and patch\n```\n\n### Event Handling and DOM Actions\nEvents can be wired directly with `pkg.On`/`pkg.OnWith` on elements; no JavaScript needed. The runtime forwards the browser event to a Go handler, runs the logic, and patches the DOM. When DOM actions or element data are needed (e.g., `getBoundingClientRect`), use a generated element ref (e.g., `UseDiv`, `UseButton`) that bundles actions.\n\n```go\n// Direct handler without a ref\npkg.Button(\n    pkg.Text(\"Click\"),\n    pkg.On(\"click\", func(evt pkg.Event) pkg.Updates {\n        return nil\n    }),\n)\n\n// DOM actions via generated refs\nbox := pkg.UseDiv(ctx)    // includes ElementActions\nbtn := pkg.UseButton(ctx) // includes ButtonActions\n\nbtn.OnClick(func(evt pkg.ClickEvent) pkg.Updates {\n    rect, err := box.GetBoundingClientRect()\n    if err == nil \u0026\u0026 rect != nil {\n        _ = rect.Width // use measurements\n    }\n    return nil\n})\n\nreturn pkg.Div(\n    pkg.Div(pkg.Attach(box), pkg.Text(\"Measure me\")),\n    pkg.Button(pkg.Attach(btn), pkg.Text(\"Get bounds\")),\n)\n```\n\n## Getting Started\n- Install: `go get github.com/eleven-am/pondlive`\n- Minimal app: create a root component `func(ctx *pkg.Ctx) pkg.Node`, then:\n  ```go\n  app, _ := pkg.NewApp(Root)\n  http.ListenAndServe(\":8080\", app.Handler())\n  ```\n- Dev bundle: `pkg.NewApp(Root, pkg.WithDevMode())` serves `/static/pondlive-dev.js`.\n\n## Quick Start (Counter)\n```go\npackage main\n\nimport (\n    \"log\"\n    \"net/http\"\n\n    \"github.com/eleven-am/pondlive/pkg\"\n)\n\nfunc Counter(ctx *pkg.Ctx) pkg.Node {\n    count, setCount := pkg.UseState(ctx, 0)\n\n    btn := pkg.UseButton(ctx)\n    btn.OnClick(func(evt pkg.ClickEvent) pkg.Updates {\n        setCount(count + 1)\n        return nil\n    })\n\n    return pkg.Div(\n        pkg.H1(pkg.Text(\"Counter\")),\n        pkg.Button(pkg.Attach(btn), pkg.Text(\"+\")),\n        pkg.P(pkg.Textf(\"Clicked %d times\", count)),\n    )\n}\n\nfunc main() {\n    app, _ := pkg.NewApp(Counter, pkg.WithDevMode())\n    log.Println(\"http://localhost:8080\")\n    log.Fatal(http.ListenAndServe(\":8080\", app.Handler()))\n}\n```\n\n\n## Examples\n- Run: `cd examples/counter \u0026\u0026 go run .`\n- Other samples: `examples/countdown`, `examples/auth`, `examples/scoped-styles`, `examples/stream-chat`.\n\n## Data Flow \u0026 Lifecycle\n1. Initial render: request hits the server; the root component renders HTML; response is sent.\n2. Connect: the client JS connects via PondSocket at `/live`.\n3. Events: browser events travel over the socket to Go handlers.\n4. Re-render: state changes trigger re-render; the diff is computed server-side.\n5. Patch: minimal patch is streamed back; the DOM updates in place.\n\n## Hooks Overview\n- `UseState`: in-memory state per session.\n- `UseEffect`: side effects with optional deps and cleanup.\n- `UseMemo`: memoized compute by deps.\n- Element refs (`UseDiv`, `UseButton`, etc.): stable references plus DOM actions.\n- `UseRef`: generic stable ref.\n- `UseContext` / `UseProvider`: shared values through the tree.\n- `UseSlots` / `UseScopedSlots`: render children/slots.\n- `UseScript`: attach client JS and exchange messages.\n- `UseHandler`: register HTTP handlers mounted under PondLive.\n- `UseUpload`: manage uploads.\n- `UseStream`: render streaming data rows.\n- `UseStyles`: scoped CSS.\n- `UseMetaTags`: set meta tags.\n- `UseHeaders`, `UseCookie`: manage response headers/cookies.\n- `UseDocument`: document-level settings.\n- `UseErrorBoundary`: access error batch for error handling UI.\n- `UseHydrated`: runs effect only after WebSocket connection is established.\n- `UsePresence`: manage presence animations and timed visibility.\n\n## Routing\n\nPondLive includes a server-side router that handles URL changes without full page reloads. Route components receive both the context and a `Match` object containing route parameters.\n\n```go\nfunc App(ctx *pkg.Ctx) pkg.Node {\n    return pkg.Routes(\n        ctx,\n        pkg.Route(ctx, pkg.RouteProps{Path: \"/\", Component: Home}),\n        pkg.Route(ctx, pkg.RouteProps{Path: \"/about\", Component: About}),\n        pkg.Route(ctx, pkg.RouteProps{Path: \"/users/:id\", Component: UserProfile}),\n    )\n}\n\nfunc Home(ctx *pkg.Ctx, match pkg.Match) pkg.Node {\n    return pkg.Div(pkg.Text(\"Welcome\"))\n}\n\nfunc UserProfile(ctx *pkg.Ctx, match pkg.Match) pkg.Node {\n    userID, _ := match.Param(\"id\")\n    return pkg.Div(pkg.Textf(\"User: %s\", userID))\n}\n```\n\n## The JavaScript Bridge (UseScript)\n\nSome functionality requires code running in the browser — integrating a map library, managing focus, or running animations. `UseScript` bridges a Go component to a client-side closure.\n\n```go\nscript := pkg.UseScript(ctx, `\n    function(el, transport) {\n        const onHighlight = (data) =\u003e { el.style.background = data.color }\n\n        // Listen for messages from Go\n        transport.on(\"highlight\", onHighlight)\n\n        // Send message to Go\n        transport.send(\"ready\", { time: Date.now() })\n\n        // Cleanup when component unmounts\n        return () =\u003e {\n            el.style.background = \"\" // remove side-effects if desired\n        }\n    }\n`)\n\n// Listen for messages from JS\nscript.On(\"ready\", func(val any) {\n    log.Println(\"JS is ready:\", val)\n})\n\n// Send message to JS\nscript.Send(\"highlight\", map[string]any{\"color\": \"red\"})\n\nreturn pkg.Div(pkg.Attach(script), pkg.Text(\"I have JS attached\"))\n```\n\n## Serving\n- `app.Handler()` is the HTTP handler.\n- PondLive handles `/live` (PondSocket) and serves the client asset at `/static/pondlive.js` (dev variant in dev mode).\n\n## State and Session\n- State is per-session, in memory on the server.\n- Options: `WithDevMode`, `WithDOMTimeout`, `WithIDGenerator`, `WithContext`, `WithPubSub`.\n- Session IDs default to random; can be overridden.\n\n## Styling and Meta\n- `UseStyles` for component-scoped CSS.\n- `UseMetaTags` for `\u003ctitle\u003e`/`\u003cmeta\u003e` updates.\n- `UseHeaders`/`UseCookie` for per-request HTTP headers/cookies.\n\n## Quick Snippets\n- Form handling: attach `pkg.On(\"submit\", ...)` on `Form` or use generated form ref actions.\n- Redirect during initial render: set redirect in the request state helpers (see session transport in codebase).\n- DOM measurements: `div := pkg.UseDiv(ctx); rect, _ := div.GetBoundingClientRect()`.\n\n## Acknowledgments\n\nPondLive is built on [PondSocket](https://github.com/eleven-am/pondsocket), a WebSocket library that provides the bidirectional communication layer between server and client.\n\n## Author\n\nRoy Ossai ([@eleven-am](https://github.com/eleven-am))\n\n## License\n\nMIT (see [LICENSE](LICENSE)).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feleven-am%2Fpondlive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feleven-am%2Fpondlive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feleven-am%2Fpondlive/lists"}