{"id":13469656,"url":"https://github.com/kripod/react-polymorphic-box","last_synced_at":"2025-04-12T17:45:19.158Z","repository":{"id":41269634,"uuid":"240761253","full_name":"kripod/react-polymorphic-box","owner":"kripod","description":"Building blocks for strongly typed polymorphic components in React.","archived":false,"fork":false,"pushed_at":"2022-08-25T05:53:06.000Z","size":1338,"stargazers_count":347,"open_issues_count":5,"forks_count":14,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-03T20:11:06.508Z","etag":null,"topics":["component","polymorphism","react","typescript"],"latest_commit_sha":null,"homepage":null,"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/kripod.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"kripod"}},"created_at":"2020-02-15T17:49:56.000Z","updated_at":"2025-01-07T21:02:59.000Z","dependencies_parsed_at":"2022-08-02T23:15:25.073Z","dependency_job_id":null,"html_url":"https://github.com/kripod/react-polymorphic-box","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kripod%2Freact-polymorphic-box","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kripod%2Freact-polymorphic-box/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kripod%2Freact-polymorphic-box/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kripod%2Freact-polymorphic-box/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kripod","download_url":"https://codeload.github.com/kripod/react-polymorphic-box/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248609541,"owners_count":21132915,"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":["component","polymorphism","react","typescript"],"created_at":"2024-07-31T15:01:49.048Z","updated_at":"2025-04-12T17:45:19.140Z","avatar_url":"https://github.com/kripod.png","language":"TypeScript","readme":"# react-polymorphic-box\n\nBuilding blocks for strongly typed polymorphic components in React.\n\n[![npm](https://img.shields.io/npm/v/react-polymorphic-box)](https://www.npmjs.com/package/react-polymorphic-box)\n[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/kripod/react-polymorphic-box.svg?logo=lgtm\u0026logoWidth=18)](https://lgtm.com/projects/g/kripod/react-polymorphic-box/context:javascript)\n[![Travis (.com)](https://img.shields.io/travis/com/kripod/react-polymorphic-box)](https://travis-ci.com/kripod/react-polymorphic-box)\n[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](https://commitizen.github.io/cz-cli/)\n\n\u003cimg src=\"./assets/demo.gif\" alt=\"Animated demonstration of package capabilities\" width=\"706\"\u003e\n\n## 💡 Motivation\n\nPopularized [by Styled Components v4](https://medium.com/styled-components/announcing-styled-components-v4-better-faster-stronger-3fe1aba1a112), the `as` prop allows changing the HTML tag rendered by a component, e.g.:\n\n```jsx\nimport { Box } from 'react-polymorphic-box';\nimport { Link } from 'react-router-dom';\n\n\u003cBox as=\"a\" href=\"https://github.com/kripod\"\u003eGitHub\u003c/Box\u003e\n\u003cBox as={Link} to=\"/about\"\u003eAbout\u003c/Box\u003e\n```\n\nWhile this pattern has been encouraged by several libraries, typings had [lacked support for polymorphism](https://blog.andrewbran.ch/polymorphic-react-components/), missing benefits like:\n\n- Automatic code completion, based on the value of the `as` prop\n- Static type checking against the associated component's inferred props\n- HTML element name validation\n\n## 📚 Usage\n\nA `Heading` component can demonstrate the effectiveness of polymorphism:\n\n```jsx\n\u003cHeading color=\"rebeccapurple\"\u003eHeading\u003c/Heading\u003e\n\u003cHeading as=\"h3\"\u003eSubheading\u003c/Heading\u003e\n```\n\nCustom components like the previous one may utilize the package as shown below.\n\n```tsx\nimport { Box, PolymorphicComponentProps } from \"react-polymorphic-box\";\n\n// Component-specific props should be specified separately\nexport type HeadingOwnProps = {\n  color?: string;\n};\n\n// Merge own props with others inherited from the underlying element type\nexport type HeadingProps\u003c\n  E extends React.ElementType\n\u003e = PolymorphicComponentProps\u003cE, HeadingOwnProps\u003e;\n\n// An HTML tag or a different React component can be rendered by default\nconst defaultElement = \"h2\";\n\nexport function Heading\u003cE extends React.ElementType = typeof defaultElement\u003e({\n  color,\n  style,\n  ...restProps\n}: HeadingProps\u003cE\u003e): JSX.Element {\n  // The `as` prop may be overridden by the passed props\n  return \u003cBox as={defaultElement} style={{ color, ...style }} {...restProps} /\u003e;\n}\n```\n\n### Typing external components\n\nAlternatively, you can also type your custom components by using the `PolymorphicComponent` type. This is especially handy when working with external libraries that already expose polymorphic components. Here's an example implementing the Heading component from above using [styled-components](https://styled-components.com):\n\n```tsx\nimport { PolymorphicComponent } from \"react-polymorphic-box\";\nimport styled from \"styled-components\";\n\n// Component-specific props\nexport type HeadingProps = {\n  color?: string;\n};\n\n// An HTML tag or a different React component can be rendered by default\nconst defaultElement = \"h2\";\n\nexport const Heading: PolymorphicComponent\u003c\n  HeadingProps, // Merged with props from the underlying element type\n  typeof defaultElement // Default element type (optional, defaults to 'div')\n\u003e = styled(defaultElement)\u003cHeadingProps\u003e`\n  color: ${(props) =\u003e props.color};\n`;\n```\n\n### Forwarding Refs\n\nLibrary authors should consider encapsulating reusable components, [passing a ref](https://reactjs.org/docs/forwarding-refs.html) through each of them:\n\n```tsx\nimport { Box } from \"react-polymorphic-box\";\n\nexport const Heading: \u003cE extends React.ElementType = typeof defaultElement\u003e(\n  props: HeadingProps\u003cE\u003e\n) =\u003e React.ReactElement | null = React.forwardRef(\n  \u003cE extends React.ElementType = typeof defaultElement\u003e(\n    { color, style, ...restProps }: HeadingProps\u003cE\u003e,\n    ref: typeof restProps.ref\n  ) =\u003e {\n    return (\n      \u003cBox\n        as={defaultElement}\n        ref={ref}\n        style={{ color, ...style }}\n        {...restProps}\n      /\u003e\n    );\n  }\n);\n```\n\nThe component can then receive a `ref` prop _([live demo](https://codesandbox.io/s/react-polymorphic-box-forwarding-refs-2l81h)),_ just like a regular HTML element:\n\n```tsx\nimport { useRef } from \"react\";\n\nfunction App() {\n  const ref = useRef\u003cHTMLHeadingElement\u003e(null);\n  return \u003cHeading ref={ref}\u003eIt works!\u003c/Heading\u003e;\n}\n```\n","funding_links":["https://github.com/sponsors/kripod"],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkripod%2Freact-polymorphic-box","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkripod%2Freact-polymorphic-box","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkripod%2Freact-polymorphic-box/lists"}