CodePark/ questions / TypeScript with React
START PRACTICINGPRACTICE
Frontend question · TypeScript › TypeScript with React

You are building a Button that wraps the native <button>. How do you accept all native button props plus your own?

type ButtonProps = React.ComponentPropsWithoutRef<"button"> & {
  variant?: "primary" | "ghost";
};

function Button({ variant = "primary", className, ...rest }: ButtonProps) {
  return <button className={`btn btn-${variant} ${className ?? ""}`} {...rest} />;
}

<Button type="submit" disabled onClick={save}>Save</Button>

Use React.ComponentProps<"button"> to get every native button prop, and intersect it with your own props using &. Then spread the remaining props onto the <button>.

React.ComponentProps<"button"> gives every prop the native element accepts:

  • onClick
  • disabled
  • type
  • aria-* attributes, and so on

The explicit variants ComponentPropsWithoutRef and ComponentPropsWithRef also work.

Destructure the props you handle yourself, and spread the rest onto the element. Callers can then pass any native attribute without you listing it. Merge className instead of overwriting it.

Sometimes one of your props has the same name as a native prop but a different type. Remove the native one first with Omit<React.ComponentProps<"button">, "type">. Otherwise the intersection creates a conflicting type.

The same pattern works for any element, such as "input" or "a".

  • Use React.ComponentProps<"button"> or ComponentPropsWithoutRef to get all native props.
  • Intersect the native props with your custom props using &.
  • Spread ...rest onto the native element.
  • Use Omit to remove native props that you redefine with a different type.