{"id":19614311,"url":"https://github.com/fpapado/precompose-props","last_synced_at":"2026-05-04T11:32:02.170Z","repository":{"id":57329671,"uuid":"121167568","full_name":"fpapado/precompose-props","owner":"fpapado","description":"Prop mapping made easy at \u003c1k","archived":false,"fork":false,"pushed_at":"2018-02-13T14:10:16.000Z","size":97,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-09T21:36:10.663Z","etag":null,"topics":["contramap","preact","props","react","theming"],"latest_commit_sha":null,"homepage":"https://npm.im/precompose-props","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/fpapado.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-11T21:11:05.000Z","updated_at":"2018-02-13T22:53:45.000Z","dependencies_parsed_at":"2022-09-16T17:50:30.380Z","dependency_job_id":null,"html_url":"https://github.com/fpapado/precompose-props","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fpapado%2Fprecompose-props","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fpapado%2Fprecompose-props/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fpapado%2Fprecompose-props/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fpapado%2Fprecompose-props/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fpapado","download_url":"https://codeload.github.com/fpapado/precompose-props/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240906645,"owners_count":19876680,"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":["contramap","preact","props","react","theming"],"created_at":"2024-11-11T10:51:03.112Z","updated_at":"2025-10-18T21:01:50.310Z","avatar_url":"https://github.com/fpapado.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Precompose-props\n\u003e Prop mapping made easy.\n\n[![travis](https://travis-ci.org/fpapado/precompose-props.svg?branch=master)](https://travis-ci.org/fpapado/precompose-props)\n[![npm](https://img.shields.io/npm/v/precompose-props.svg)](https://www.npmjs.com/package/precompose-props)\n\n## Table of Contents\n\n-   [Install](#install)\n-   [Motivation](#install)\n-   [Usage](#usage)\n-   [API](#api)\n-   [License](#license)\n\n## Install\nThis package is distributed via [npm](https://www.npmjs.com/get-npm).\n\n```shell\n$ npm install --save precompose-props\n# or\n$ yarn add precompose-props\n```\n\nThen import according to your modules model and bundler, such as [Rollup](https://rollupjs.org/guide/en) and [Webpack](https://webpack.js.org/)\n\n```js\n// ES6 Modules\n// For all possible functions to import, look at \"export\" in src/index.js\nimport { contramap, withTheme } from 'precompose-props';\n\n/// CommonJS modules\nvar precomposeProps = require('precompose-props');\n```\n\nA [UMD](https://github.com/umdjs/umd) version is also available on [unpkg](https://unpkg.com/):\n```html\n\u003cscript src=\"https://unpkg.com/precompose-props/dist/precompose-props.umd.js\"\u003e\u003c/script\u003e\n\n```\n\n## Motivation\n\nWhen working with React, I often want to make a component that \"wraps\" the props of another component, mapping from one set to another.\nThis is especially true when composing styles; I want to have a higher-level \"theme\" prop, that collects a set of lower-level \"style\" props.\nDoing this manually has some annoying boilerplate, and leads to more noise than I'd like.\n\nIdeally, I would be able to do something like this:\n\n```jsx\nconst Th = ({fontWeight, color, children}) =\u003e \u003cth style={{fontWeight, color}}\u003e{children}\u003c/th\u003e;\n\nconst Cell = withTheme({bold: {fontWeight: '7', color: 'black'}})(Th);\n```\n\n### Ideas\n\nIn his talk [\"Oh Composable World!\"](https://www.youtube.com/watch?v=SfWR3dKnFIo), Brian Lonsdorf (aka Dr. Boolean) goes over how to use `contramap` to compose props.\nA contramap in this context allows us to use a function pre-process a set of props, before passing the result on to a component. This is exactly what we want!\n(Also, that sounds like `mapStateToProps()` from `react-redux`).\n\nLet's define contramap:\n\n```js\nconst contramap = mapFn =\u003e Component =\u003e props =\u003e Component(mapFn(props));\n```\n\nIn his talk, he defines a `Comp = x =\u003e {...}` function, because there is a larger point about composition and concatenation for React (contravariance and monoids).\nI am not doing that, because calling .fold() would look out of place in our codebase. \nWould be simple to add and fun to try though :)\n\n## Usage\n```jsx\nimport React from \"react\";\nimport { render } from \"react-dom\";\nimport {\n  withTheme,\n  named,\n  toggle,\n  contramap,\n  concatAndMergeProps\n} from \"precompose-props\";\n\n// The lower component\nconst P = ({ measure, lineHeight, fontSize, fontWeight, children }) =\u003e (\n  \u003cp style={{ maxWidth: measure, lineHeight, fontWeight, fontSize }}\u003e\n    {children}\n  \u003c/p\u003e\n);\n\n// Higher component\nconst Text = withTheme({\n  bold: toggle({ fontWeight: \"700\" }),\n  kind: named(\n    { copy: { lineHeight: \"1.5\", measure: \"34em\" } },\n    { heading: { lineHeight: \"1.25\" } }\n  )\n})(P);\n\n// Equivalent forms\n// Using concatAndMergeProps directly with contramap\nconst TextAlt = contramap(\n  concatAndMergeProps({\n    bold: toggle({ fontWeight: \"700\" }),\n    kind: named(\n      { copy: { lineHeight: \"1.5\", measure: \"34em\" } },\n      { heading: { lineHeight: \"1.25\" } }\n    )\n  })\n)(P);\n\n// Using contramap with a custom function\n/*\nmapFakeToProps(({bold, kind, ...rest}) =\u003e \"Fill this in\");\nconst TextManual = contramap(mapFakeToProps)(P);\n*/\n\nconst App = () =\u003e (\n  \u003cdiv\u003e\n    \u003cText kind=\"heading\" fontSize=\"2rem\" bold\u003e\n      I am a Heading\n    \u003c/Text\u003e\n    \u003cText kind=\"copy\" fontSize=\"1rem\"\u003e\n      This is a text oh this is a text, and would you guess, this is copy text\n      with max widths and stuff.\n    \u003c/Text\u003e\n    \u003cTextAlt kind=\"copy\" fontSize=\"1rem\"\u003e\n      This is a text oh this is a text, and would you guess, this is copy text\n      with max widths and stuff.\n    \u003c/TextAlt\u003e\n  \u003c/div\u003e\n);\n\nrender(\u003cApp /\u003e, document.getElementById(\"root\"));\n```\n\n### Any abstraction you like\n\nAnother piece of wisdom by Dr. Boolean (I'm a fan, can't you tell?) is on API design (paraphrasing):\n\n-   A set of primitives\n-   A way to compose them\n-   A set of precomposed things\n\nArmed with that thought, here are the functions provided and their use cases, lower- to higher- level.\n\n**Low-level**\n\n-   `contramap`\n      apply a mapping function from one set of props to another, pass to component\n\n-   `concatProps`\n      concatenate props according to a specification\n\n-   `concatAndMergeProps`\n      concatenate props according to a specification, merges\n\n**Specification Utilities**\n\nA specification is of the form\n```ts\n{[K in higherProp]: mapFn}\n\nmapFn: higherPropValue =\u003e Partial\u003cLowerProps\u003e`.\n```\n\n-   `toggle(obj)(value)`\n      return obj if value is true\n\n-   `named(obj)(value)`\n      return the entry at obj[value]\n\n**Higher-level**\n\n-   `withTheme`\n      maps props with specification, merge other props\n\n-   `mapTheme`\n      maps props with specification, do not merge other props\n\nIf you see a pattern there, it's because the latter are simply compositions of the former!\nFunctions are curried by default. I have not used Ramda's `curry`; this is open to change.\n\n### Examples\n[Edit the example above on CodeSandbox](https://codesandbox.io/s/0107ywo83l)\n\n### API\n\n\u003c!-- Generated by documentation.js. Update this documentation by updating the source code. --\u003e\n\n#### Table of Contents\n\n## TODO\n\n-   Types\n-   Example folder\n\n## Inspiration\n- Brian Lonsdorf (drboolean) for his talks on functional JS. Even when the language is not the best for it, I\nwill occasionally run into cases where I think about concatenation or the order of operations.\n- Jason Miller (developit) for his package build setups, and `microbundle`. They were a great starting\npoint to figure out how to publish this damn thing.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffpapado%2Fprecompose-props","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffpapado%2Fprecompose-props","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffpapado%2Fprecompose-props/lists"}