npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@leafygreen-ui/polymorphic

v2.0.2

Published

LeafyGreen UI Kit Polymorphic

Downloads

3,434,230

Readme

Polymorphic

pol·y·morph (noun)

  • an organism, inorganic object or material which takes various forms.

pol·y·mor·phic (adjective)

  • occurring in several different forms, in particular with reference to species or genetic variation.
  • (of a feature of a programming language) allowing routines to use variables of different types at different times.

Polymorphic is a suite of types, hooks, components and factories that allows users to create components that can render as any HTML element or React component.

Usage

There are two main ways to use Polymorphic, depending on whether the as prop is defined internally by your component, or passed in as an external prop.

Basic usage

If the logic defining the as prop is defined internally within your component, use the standalone Polymorph component. A simple example of this may be rendering an element as a header or paragraph text depending on certain props passed in.

interface MyProps {
  someProp: string;
}

const MyComponent = (props: MyProps) => {
  const shouldRenderAs = props.isHeader ? 'h1' : 'p';
  return <Polymorph as={shouldRenderAs} {...props} />;
};

Extending Polymorphic behavior

If you want to expose as as a prop of your component, use the Polymorphic factory function and related hooks. This is likely the more common use case, since it allows you to create new polymorphic components (such as a Button component that can render as a link, etc.)

Note that any inherited props will be indeterminate in the factory function, since the value as is not known. (i.e. the attributes of rest in the following example are unknown).

interface MyProps {
  someProp: string;
}
const MyComponent = Polymorphic<MyProps>(({ as, ...rest }) => {
  const { Component, ref } = usePolymorphic(as);
  return <Component ref={ref} {...rest} />;
});

With Refs

Accepting a forwarded ref to a Polymorphic component is as simple as passing in a ref into the render function (same as React.forwardRef).

interface MyProps {
  someProp: string;
}
const MyComponent = Polymorphic<MyProps>(({ as, ...rest }, forwardedRef) => {
  const { Component } = usePolymorphic(as);
  return <Component ref={forwardedRef} {...rest} />;
});

React Server Components

React Server Components do not support React Client Component APIs, such as React.useRef. To avoid React Client APIs, use the usePolymorphicComponent and useInferredPolymorphicComponent hooks instead.

Note that forwardedRef is still allowed.

interface MyProps {
  someProp: string;
}
const MyComponent = Polymorphic<MyProps>(({ as, ...rest }, forwardedRef) => {
  const Component = usePolymorphicComponent(as);
  return <Component ref={forwardedRef} {...rest} />;
});

Inferred as prop

Components extended using the Polymorphic factory function can be made to infer the as prop value based on the href passed in. Ensure the custom props are wrapped in InferredPolymorphicProps, and use the useInferredPolymorphic hook. Make sure to pass both as and a rest object (that may contain href) into the hook.

export const MyInferredComponent = InferredPolymorphic<MyProps>(
  ({ as, ...rest }) => {
    const { Component, ref } = useInferredPolymorphic(as, rest);
    return (
      <Component ref={ref} {...rest}>
        {title}
      </Component>
    );
  },
);

//

<MyInferredComponent href="mongodb.design" />; // renders as <a>

Inferred as with a default value

Sometimes, when developing a component that uses Polymorphic, you'll want to set a default value for the as prop. For example, when creating a Button component, you may want to have the as prop default to the HTML button element. To set a default value for the inferred as value, you'll need to provide the default value both to TypeScript and React:

export const MyInferredComponentWithDefault = InferredPolymorphic<
  ExampleProps,
  'button'
>(({ as = 'button', title, ...rest }) => {
  const { Component, ref } = useInferredPolymorphic(as, rest);
  return (
    <Component ref={ref} {...rest}>
      {title}
    </Component>
  );
});

Note: When a component is InferredPolymorphic, the href will force the component to render as an anchor, and will override any default value.

<MyInferredComponentWithDefault />; // as <button>
<MyInferredComponentWithDefault href="mongodb.design" />; // as <button>

With Emotion styled API

Polymorphic also supports usage with Emotions styled API. To get TypeScript to accept the Polymorphic props you'll need to explicitly type your styled component as PolymorphicComponentType.

const StyledPolymorph = styled(Polymorph)`
  color: hotpink;
` as PolymorphicComponentType;

// or

const MyStyledComponent = styled(MyComponent)`
  color: hotpink;
` as typeof MyComponent;

This also works with InferredPolymorphic components

const StyledInferred = styled(MyInferredComponent)`
  color: hotpink;
` as typeof MyInferredComponent;

With styled props (and Typescript)

Since Polymorphic components are strictly typed, to use styled props with Typescript you will need to define the additional props you expect to use within styled, and pass these into styled as generic type.

interface StyledProps {
  color: string;
}

const MyStyledComponent = styled(MyComponent)<StyledProps>`
  color: ${props => props.color};
` as StyledComponent<
  StyledProps & PolymorphicProps<PolymorphicAs, ExampleProps>
  // or StyledProps & InferredProps<PolymorphicAs, ExampleProps>
>;

Note: TSDocs will not compile for styled polymorphs. This can be remedied by creating a wrapper around the styled function that explicitly returns a PolymorphicComponentType

Supported (but not recommended) usage

While it is possible to use the Polymorph component to extend polymorphic behavior, it can be much more verbose and error prone than using the Polymorphic factory function. For completeness, an example of how to do this is provided below:

type MyProps<T extends PolymorphicAs> = PolymorphicPropsWithRef<
  T,
  {
    someProp: string;
  }
>;

export const MyComponent = <T extends PolymorphicAs = 'div'>(
  { as, title, ...rest }: MyProps<T>,
  forwardedRef: PolymorphicRef<T>,
) => {
  return (
    <Polymorph as={as as PolymorphicAs} ref={forwardedRef} {...rest}>
      {title}
    </Polymorph>
  );
};

Prior art