Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kripod/react-polymorphic-box
Building blocks for strongly typed polymorphic components in React.
https://github.com/kripod/react-polymorphic-box
component polymorphism react typescript
Last synced: 15 days ago
JSON representation
Building blocks for strongly typed polymorphic components in React.
- Host: GitHub
- URL: https://github.com/kripod/react-polymorphic-box
- Owner: kripod
- License: mit
- Created: 2020-02-15T17:49:56.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2022-08-25T05:53:06.000Z (about 2 years ago)
- Last Synced: 2024-10-29T01:13:21.314Z (16 days ago)
- Topics: component, polymorphism, react, typescript
- Language: TypeScript
- Size: 1.28 MB
- Stars: 348
- Watchers: 7
- Forks: 14
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
- awesome-list - react-polymorphic-box
README
# react-polymorphic-box
Building blocks for strongly typed polymorphic components in React.
[![npm](https://img.shields.io/npm/v/react-polymorphic-box)](https://www.npmjs.com/package/react-polymorphic-box)
[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/kripod/react-polymorphic-box.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/kripod/react-polymorphic-box/context:javascript)
[![Travis (.com)](https://img.shields.io/travis/com/kripod/react-polymorphic-box)](https://travis-ci.com/kripod/react-polymorphic-box)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](https://commitizen.github.io/cz-cli/)## 💡 Motivation
Popularized [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.:
```jsx
import { Box } from 'react-polymorphic-box';
import { Link } from 'react-router-dom';GitHub
About
```While this pattern has been encouraged by several libraries, typings had [lacked support for polymorphism](https://blog.andrewbran.ch/polymorphic-react-components/), missing benefits like:
- Automatic code completion, based on the value of the `as` prop
- Static type checking against the associated component's inferred props
- HTML element name validation## 📚 Usage
A `Heading` component can demonstrate the effectiveness of polymorphism:
```jsx
Heading
Subheading
```Custom components like the previous one may utilize the package as shown below.
```tsx
import { Box, PolymorphicComponentProps } from "react-polymorphic-box";// Component-specific props should be specified separately
export type HeadingOwnProps = {
color?: string;
};// Merge own props with others inherited from the underlying element type
export type HeadingProps<
E extends React.ElementType
> = PolymorphicComponentProps;// An HTML tag or a different React component can be rendered by default
const defaultElement = "h2";export function Heading({
color,
style,
...restProps
}: HeadingProps): JSX.Element {
// The `as` prop may be overridden by the passed props
return ;
}
```### Typing external components
Alternatively, 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):
```tsx
import { PolymorphicComponent } from "react-polymorphic-box";
import styled from "styled-components";// Component-specific props
export type HeadingProps = {
color?: string;
};// An HTML tag or a different React component can be rendered by default
const defaultElement = "h2";export const Heading: PolymorphicComponent<
HeadingProps, // Merged with props from the underlying element type
typeof defaultElement // Default element type (optional, defaults to 'div')
> = styled(defaultElement)`
color: ${(props) => props.color};
`;
```### Forwarding Refs
Library authors should consider encapsulating reusable components, [passing a ref](https://reactjs.org/docs/forwarding-refs.html) through each of them:
```tsx
import { Box } from "react-polymorphic-box";export const Heading: (
props: HeadingProps
) => React.ReactElement | null = React.forwardRef(
(
{ color, style, ...restProps }: HeadingProps,
ref: typeof restProps.ref
) => {
return (
);
}
);
```The 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:
```tsx
import { useRef } from "react";function App() {
const ref = useRef(null);
return It works!;
}
```