DEV Community

Cover image for How to extend HTML elements with React
Douglas Moura
Douglas Moura

Posted on • Updated on • Originally published at douglasmoura.dev

How to extend HTML elements with React

Most of the work needed to create custom HTML elements that fit the design system of your company resides styling and adding your own props. So, let's say you have to create a custom Button, that should receive a children prop and should have DOM access via ref. That's how you can do it:

import { forwardRef } from 'react';

type ButtonProps = {
  loading?: boolean; // custom prop
} & React.PropsWithChildren<React.ComponentPropsWithRef<'button'>>;

const Button: React.FC<ButtonProps> = forwardRef(
  ({ loading, children, ...props }, ref) => {
    return (
      <button data-loading={loading} {...props} ref={ref}>
        {children}
      </button>
    );
  }
);

export default Button;
Enter fullscreen mode Exit fullscreen mode

We use the PropsWithChildren generic interface that gives the children prop and receive React.ComponentPropsWithRef<'button'>, that passes all props that a button can receive.

Of course, you can change the interface ComponentPropsWithRef for ComponentPropsWithoutRef and drop the forwardRef function on the definition of your component (although, I do not recomend it - refs may be useful later on your application):

type ButtonProps = {
  loading?: boolean; // custom prop
} & React.PropsWithChildren<React.ComponentPropsWithoutRef<'button'>>;

const Button: React.FC<ButtonProps> = ({ loading, children, ...props }) => {
  return (
    <button data-loading={loading} {...props} ref={ref}>
      {children}
    </button>
  );
};

export default Button;
Enter fullscreen mode Exit fullscreen mode

You may, even, drop the interface PropsWithChildren, but on doing that, you'd have to implement the children prop by yourself:

type ButtonProps = {
  loading?: boolean; // custom prop
  children?: React.ReactNode;
} & React.ComponentPropsWithoutRef<'button'>;

const Button: React.FC<ButtonProps> = ({ loading, children, ...props }) => {
  return (
    <button data-loading={loading} {...props} ref={ref}>
      {children}
    </button>
  );
};

export default Button;
Enter fullscreen mode Exit fullscreen mode

Want more? Check the live implementation on StackBlitz

Top comments (0)