{"id":16222666,"url":"https://github.com/moishinetzer/remix-live-loader","last_synced_at":"2025-03-19T11:31:40.284Z","repository":{"id":207162955,"uuid":"710018385","full_name":"moishinetzer/remix-live-loader","owner":"moishinetzer","description":"Utility helpers and a practical demonstration of using Server-Sent Events for real-time data updates in Remix","archived":false,"fork":false,"pushed_at":"2023-11-14T23:57:11.000Z","size":138,"stargazers_count":50,"open_issues_count":2,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-09T01:12:31.450Z","etag":null,"topics":["remix","server-sent-events","typescript"],"latest_commit_sha":null,"homepage":"https://www.youtube.com/watch?v=_7yJEC124jM","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moishinetzer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2023-10-25T20:58:31.000Z","updated_at":"2025-03-04T16:46:20.000Z","dependencies_parsed_at":"2024-10-27T20:32:44.730Z","dependency_job_id":"bf4fcbd1-3951-4b77-9dd9-b78f5bb9b999","html_url":"https://github.com/moishinetzer/remix-live-loader","commit_stats":null,"previous_names":["moishinetzer/remix-live-loader"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moishinetzer%2Fremix-live-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moishinetzer%2Fremix-live-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moishinetzer%2Fremix-live-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moishinetzer%2Fremix-live-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moishinetzer","download_url":"https://codeload.github.com/moishinetzer/remix-live-loader/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243989109,"owners_count":20379648,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["remix","server-sent-events","typescript"],"created_at":"2024-10-10T12:14:53.653Z","updated_at":"2025-03-19T11:31:36.533Z","avatar_url":"https://github.com/moishinetzer.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Remix Live Loader\n\n## Introduction\n\nThis repo demonstrates how to use Server-Sent Events to invalidate data on other clients in real-time. It showcases a practical implementation of real-time data updates across multiple clients, ensuring that all users see the most current data as it changes.\n\nFor the best demo of this repo, how it was implemented, and how to use it, I highly recommend watching the following video:\n\n[Server-Sent Events in Remix](https://www.youtube.com/watch?v=_7yJEC124jM)\n\n## Getting Started\n\nTo implement this functionality, incorporate the following files into your app.\n\n- [`app/utils/emitter.server.ts`](app/utils/emitter.server.ts): Manages the event emission across various clients.\n\n- [`app/utils/create-event-stream.server.ts`](app/utils/create-event-stream.server.ts): Sets up an event stream for listening to specific events.\n\n- [`app/utils/use-live-loader.ts`](app/utils/use-live-loader.ts): Extends `useLoaderData` for real-time data revalidation.\n\n## Usage\n\n### emitter\n\n```tsx\nconst emitter: EventEmitter;\n```\n\nAn EventEmitter singleton used across all requests to emit events to the event stream. Example use case: Emitting a 'new-message' event when a new chat message is received.\n\n### createEventStream\n\n```tsx\nfunction createEventStream(request: Request, eventName: string): EventStream;\n```\n\nThis function initializes an event stream that listens for the specified event name. It sends an event with the current timestamp as data and includes cleanup logic for memory optimization.\n\n### useLiveLoader\n\n```tsx\nfunction useLiveLoader\u003cT\u003e(): SerializeFrom\u003cT\u003e;\n```\n\nThis function extends useLoaderData, automatically revalidating all data upon event stream triggers. Ideal for real-time data updates on pages like live chat or notifications.\n\nListens to events being emitted to the current path + `/stream` and revalidates the data when events are received.\n\n## Walkthrough\n\n1. **Stream Setup**: Create `stream.tsx` in the relevant directory. This route will manage the event stream. For example, for a `/chat` route, set up a corresponding `/chat/stream`.\n\n2. **Event Listening**: Use `createEventStream` in the `stream` route's loader function to listen for events. For a chat application, this could be listening for new messages in a chat room.\n\n```tsx\nimport type { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { createEventStream } from \"~/utils/create-event-stream.server\";\n\nexport function loader({ request, params }: LoaderFunctionArgs) {\n  // Here we are listening for events emitted to \"chat\" and returning an event stream\n  return createEventStream(request, \"chat\");\n}\n```\n\n3. **Data Revalidation**: Implement `useLiveLoader` in your data-serving route to automatically revalidate data with each event. In a chat application, this ensures that the chat view updates in real-time as new messages arrive.\n\n```tsx\nimport { useLiveLoader } from \"~/utils/use-live-loader\";\nimport { json } from \"@remix-run/node\";\n\nexport async function loader() {\n  let chats = await db.chats.findMany();\n\n  return json({\n    chats,\n  });\n}\n\nexport default function Chat() {\n  // Here we are using the useLiveLoader hook to get the data from the loader function\n  // and revalidate it whenever the event stream is triggered\n  let { chats } = useLiveLoader\u003ctypeof loader\u003e();\n\n  return (\n    \u003cdiv\u003e\n      \u003ch1\u003eChat\u003c/h1\u003e\n      \u003cul\u003e\n        {chats.map((chat) =\u003e (\n          \u003cli key={chat.id}\u003e{chat.message}\u003c/li\u003e\n        ))}\n      \u003c/ul\u003e\n\n      \u003cForm method=\"post\" action=\"/chat\"\u003e\n        \u003cinput type=\"text\" name=\"message\" /\u003e\n        \u003cbutton type=\"submit\"\u003eSend\u003c/button\u003e\n      \u003c/Form\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n4. **Triggering Updates**: Utilize the `emitter` in routes where you want to trigger updates (like after creating a new chat message). This ensures all clients connected to the event stream receive real-time updates.\n\n```tsx\nimport { emitter } from \"~/utils/emitter.server\";\nimport { json, type ActionFunctionArgs } from \"@remix-run/node\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n  let formData = await request.formData();\n  let message = formData.get(\"message\");\n\n  await db.chats.create({\n    data: {\n      message,\n    },\n  });\n\n  // Here we are emitting an event to the \"chat\" event stream\n  // which will trigger a revalidation of the data in the useLiveLoader hook\n  // for all clients listening to the event stream\n  emitter.emit(\"chat\");\n\n  return null;\n}\n```\n\n## Acknowledgements\n\nSpecial thanks to [Alex Anderson](https://twitter.com/ralex1993) and his [great talk](https://www.youtube.com/watch?v=cAYHw_dP-Lc) at RemixConf 2023 which inspired this repo.\n\nAlso, a shoutout to the Remix team and [Brooks Lybrand](https://twitter.com/BrooksLybrand) for hosting me and for all the support.\n\n## Contributing\n\nContributions are welcome! Feel free to open issues for bugs or feature requests, or submit PRs for improvements. Please ensure your contributions are well-documented and tested.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoishinetzer%2Fremix-live-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoishinetzer%2Fremix-live-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoishinetzer%2Fremix-live-loader/lists"}