{"id":13850359,"url":"https://github.com/andrewbranch/merge-props","last_synced_at":"2025-04-09T11:10:35.897Z","repository":{"id":33009213,"uuid":"149573980","full_name":"andrewbranch/merge-props","owner":"andrewbranch","description":"Merges className, style, and event handler props for React elements","archived":false,"fork":false,"pushed_at":"2023-03-21T00:12:24.000Z","size":937,"stargazers_count":100,"open_issues_count":17,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T08:11:30.758Z","etag":null,"topics":["react"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/andrewbranch.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}},"created_at":"2018-09-20T08:05:53.000Z","updated_at":"2024-12-06T10:12:39.000Z","dependencies_parsed_at":"2024-01-16T04:31:52.637Z","dependency_job_id":"594ff46b-0789-4582-ae13-a9c55deec5d7","html_url":"https://github.com/andrewbranch/merge-props","commit_stats":{"total_commits":42,"total_committers":7,"mean_commits":6.0,"dds":0.2857142857142857,"last_synced_commit":"dfabe62918751f1d6379b84db87ef333183e9bd6"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewbranch%2Fmerge-props","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewbranch%2Fmerge-props/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewbranch%2Fmerge-props/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewbranch%2Fmerge-props/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrewbranch","download_url":"https://codeload.github.com/andrewbranch/merge-props/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248027407,"owners_count":21035594,"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":["react"],"created_at":"2024-08-04T20:01:08.392Z","updated_at":"2025-04-09T11:10:35.868Z","avatar_url":"https://github.com/andrewbranch.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","⚛️ React"],"sub_categories":["React-specific libs:"],"readme":"# merge-props [![Build Status](https://travis-ci.org/andrewbranch/merge-props.svg?branch=master)](https://travis-ci.org/andrewbranch/merge-props) [![codecov](https://codecov.io/gh/andrewbranch/merge-props/branch/master/graph/badge.svg)](https://codecov.io/gh/andrewbranch/merge-props) [![npm](https://img.shields.io/npm/v/merge-props.svg)](https://www.npmjs.com/package/merge-props) ![size](https://img.shields.io/bundlephobia/minzip/merge-props.svg)\n\nMerges React `className`, `style`, and event handlers (`onClick`, `onFocus`, `on{LiterallyEveryEvent}`) by the following rules:\n\n- `className` props are concatenated\n- `style` props are shallow merged with later values taking precedence\n- functions are run in sequence from left to right.\n\n## Installation\n\n```\nnpm install merge-props\n```\n\n## Example usage\n\n```js\nconst props = mergeProps(\n  { onClick: fn1 },\n  { onClick: fn2, className: 'blue' },\n  { onClick: fn3, className: 'button', styles: { display: 'block' } },\n  { styles: { display: 'flex', color: 'red' } }\n);\n\n\u003cbutton {...props}\u003eBest button ever\u003c/button\u003e\n```\n\nThe button will have a `className` of `\"blue button\"`, a `style` equal to `{ display: 'flex', color: 'red' }`,and when it is clicked, it will execute `fn1`, then `fn2`, then `fn3`, in that order.\n\n## Why is this useful?\n\nOne useful pattern for [render props](https://reactjs.org/docs/render-props.html) is having them pass props that are meant to be spread over the returned element to facilitate communication between the parent component and the render prop element. For example, consider a Tooltip component that decorate any DOM element or React component that accepts event handlers. If the Tooltip wants to avoid rendering a wrapper around its “trigger” element, it could use a render prop to inject `onMouseEnter`, `onMouseLeave`, `onFocus`, `onBlur`, and various WAI-ARIA attributes:\n\n```jsx\n\u003cTooltip text=\"Some extra information about this button\"\u003e\n  {props =\u003e \u003cbutton {...props}\u003eHovering this is great\u003c/button\u003e}\n\u003c/Tooltip\u003e\n```\n\nThis is a nifty pattern in environments where flexibility, reusability, and ability to customize are highly valued, because Tooltip can pass event handlers as `props` in order to attach what it needs to the target element while allowing the consumer to retain full control over what actually happens to those `props`.\n\nHowever, one minor weakness of this pattern is the level of boilerplate that needs to be introduced if you needed to merge those injected `props` with props of your own in an intelligent way. For example, lets say that Tooltip also injects a `className` (perhaps to change the cursor appearance of whatever is being hovered), but you also need to pass your own `className` to the button in order to turn it a pretty shade of blue. You’d have to do something like\n\n```jsx\n\u003cTooltip text=\"Some extra information about this button\"\u003e\n  {({ className, ...props }) =\u003e (\n    \u003cbutton {...props} className={className + ' pretty-blue'}\u003e\n      Hovering this is great\n    \u003c/button\u003e\n  )}\n\u003c/Tooltip\u003e\n```\n\nAnd it gets much messier if you needed to run your own event handlers in addition to the injected event handlers:\n\n```jsx\n\u003cTooltip text=\"Some extra information about this button\"\u003e\n  {({ className, onFocus, onBlur, ...props }) =\u003e (\n    \u003cbutton\n      {...props}\n      className={className + ' pretty-blue'}\n      onFocus={event =\u003e {\n        // BTW if Tooltip’s API changed to not inject this, it would break\n        onFocus(event);\n        myOwnOnFocus(event);\n      }}\n      onBlur={event =\u003e {\n        onBlur(event); // Sure hope this exists forever\n        myOwnOnBlur(event);\n      }}\n    \u003e\n      Hovering this is kind of sad\n    \u003c/button\u003e\n  )}\n\u003c/Tooltip\u003e\n```\n\nEnter `mergeProps`:\n\n```jsx\n\u003cTooltip text=\"Some extra information about this button\"\u003e\n  {(props) =\u003e (\n    \u003cbutton {...mergeProps(props, {\n      onFocus: myOwnOnFocus,\n      onBlur: myOwnOnBlur,\n      className: 'pretty-blue',\n      style: { alignSelf: 'flex-start' }\n    })}\u003e\n      I love button\n    \u003c/button\u003e\n  )}\n\u003c/Tooltip\u003e\n```\n\nIt runs Tooltip’s `onFocus` and `onBlur` right before your own handlers, combines Tooltip’s `className` with `pretty-blue`, and even merges your `style` prop with one that Tooltip might want to add.\n\n\n## Gotchas\n\n**I got an error like `Didn’t know how to merge prop 'foo'`. What?**\n\n`mergeProps` knows how to merge `className`, `style`, and event handlers (any functions, in practice). If you pass it anything else, it will happily pass it through:\n\n```jsx\nconst merged = mergeProps({ className: 'one' }, { className: 'two', role: 'button' });\nmerged.role; // 'button'\n```\n\nHowever, if it receives _two_ instances of the same prop by an unknown name, `mergeProps` is faced with a decision of how to merge two props that it knows nothing about. Rather than risk making a bad decision, especially one that may be hard to debug, it throws an error saying that it doesn’t know what to do:\n\n```jsx\n// What? How to merge two `role` props? ¯\\_(ツ)_/¯ \nconst merged = mergeProps({ role: 'button' }, { role: 'presentation' });\n// Error: Didn’t know how to merge prop 'role'.\n```\n\n## Related\n\n* [babel-plugin-jsx-merge-props](https://github.com/hooriza/babel-plugin-jsx-merge-props): Use merge-props automatically via a Babel plugin\n\n## Contributing\n\nChanges should be tested and have 100% coverage:\n\n```\nnpm test\n```\n\nDesign changes may be considered via an issue or PR.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewbranch%2Fmerge-props","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrewbranch%2Fmerge-props","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewbranch%2Fmerge-props/lists"}