# An island in a LiveView: socket, hooks and events — Mercury LMS > LM1. One LiveSocket per page; `MercuryHook`'s `mounted`/`updated`/`reconnected`/`destroyed`; props that follow the LiveView's assigns as patches; events up with `pushEvent` and down with `push_event` + `handleEvent`. ## Read this when You connect an island to its LiveView: open the socket, send an event from the island, answer it on the server, or push a server event to one island. ## The contract - Socket paths: echd_os `socket "/live"`; echo_edu `socket "/_edu/live"`, read by EDU's boot from `` (else `"/live"`). - One LiveSocket per page: `new LiveSocket(path, Socket, { params: { _csrf_token }, hooks })`. The import map points `@fiberfx/phoenix` and `@fiberfx/phoenix_live_view` at one served copy each. - `updated()`: `data-use-diff="true"` → `applyPatch(props, data-props-diff)`, else full `data-props`; streams patched from `data-streams-diff`. `reconnected()`: full `data-props`, streams rebuilt from `{}`. `destroyed()`: `render(null)` and every `handleEvent` ref made through the hook removed. - Each update publishes a new top-level props object; untouched nested values keep identity. - Server side, `diff` on (default): only assigns LiveView marks changed are sent; a key with no old value → `replace /`, else `Jsonpatch.diff` under `/`. - Hook functions (props, or `useLiveMercury()`; `null` outside an island): `pushEvent(event, payload?, onReply?)`, `pushEventTo(phxTarget, event, payload?, onReply?)`, `handleEvent(event, cb)` → ref, `removeHandleEvent(ref)`, `upload(name, files)`, `uploadTo(target, name, files)`. - EDU server events: one name, `"edu:island"` (`SERVER_EVENT`), envelope `%{id, event, payload}`; filter with `isAddressedTo(payload, islandId)` from `@echd/edu-boot/island`. - `Link`: `patch` → `data-phx-link="patch"`; `navigate` → `"redirect"` (same `live_session`); `replace` → `data-phx-link-state="replace"`; `href` alone → plain link. ## Minimal example ```tsx import { useEffect } from "@fiberfx/hyperscript/hooks"; import { useLiveMercury } from "@fiberfx/live-mercury"; import { isAddressedTo, SERVER_EVENT } from "@echd/edu-boot/island"; export function Ping(props: { islandId?: string }) { const live = useLiveMercury(); const handleEvent = live?.handleEvent; const removeHandleEvent = live?.removeHandleEvent; const islandId = props.islandId ?? ""; useEffect(() => { if (!handleEvent || !removeHandleEvent) return; const ref = handleEvent(SERVER_EVENT, (payload) => { if (isAddressedTo(payload, islandId)) console.log(payload.event, payload.payload); }); return () => removeHandleEvent(ref); }, [handleEvent, removeHandleEvent, islandId]); return ; } ``` ```elixir def handle_event("demo:clicked", %{"count" => count}, socket) do {:noreply, push_event(socket, "edu:island", %{id: "island-demo", event: "ack", payload: %{count: count}})} end ``` ## Never emit A second `new LiveSocket(...)` on a page that already has one; `phx-update` other than `"ignore"` on an island; an import from `react`. ## Next - LM2, building islands with mx: /live-mercury/building (/live-mercury/building/llms.txt) - Back: /live-mercury/islands/llms.txt - This companion: /live-mercury/liveview/llms.txt ## Navigation - [Read this page in the browser](/live-mercury/liveview) - [Agent index](/llms.txt) - [All English companions](/llms-full.txt) - [Sitemap](/sitemap.xml) - [Course companion](/live-mercury/llms.txt)