{"id":13773397,"url":"https://github.com/alexkrolick/react-renderless","last_synced_at":"2025-08-03T15:34:20.554Z","repository":{"id":57343483,"uuid":"105114889","full_name":"alexkrolick/react-renderless","owner":"alexkrolick","description":"Utilities for creating and working with renderless React components","archived":false,"fork":false,"pushed_at":"2018-02-15T19:12:12.000Z","size":39,"stargazers_count":40,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-06T03:49:13.211Z","etag":null,"topics":["composition","hoc","react","reducer","render-prop","renderless-components","state","state-management","stateprovider-component"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/alexkrolick.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":"2017-09-28T07:15:20.000Z","updated_at":"2024-01-06T12:42:28.000Z","dependencies_parsed_at":"2022-09-12T07:00:24.254Z","dependency_job_id":null,"html_url":"https://github.com/alexkrolick/react-renderless","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexkrolick%2Freact-renderless","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexkrolick%2Freact-renderless/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexkrolick%2Freact-renderless/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexkrolick%2Freact-renderless/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexkrolick","download_url":"https://codeload.github.com/alexkrolick/react-renderless/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242667466,"owners_count":20166296,"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":["composition","hoc","react","reducer","render-prop","renderless-components","state","state-management","stateprovider-component"],"created_at":"2024-08-03T17:01:15.142Z","updated_at":"2025-03-09T08:31:06.413Z","avatar_url":"https://github.com/alexkrolick.png","language":"JavaScript","readme":"# react-renderless 🖇\n\nUtilities for creating and working with renderless React components.\n\n*What is a \"renderless component\"?* A renderless component is the opposite of a stateless component. It does not implement a render method. Instead, renderless components are composed with stateless functional components to create UI elements.\n\n- [Usage](#usage)\n- [API](#api)\n  - [StateProvider Component](#stateprovider-component)\n  - [withRender Higher-Order Component](#withrender-higher-order-component)\n- [Examples](#examples)\n  - [Textboxes](#textboxes-codepen)\n  - [Reducer](#reducer-codepen)\n  - [More Examples on Codepen](#more-examples-on-codepen)\n- [Inspiration](#inspiration)\n- [License](#license)\n\n## Usage\n\n### Script\n\n```html\n\u003cscript src=\"https://unpkg.com/react-renderless\"\u003e\u003c/script\u003e\n```\n\n```jsx\nconst { StateProvider, withRender } = reactRenderless\n```\n\n### Package\n\n```bash\nyarn add react-renderless\n# OR\nnpm install --save react-renderless\n```\n\n```jsx\n// commonjs\nconst { StateProvider, withRender } = require(\"react-renderless\") \n// es module\nimport { StateProvider, withRender } from \"react-renderless\" \n```\n\n## API\n\n### StateProvider Component\n\nInstead of extending React.Component for renderless components, extend StateProvider.\n\n```js\nStateProvider.propTypes = {\n  children: PropTypes.func, // pass either children or render\n  render: PropTypes.func, // pass either children or render\n  initialState: PropTypes.object, // optional\n}\n```\n\n- The [render prop](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce) will be called with 2 arguments: `(props, context)` where props is `{...this.props, ...this.state, ...this.handlers}`. The prop should either be `render` or the only child of the parent. The render prop must return an element when called (not a component class)!\n\n- Initial state: The component's initial state can be set by passing a prop or by setting an `initialState` getter on the class.\n\n- Handlers: An object that provides all the actions needed to modify the state. It is initiated one time, at mount. Handlers that need to be bound to the component instance should use arrow functions for declaration. (This will _not_ create additional functions in each render.)\n\n```jsx\nclass SimpleState extends StateProvider {\n  get initialState() {\n    return {\n      foo: \"bar\",\n    };\n  }\n\n  get handlers() {\n    return {\n      set: (key, value) =\u003e this.setState({ [key]: value }),\n    };\n  }\n}\n\nconst App = () =\u003e (\n  \u003cSimpleState\n    render={({foo}) =\u003e \u003cb\u003e{foo}\u003c/b\u003e}\n    initialState={{foo: 'baz'}}\n  /\u003e\n)\n\n// renders \"baz\" because the initialState prop overrides the getter\n\n```\n\n### withRender Higher-Order Component\n\n`withRender` combines a container and presenter (renderless and stateless) into a new component that acts like a normal React component. Under the hood it simply passes the presenter as the render prop to the renderless component.\n\n\n\n```jsx\nconst Combined = withRender(MyStateProvider, MyRenderFunction)\n\nconst App = () =\u003e \u003cCombined initialState={{foo: 'baz'}} /\u003e\n```\n\n`withRender` is curried so it can either be with 1 argument to create a factory for a stateful component or 2 to create a new component immediately.\n\n```jsx\nconst textStateFactory = withRender(TextState)\nconst TextInput = textStateFactory(Input)\nconst BigTextInput = textStateFactory(BigInput)\n```\n\n## Examples\n\n#### Textboxes [Codepen](https://codepen.io/alexkrolick/pen/RLVprZ/)\n\n[\u003cimg width=\"365\" alt=\"screen shot 2017-09-28 at 1 14 30 am\" src=\"https://user-images.githubusercontent.com/1571667/30955993-774923ea-a3ea-11e7-8cc9-65978c654b21.png\"\u003e](https://codepen.io/alexkrolick/pen/RLVprZ/)\n\n```jsx\nconst Input = ({ text, setText }) =\u003e \u003cinput onChange={setText} value={text} /\u003e;\n\nclass Text extends StateProvider {\n  get handlers() {\n    return {\n      setText: e =\u003e this.setState({ text: e.target.value })\n    };\n  }\n}\n\nclass UpperText extends StateProvider {\n  get initialState() {\n    return {\n      text: \"\"\n    };\n  }\n\n  get handlers() {\n    return {\n      setText: e =\u003e this.setState({ text: e.target.value.toUpperCase() })\n    };\n  }\n}\n\nclass LowerText extends StateProvider {\n  get handlers() {\n    return {\n      setText: e =\u003e this.setState({ text: e.target.value.toLowerCase() })\n    };\n  }\n}\n\nconst TextInput = props =\u003e \u003cText {...props}\u003e{Input}\u003c/Text\u003e;\nconst UpperTextInput = withRender(UpperText, Input);\n\nconst App = () =\u003e (\n  \u003cdiv\u003e\n    \u003cTextInput initialState={{ text: \"\" }} /\u003e TextInput \u003cbr /\u003e\n    \u003cUpperTextInput /\u003e UpperTextInput \u003cbr /\u003e\n    \u003cLowerTextInput initialState={{ text: \"\" }} /\u003e LowerTextInput\n  \u003c/div\u003e\n);\n\nReactDOM.render(\u003cApp /\u003e, document.body);\n```\n\n### Reducer [Codepen](https://codepen.io/alexkrolick/pen/eGWEXZ?editors=0010)\n\n[\u003cimg width=\"279\" alt=\"screen shot 2017-09-28 at 2 39 14 am\" src=\"https://user-images.githubusercontent.com/1571667/30959869-48ae6f20-a3f6-11e7-94e9-0457435fb4db.png\"\u003e](https://codepen.io/alexkrolick/pen/eGWEXZ?editors=0010)\n\n```jsx\nconst { StateProvider, withRender } = reactRenderless;\n\nclass Reducer extends StateProvider {\n  get handlers() {\n    const reducer = {\n      \"foo:update\": ({ foo }) =\u003e ({ foo: foo }),\n      \"bar:inc\": () =\u003e ({ bar }) =\u003e ({ bar: bar + 1 }),\n      \"bar:dec\": () =\u003e ({ bar }) =\u003e ({ bar: bar - 1 })\n    };\n    return {\n      action: (type, payload) =\u003e {\n        if (!reducer[type]) return this.setState({});\n        this.setState(reducer[type](payload));\n      }\n    };\n  }\n}\n\nconst App = () =\u003e (\n  \u003cReducer initialState={{ foo: \"\", bar: 0 }}\u003e\n    {({ action, ...state }) =\u003e (\n      \u003cdiv\u003e\n        \u003cp\u003e\n          \u003cb\u003eFoo\u003c/b\u003e\u0026nbsp;\n          \u003cinput\n            onChange={e =\u003e action(\"foo:update\", e.target.value)}\n            value={state.foo}\n          /\u003e\n        \u003c/p\u003e\n        \u003cp\u003e\n          \u003cb\u003eBar\u003c/b\u003e\u0026nbsp;\n          \u003cbutton onClick={() =\u003e action(\"bar:dec\")}\u003e-\u003c/button\u003e\n          \u003cspan\u003e{state.bar}\u003c/span\u003e\n          \u003cbutton onClick={() =\u003e action(\"bar:inc\")}\u003e+\u003c/button\u003e\n        \u003c/p\u003e\n      \u003c/div\u003e\n    )}\n  \u003c/Reducer\u003e\n);\n\nReactDOM.render(\u003cApp /\u003e, document.body);\n```\n\n### More Examples on Codepen\n\n- Global reducer using [React-Broadcast](https://github.com/ReactTraining/react-broadcast): https://codepen.io/alexkrolick/pen/NaYaXj\n- Nested, auto-namespaced global reducers: https://codepen.io/alexkrolick/pen/MEvGWG\n\n## Inspiration\n\n- [Recompose](https://github.com/acdlite/recompose) by Andrew Clark\n- [React Powerplug](https://github.com/renatorib/react-powerplug) by Renato Ribeiro\n- [Use a Render Prop!](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce) by Michael Jackson\n\n## Similar\n\n- [Unstated](https://github.com/jamiebuilds/unstated) by Jamie Kyle\n\n## License \n\n[MIT](./LICENSE)\n","funding_links":[],"categories":["Libraries"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexkrolick%2Freact-renderless","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexkrolick%2Freact-renderless","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexkrolick%2Freact-renderless/lists"}