Dat Nguyen
Dat Nguyen

Reputation: 197

Implement "as" prop in React component in TypeScript

I'm implementing a generic component which accept as prop as any HTML element in TypeScript.

<Component as='div' {...myProps} {...divProps} />

<Component as='button' {...myProps} {...buttonProps} />

I just get lost while trying to define my component's prop as any HTML element.

Please share your advice.

Thanks.

Upvotes: 12

Views: 10587

Answers (2)

yousef khalil
yousef khalil

Reputation: 160

The following article describes building a strongly typed polymorphic component.

Here is a summary of how to do it. The critical point is to use 2 generic types provided by react, these types are ElementType and ComponentPropsWithoutRef. ElementType represents a valid React Component or HTMLElement, ComponentPropsWithoutRef can infer the props of Component or an element.

import type {
  ComponentPropsWithoutRef,
  ElementType,
  PropsWithChildren,
} from 'react';

type PolymorphicAsProp<E extends ElementType> = {
  as?: E;
};

type PolymorphicProps<E extends ElementType> = PropsWithChildren<
  ComponentPropsWithoutRef<E> & PolymorphicAsProp<E>
>;

const defaultElement = 'p';

type TextProps<E extends ElementType = typeof defaultElement> =
  PolymorphicProps<E> & {
    color?: 'primary' | 'secondary';
  };

function Text<E extends ElementType = typeof defaultElement>({
  as,
  children,
  color = 'primary',
  className,
  ...restProps
}: TextProps<E>) {
  const Component = as ?? defaultElement;

  const customClassName = 'some-custom-class-name';

  return (
    <Component {...restProps} className={className + ' ' + customClassName}>
      {children}
    </Component>
  );
}

Here is some demo of Text component :

const Demo = () => {
  return (
    <div>
      <Text as="h1">Hello</Text>
      // With `a` you can pass href and it's strongly typed
      <Text as="a" href="/test-url">
        Hy
      </Text>
    </div>
  );
};

Upvotes: 8

b3hr4d
b3hr4d

Reputation: 4588

Use somthing like this:

interface IProps {
  as: React.ElementType;
}
export const Component = (props: IProps) => {
  const { as: Cmp = "div", ...rest } = props;

  return <Cmp {...rest} />;
};

Upvotes: 9

Related Questions