{"id":21096864,"url":"https://github.com/klarna/remote-frames","last_synced_at":"2025-06-20T11:36:13.923Z","repository":{"id":26894506,"uuid":"105527849","full_name":"klarna/remote-frames","owner":"klarna","description":"Render a subset of the React tree to a different location, from many locations, without having to coordinate them","archived":false,"fork":false,"pushed_at":"2023-02-28T01:51:32.000Z","size":915,"stargazers_count":27,"open_issues_count":13,"forks_count":12,"subscribers_count":282,"default_branch":"master","last_synced_at":"2025-06-09T17:16:12.212Z","etag":null,"topics":["iframe","in-k","portals","react","react-dom"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/klarna.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}},"created_at":"2017-10-02T11:41:09.000Z","updated_at":"2022-09-09T05:17:41.000Z","dependencies_parsed_at":"2024-11-19T22:54:39.940Z","dependency_job_id":null,"html_url":"https://github.com/klarna/remote-frames","commit_stats":{"total_commits":140,"total_committers":23,"mean_commits":6.086956521739131,"dds":0.7714285714285715,"last_synced_commit":"315b4c2bb6f71bcaec2b5314a8c03a2714f7ddea"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/klarna/remote-frames","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarna%2Fremote-frames","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarna%2Fremote-frames/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarna%2Fremote-frames/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarna%2Fremote-frames/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/klarna","download_url":"https://codeload.github.com/klarna/remote-frames/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klarna%2Fremote-frames/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260937711,"owners_count":23085729,"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":["iframe","in-k","portals","react","react-dom"],"created_at":"2024-11-19T22:44:28.201Z","updated_at":"2025-06-20T11:36:08.905Z","avatar_url":"https://github.com/klarna.png","language":"JavaScript","readme":"# Deprecated\n\nNo longer being maintained.\n\n# @klarna/remote-frames\n\n[![Build Status](https://travis-ci.org/klarna/remote-frames.svg?branch=master)](https://travis-ci.org/klarna/remote-frames)\n[![npm version](https://img.shields.io/npm/v/@klarna/remote-frames.svg)](https://www.npmjs.com/package/@klarna/remote-frames)\n\nRender a subset of the React tree to a different location, from many locations, without having to coordinate them.\n\n## Usage\n\nSay that you have an HTML with two DOM nodes that you want to render to:\n\n```html\n\u003c!doctype html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003eRemote frame\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"dialogs-node\"\u003e\u003c/div\u003e\n    \u003cdiv id=\"main-content-node\"\u003e\u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n…and for some reason, you want elements in the React tree rendered under the `\"main-content-node\"` to be able to inject elements into the `\"dialogs-node\"`. The `RemoteFrame` allows you to send this elements to the remote tree (the one under `\"dialogs-node\"`).\n\n```js\nimport React, { Component } from 'react'\nimport { render } from 'react-dom'\nimport { RemoteFrame, RemoteFramesProvider } from '@klarna/remote-frames'\n\nconst Dialog1 = () =\u003e \u003carticle\u003e\n  \u003ch2\u003eLorem ipsum\u003c/h2\u003e\n\u003c/article\u003e\n\nconst Dialog2 = () =\u003e \u003csection\u003e\n  \u003ch3\u003eDolor sit amet\u003c/h3\u003e\n\u003c/section\u003e\n\nclass App extends Component {\n  constructor() {\n    super()\n\n    this.state = {\n      showDialog1: false,\n      showDialog2: false,\n    }\n  }\n\n  render() {\n    const { showDialog1, showDialog2 } = this.state\n\n    return \u003cRemoteFramesProvider\n      targetDomElement={Promise.resolve(\n        document.getElementById('dialogs-node')\n      )}\n      onFrameAdded={frameJSX =\u003e {\n        console.log(\n          'a new frame was added to the dialogs-node stack',\n          frameJSX\n        )\n      }}\n      onFrameRemoved={frameJSX =\u003e {\n        console.log(\n          'a frame was removed from the dialogs-node stack',\n          frameJSX\n        )\n      }}\n      onNoFrames={lastJSXRemoved =\u003e {\n        console.log(\n          'all frames have been removed from the stack',\n          lastJSXRemoved\n        )\n      }}\u003e\n      \u003cdiv\u003e\n        \u003ch1\u003eApp that demonstrates remote-frames\u003c/h1\u003e\n        \u003cbutton\n          onClick={() =\u003e this.setState({\n            showDialog1: !showDialog1\n          })}\u003e\n          {showDialog1 ? 'Hide Dialog 1' : 'Show Dialog 1'}\n        \u003c/button\u003e\n\n        \u003cbutton\n          onClick={() =\u003e this.setState({\n            showDialog2: !showDialog1\n          })}\u003e\n          {showDialog2 ? 'Hide Dialog 2' : 'Show Dialog 2'}\n        \u003c/button\u003e\n\n        {showDialog1 \u0026\u0026 \u003cRemoteFrame\u003e\n          \u003cDialog1 /\u003e\n        \u003c/RemoteFrame\u003e}\n\n        {showDialog2 \u0026\u0026 \u003cRemoteFrame\u003e\n          \u003cDialog2 /\u003e\n        \u003c/RemoteFrame\u003e}\n      \u003c/div\u003e\n    \u003c/RemoteFramesProvider\u003e\n  }\n}\n\nrender(\n  \u003cApp /\u003e,\n  document.getElementById('main-content-node')\n)\n```\n\nWhenever you click the \"Show\" / \"Hide\" buttons, the dialogs are sent to a React tree under the `\"dialogs-node\"`, and rendered one at a time. If there was no dialog being shown at the time, then the new dialog is added; if there was a dialog shown already, the new dialog is shown instead, but then if the new dialog is removed, the old dialog is shown again, as in a sort of stack.\n\nState of the elements inside the `RemoteFrame` is preserved, even when unmounted.\n\n### Missing RemoteFramesProvider\n\nIf there is no `RemoteFramesProvider` in the tree before the `RemoteFrame`, the content of `RemoteFrame` will just be rendered in place.\n\n### Context\n\nFor the React.context to be propagated to the new tree, you have to manually specify what props of the context you want to propagate:\n\n```js\nimport React, { Component } from 'react'\nimport { render } from 'react-dom'\nimport PropTypes from 'prop-types'\nimport { getContext, withContext } from 'recompose'\nimport {\n  RemoteFrame,\n  RemoteFramesProvider\n} from '@klarna/remote-frames'\n\nconst Dialog1 = getContext({ content1: PropTypes.string })(({content1}) =\u003e \u003carticle\u003e\n  \u003ch2\u003e{content1}\u003c/h2\u003e\n\u003c/article\u003e)\n\nconst Dialog2 = getContext({ content2: PropTypes.string })(({content2}) =\u003e \u003csection\u003e\n  \u003ch3\u003e{content2}\u003c/h3\u003e\n\u003c/section\u003e)\n\n\nconst App = withContext(\n  {\n    content1: PropTypes.string,\n    content2: PropTypes.string,\n  },\n  () =\u003e ({\n    content1: 'Hello Dialog 1',\n    content2: 'Hello Dialog 2',\n  })\n)(() =\u003e {\n  return \u003cRemoteFramesProvider\n    contextTypes={{\n      content1: PropTypes.string,\n    }}\n    targetDomElement={Promise.resolve(\n      document.getElementById('dialogs-node')\n    )}\u003e\n    \u003cdiv\u003e\n      \u003ch1\u003eApp that demonstrates remote-frames\u003c/h1\u003e\n      \u003cbutton\n        onClick={() =\u003e this.setState({\n          showDialog1: !showDialog1\n        })}\u003e\n        {showDialog1 ? 'Hide Dialog 1' : 'Show Dialog 1'}\n      \u003c/button\u003e\n\n      \u003cbutton\n        onClick={() =\u003e this.setState({\n          showDialog2: !showDialog1\n        })}\u003e\n        {showDialog2 ? 'Hide Dialog 2' : 'Show Dialog 2'}\n      \u003c/button\u003e\n\n      \u003cRemoteFrame\u003e\n        \u003cDialog1 /\u003e\n      \u003c/RemoteFrame\u003e\n\n      \u003cRemoteFrame\n        contextTypes={{\n          content2: PropTypes.string,\n        }}\u003e\n        \u003cDialog2 /\u003e\n      \u003c/RemoteFrame\u003e\n    \u003c/div\u003e\n  \u003c/RemoteFramesProvider\u003e\n})\n\nrender(\n  \u003cApp /\u003e,\n  document.getElementById('main-content-node')\n)\n```\n\n### Callbacks on `RemoteFramesProvider`\n\nTwo callbacks are available on `RemoteFramesProvider`:\n\n- `onFrameAdded`: gets called whenever another frame is added to the stack\n- `onNoFrames`: gets called whenever all frames are removed from the stack\n- `onFrameRemoved`: gets called whenever a frame is removed from the stack\n\n### Passing the `targetDomElement`\n\nThe `targetDomElement` used to render the new React tree can be passed directly to the `RemoteFramesProvider` as a prop, or it can be passed as a Promise, allowing you to wait until the targetDomElement is available (for example if it is rendered in another window).\n\nFrames stacked before the `targetDomElement` is available will be queued, so you will not lose any information.\n\n### Wrapping into `wrapperComponent`\n\nThe `wrapperComponent` (alongside with `wrapperComponentProps`) used to wrap `GlobalTarget` into HOC (for example if it is needed to wrap everything into `ThemeProvider`, etc.).\n\n```js\n\u003cRemoteFramesProvider\n  targetDomElement={document.getElementById('dialogs-node')}\n  wrapperComponent={ThemeProvider}\n  wrapperComponentProps={{ value: themeName }}\u003e\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklarna%2Fremote-frames","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fklarna%2Fremote-frames","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklarna%2Fremote-frames/lists"}