{"id":34995181,"url":"https://github.com/harmon25/react_surface","last_synced_at":"2025-12-27T02:03:41.583Z","repository":{"id":55116703,"uuid":"325666091","full_name":"harmon25/react_surface","owner":"harmon25","description":"Render React components with Phoenix LiveView + Surface","archived":false,"fork":false,"pushed_at":"2021-01-28T14:24:07.000Z","size":618,"stargazers_count":12,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-17T03:08:50.214Z","etag":null,"topics":["phoenix","react","surface"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/harmon25.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}},"created_at":"2020-12-30T23:07:20.000Z","updated_at":"2024-04-17T03:08:50.215Z","dependencies_parsed_at":"2022-08-14T12:30:57.997Z","dependency_job_id":null,"html_url":"https://github.com/harmon25/react_surface","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/harmon25/react_surface","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harmon25%2Freact_surface","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harmon25%2Freact_surface/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harmon25%2Freact_surface/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harmon25%2Freact_surface/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/harmon25","download_url":"https://codeload.github.com/harmon25/react_surface/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harmon25%2Freact_surface/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28069213,"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","status":"online","status_checked_at":"2025-12-27T02:00:05.897Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["phoenix","react","surface"],"created_at":"2025-12-27T02:02:32.543Z","updated_at":"2025-12-27T02:03:41.571Z","avatar_url":"https://github.com/harmon25.png","language":"Elixir","readme":"# ReactSurface\n\nCreates a Surface + LiveView container for rendering and updating React components.\n\n## Features\n- LiveView js hook which performs performs rendering or hydration of the component\n- A LiveView React Context that is injected with each render/update providing access to LiveView js functions\n- An optional SSR macro that can assist with generating placeholder or static react markup at compile time\n  - This runs a hydrate when mounting, as it was rendered server side\n\nIn the future:\n - Integrate Surface with [React Server Components](https://github.com/josephsavona/rfcs/blob/server-components/text/0000-server-components.md#alternatives) if possible i.e use Surface for writing the React Server Component (Surface AST -\u003e react/jsx?) \n\n## Client Setup\n\nCreate a components object to be passed into the hook generation function.\n\n*It is recommended to create a module that exports all your components used by react-surface.*\n`assets/js/components/index.js`:\n```js \nimport Component1 from \"./component1\"\nimport Component2 from \"./component2\" \n\nexport default {\n  Component1,\n  Component2,\n}\n```\n\n`assets/js/app.js`:\n```js \nimport components from \"./components\" \nimport { buildHook } from \"react-surface\";\n\n// pass component mapping to buildHook function\nconst reactSurfaceHook = buildHook(components)\n// setup liveview as normal, merging your hooks with react-surface hooks.\nlet liveSocket = new LiveSocket(\"/live\", Socket, {\n  {...otherhooks, ...reactSurfaceHook},\n  params: { _csrf_token: csrfToken },\n});\n```\n\n## SSR Setup\n\nIf not performing SSR via `use ReactSurface.SSR` this can be skipped.\n\n### Run mix task to create SSR script inside your asset directory.\n\n```\nmix gen_ssr_script\n```\n\n### Define `node_ssr` config in config.exs\n\nThis is used to configure `node_ssr` at compile time to understand where your components are, and how many instances you are running\n\n```elixir\nconfig :node_ssr,\n   # REQUIRED - This be the folder with assets, and a package.json file - passed to erlexec `:cd` option\n   assets_path: \"#{File.cwd!()}/assets\",\n   # this is the name of the script to be invoked - defaults to \"ssr.js\", can change it here.\n   script_name: \"ssr.js\"\n```\n\nOptional requirements:\n``` elixir\n  component_path: \"js/components\" # this is the default, relative path to your components directory from assets/.\n  component_ext: \".js\" # this is the default, used with nodejs require statements\n  count: 1 # this is the number of workers in the nodejs cluster - likely not necessary to have more than 1, unless rendering lots of components\n```\n\n## Example\n\n### On the Server in a Surface component\n\nWhere `props` is a map of JSON serializable values.\n\n```elixir\n \u003cReact component=\"HelloReactSurface\" props={{ %{name: \"Doug\"} }}/\u003e\n```\n\nSupply an `rid` when rendering multiple of the same component on the same page. (translated to DOM id attrubute)\n\n```elixir\n \u003cReact rid=\"unique_id\" component=\"HelloReactSurface\" props={{ %{name: \"Doug\"} }}/\u003e\n```\n\nThis will result in the following DOM being generated in Elixir.\n\n```html\n\u003cdiv\n  id=\"SHA1:8\"\n  rs-c=\"HelloReactSurface\"\n  rs-p=\"eyJuYW1lIjogIkRvdWcifQo\"\n  rs-m=\"r\"\n  phx-hook=\"_RS\"\n\u003e\n  \u003cdiv id=\"r \u003c\u003e SHA1:8\" phx-update=\"ignore\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n```\n\nThe props are being base64 encoded (no padding) for the DOM attribute\nThe ids are generated sha1 hashes, based on the component name, and optional rid prop\n\nWhen server rendering it is similar - but with the rendered component contents as a child of the inner div on initial render, and a different hook\n\n```html\n\u003cdiv\n  id=\"SHA1:8\"\n  rs-c=\"HelloReactSurface\"\n  rs-p=\"eyJuYW1lIjogIkRvdWcifQo\"\n  rs-m=\"h\"\n  phx-hook=\"_RS\"\n\u003e\n  \u003cdiv id=\"r \u003c\u003e SHA1:8\" phx-update=\"ignore\"\u003e\u003c!-- SSRed REACT ROOT --\u003e\u003c/div\u003e\n\u003c/div\u003e\n```\n\n## LiveView event handling\n\nLiveView events can be accessed via the `useLiveContext` React hook exported from the javascript package.\nThis hook returns an object with the functions: `{handleEvent, pushEvent, pushEventTo}`\n\nSee the [HelloReactSurface.js](demo/assets/js/components/HelloReactSurface.js) component for an example.\n\n\n## Installation\n\nIf [available in Hex](https://hex.pm/docs/publish), the package can be installed\nby adding `react_surface` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:react_surface, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n\nFrom github:\n\n```elixir\ndef deps do\n  [\n    {:react_surface, github: \"harmon25/react_surface\"}\n  ]\nend\n```\n\nAdd `react-surface` as a dep in your package.json\n\n```json\n{\n  \"react-surface\": \"file:../deps/react_surface\"\n}\n```\n\n## How to add react to your phoenix app\n\nIn your assets dir:\n\n```sh\nnpm install react react-dom --save\nnpm install @babel/preset-env @babel/preset-react --save-dev\n```\n\nAdd `\"@babel/preset-react\"` as a babel preset in `.babelrc`\n\nAdd the following to your `assets/webpack.config.js` file to ensure only a single react + react-dom is included in your bundle:\n\n```javascript\nmodule.exports = (env, options) =\u003e ({\n  // add:\n  resolve: {\n    alias: {\n      react: path.resolve(__dirname, './node_modules/react'),\n      'react-dom': path.resolve(__dirname, './node_modules/react-dom')\n    }\n  }\n// ...\n});\n```\n\n## Inspiration\n\nThis library is inspired by [react-phoenix](https://github.com/geolessel/react-phoenix) and [phoenix_live_react](https://github.com/fidr/phoenix_live_react).\n\nCheck em out if you want to use react components in an eex or leex templates.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharmon25%2Freact_surface","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharmon25%2Freact_surface","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharmon25%2Freact_surface/lists"}