{"id":21541516,"url":"https://github.com/reshape/react-components","last_synced_at":"2025-07-03T22:04:37.845Z","repository":{"id":72842970,"uuid":"97513799","full_name":"reshape/react-components","owner":"reshape","description":"render react components from custom elements","archived":false,"fork":false,"pushed_at":"2017-11-10T21:50:02.000Z","size":60,"stargazers_count":2,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-03T22:03:47.822Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reshape.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"contributing.md","funding":null,"license":"LICENSE.md","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-07-17T19:22:39.000Z","updated_at":"2018-08-27T02:45:12.000Z","dependencies_parsed_at":null,"dependency_job_id":"d3a03025-4431-4e8e-a11b-16f69440c14d","html_url":"https://github.com/reshape/react-components","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/reshape/react-components","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshape%2Freact-components","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshape%2Freact-components/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshape%2Freact-components/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshape%2Freact-components/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reshape","download_url":"https://codeload.github.com/reshape/react-components/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshape%2Freact-components/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263410758,"owners_count":23462295,"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":[],"created_at":"2024-11-24T05:04:57.063Z","updated_at":"2025-07-03T22:04:37.792Z","avatar_url":"https://github.com/reshape.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Reshape React Components\n\n[![npm](https://img.shields.io/npm/v/reshape-react-components.svg?style=flat-square)](https://npmjs.com/package/reshape-react-components)\n[![tests](https://img.shields.io/travis/reshape/react-components.svg?style=flat-square)](https://travis-ci.org/reshape/react-components?branch=master)\n[![dependencies](https://img.shields.io/david/reshape/react-components.svg?style=flat-square)](https://david-dm.org/reshape/react-components)\n[![coverage](https://img.shields.io/codecov/c/github/reshape/react-components.svg?style=flat-square)](https://codecov.io/gh/reshape/react-components)\n\nRender react components to static html and use them like custom elements.\n\n\u003e **Note:** This project is in early development, and versioning is a little different. [Read this](http://markup.im/#q4_cRZ1Q) for more details.\n\n### Installation\n\n`yarn add reshape-react-components`\n\n### Usage\n\nSetup is pretty simple -- just add the plugin to reshape and pass it an object with the key being the custom element name you want to be replaced with the rendered component, and the value as the actual component. Reshape will render your components and replace the given custom element names with the components' static html.\n\n```js\nconst React = require('react')\nconst reshape = require('reshape')\nconst renderComponents = require('reshape-react-components')\n\nconst MyComponent = ({ foo }) =\u003e {\n  return React.createElement('p', {}, `the value of foo is \"${foo}\"`)\n}\n\nconst html = \"\u003cmy-component foo='bar' /\u003e\"\n\nreshape({ plugins: [renderComponents({ 'my-component': MyComponent })] })\n  .process(html)\n  .then((res) =\u003e {\n    console.log(res.output()) // \u003cp\u003ethe value of foo is \"bar\"\u003c/p\u003e\n  })\n```\n\n#### Hydrating Initial State\n\nSo there is one case where you might want some additional logic to avoid duplication. Luckily, we have this logic ready to go, and will walk you through both the use case and solution here. So imagine you have a component like this:\n\n```js\nexport default class SortableList {\n  render () {\n    return (\n      \u003cul className='sortable'\u003e\n        \u003cspan className='sort-icon' /\u003e\n        {this.props.children}\n      \u003c/ul\u003e\n    )\n  }\n\n  componentDidMount () {\n    // some logic here to make this list sortable\n  }\n}\n```\n\nNow you set up the component through the plugin as such:\n\n```js\nconst renderComponents = require('reshape-react-components')\nconst SortableList = require('./sortable-list')\n\nrenderComponents({ 'sortable-list': SortableList })\n```\n\nAnd now in your html, you'd put down something like this:\n\n```html\n\u003cbody\u003e\n  \u003csortable-list\u003e\n    \u003cli\u003ewow\u003c/li\u003e\n    \u003cli\u003eamaze\u003c/li\u003e\n    \u003cli\u003every list\u003c/li\u003e\n  \u003c/sortable-list\u003e\n\u003c/body\u003e\n```\n\nOk, so you would get the rendered out `ul` with the classes and span elements you wanted, as expected. However, with this element, you definitely want to also client-side render it since it contains interactive elements. So if your client-side javascript, you run something like this:\n\n```js\nconst {render} = require('react')\nconst SortableList = require('./sortable-list')\n\nrender(\u003cSortableList /\u003e, document.body, document.querySelector('.sortable'))\n```\n\nOk so this would find the right element and add the javascript interactivity on top. But it would also remove all the contents of your list as soon as the javascript render loads in, because you just rendered an empty element in the code above. Oops! Let's fix that:\n\n```js\nconst {render} = require('react')\nconst SortableList = require('./sortable-list')\n\nrender(\n  \u003cSortableList\u003e\n    \u003cli\u003ewow\u003c/li\u003e\n    \u003cli\u003eamaze\u003c/li\u003e\n    \u003cli\u003every list\u003c/li\u003e\n  \u003c/SortableList\u003e,\n  document.body,\n  document.querySelector('.sortable')\n)\n```\n\nOk so this works, but now we have some seriously non-DRY code. Now our markup has to be repeated both in our html for the initial static render, and in the client-side js for the client render. Luckily, `reshape-react-components` has got your back. By default, it takes the initial html you used to render your react element, parsed into a reshape AST and compressed as efficiently as possible, and gives it to your element as a prop called `_state`. It also provides a helper that you can use to decompress and hydrate it into a vdom tree that can be directly rendered by react. So let's take advantage of this in our code and completely cut out all repetition - starting with our component.\n\nWhat we'll do here is put our compressed initial state on a data attribute so that our client-side js can pick it up and hydrate:\n\n```js\nexport default class SortableList {\n  render () {\n    return (\n      \u003cul className='sortable' data-state={this.props._state}\u003e\n        \u003cspan className='sort-icon' /\u003e\n        {this.props.children}\n      \u003c/ul\u003e\n    )\n  }\n\n  componentDidMount () {\n    // some logic here to make this list sortable\n  }\n}\n```\n\nYou can see on the top level `ul`, we placed an additional data prop. If you render this to the page, you'll see something like this:\n\n```html\n\u003cul class='sortable' data-state='3nko2ir2cR3i2nr2croi23nrc23='\u003e\u003c/ul\u003e\n```\n\nNow let's pick up that compressed initial state from out client side javascript:\n\n```js\nconst {render} = require('react')\nconst SortableList = require('./sortable-list')\nconst sortableEl = document.querySelector('.sortable')\nconsole.log(sortable.dataset.state)\n```\n\nLooking good -- now we can pull in `reshape-react-components`'s helper function that will hydrate the initial state as a vdom tree that's directly renderable by react. We just need to pass it the compressed initial state, and a remapping back from the custom element name to the actual component as we required it on the client side.\n\n```js\nconst {render} = require('react')\nconst SortableList = require('./sortable-list')\nconst {hydrateInitialState} = require('reshape-react-components/lib/browser')\n\nconst sortableEl = document.querySelector('.sortable')\nconst vdom = hydrateInitialState(sortableEl.dataset.state, {\n  'sortable-list': SortableList\n})\n\nconsole.log(vdom)\n```\n\nNote that for client side code, we require from `reshape-react-components/lib/browser`. Although full es-module compatibility would allow a selective import that avoided additional dependencies, at the moment both node stable and many build systems are *not* yet es-module compatible, so it's safer to import from a separate namespace to ensure there is no extra bloat for the client-side code.\n\n\u003e *Note:* If you're using webpack2, you can require `reshape-react-components/lib/browser.esm` to get a version that uses es modules\n\nIn the console log, you'll see that we have a full react vdom ready to go, using the right components everywhere you needed them. Now the last step is just to render it!\n\n```js\nconst {render} = require('react')\nconst SortableList = require('./sortable-list')\nconst {hydrateInitialState} = require('reshape-react-components/lib/browser')\n\nconst sortableEl = document.querySelector('.sortable')\nconst vdom = hydrateInitialState(sortableEl.dataset.state, {\n  'sortable-list': SortableList\n})\n\nrender(vdom, document.body, sortableEl)\n```\n\nAnd that's it! You'll see no visual difference, as react won't re-render existing html, but it will remove the `data-state` property and layer on the javascript interaction as soon as it loads. Perfect!\n\n### Passing Complex Data Into Components\n\nYou might find yourself in a situation in which you want to pass a large js object into your component at some point. This works fluidly with client-only react, since its javascript -\u003e javascript, but when you are rendering as static, the data is written as static and transmitted via http before being consumed by javascript. This causes two issues.\n\nFirst, html is parsed such that attribute values start and end with quotes. So if you have something like `\u003cdiv data=\"{ foo: \"bar\" }\"\u003e\u003c/div\u003e`, you now have a parsing issue. HTML parsers will think that as soon as it hits the second double quote, you are done with your attribute value, and now you've got screwed up data and invalid html.\n\nSecond, since you are going to be transmitting this data directly in your html, it is included directly in your http requests and will appear in your html. As such, it would be a benefit to compress the data to minimize size.\n\nThere are two ways that this can be accomplished. The first is to only pass data that you need into your component, attribute by attribute, as such:\n\n```html\n\u003cmy-component\n  something='xxx'\n  somethingElse='yyy'\n  wowMoreThings='zzz'\n  lastThing='aaa'\n\u003e\u003c/my-component\u003e\n```\n\nThe second way is to serialize your full object, pass it in a single attribute, and deserialze on the other end. This library exposes simple and efficient serialization and deserialization functions that you can use for this purpose. Using [spike](https://spike.cf) as an example here:\n\n```js\n// app.js\nconst renderComponents = require('reshape-react-components')\nconst MyComponent = require('./my-component')\n\nmodule.exports = {\n  reshape: htmlStandards({\n    locals: { encode: render.encode, data: { foo: 'bar' } },\n    appendPlugins: [renderComponents({ 'my-component': MyComponent })]\n  })\n}\n```\n\n```html\n\u003c!-- index.html --\u003e\n\u003cmy-component data='{{ encode(data) }}'\u003e\u003c/my-component\u003e\n```\n\n```js\n// my-component.js\nconst {h} = require('react')\nconst {decode} = require('reshape-react-components/lib/browser')\n\nmodule.exports = ({ data }) =\u003e {\n  const _data = decode(data)\n  return (\u003cp\u003eJSON.stringify(_data)\u003c/p\u003e)\n}\n```\n\n### License \u0026 Contributing\n\n- Details on the license [can be found here](LICENSE.md)\n- Details on running tests and contributing [can be found here](contributing.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freshape%2Freact-components","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freshape%2Freact-components","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freshape%2Freact-components/lists"}