Ilja
Ilja

Reputation: 46527

Extending component props to default html button props correctly

I am working on a button component that has variant prop to determine it's color. Here is simplified version

interface Props extends React.HTMLProps<HTMLButtonElement> {
  variant: 'yellow' | 'green';
}

function Button({ variant, ...props }: Props) {
  console.log(variant);

  return (
    <button
      type="button"
      {...props}
    />
  );
}

I am getting typescript error under my type that says:

(JSX attribute) ButtonHTMLAttributes.type?: "button" | "submit" | "reset" | undefined Type 'string' is not assignable to type '"button" | "submit" | "reset" | undefined'.ts(2322) index.d.ts(1872, 9): The expected type comes from property 'type' which is declared here on type 'DetailedHTMLProps, HTMLButtonElement>'

So I am not sure if I am extending to button props correctly? Perhaps there is another way?

Upvotes: 18

Views: 22906

Answers (3)

Guilhermo Masid
Guilhermo Masid

Reputation: 86

This is an alternative solution to this same problem, you can replace 'button' for any html tag

import { ComponentProps } from 'react';

interface ButtonProps extends ComponentProps<'button'> {
  variant: 'yellow' | 'green';
}

function Button({ variant, ...props }: ButtonProps) {

Upvotes: 2

Hasan Haghniya
Hasan Haghniya

Reputation: 2545

Update 2023

if you want to create button without any extra stuff you can extend React.ComponentPropsWithoutRef<"button"> and for people that trying to make reusable component you will need to forwardRef the underlying element and then you can use ComponentPropsWithRef:

interface Props extends React.ComponentPropsWithoutRef<"button"> {
  variant: 'yellow' | 'green';
}

why not using React.HTMLAttributes<HTMLButtonElement>?

this is what happens when you use:

export interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
  /* etc */
}

function App() {
  // Property 'type' does not exist on type 'IntrinsicAttributes & ButtonProps'
  return <Button type="submit"> text </Button>;
}

Old answers

Codesandbox Live Preview

what you're probably looking for is React.HTMLAttributes<HTMLButtonElement>

import * as React from "react";
import { render } from "react-dom";

import "./styles.css";

interface Props extends React.HTMLAttributes<HTMLButtonElement> {
  variant: 'yellow' | 'green';
}

function Button({ variant, ...props }: Props) {

  return (
    <button
      type="button"
      {...props}
    />
  );
}

const rootElement = document.getElementById("root");
render(<Button variant='green' />, rootElement);

to being more accurate we can use React.ButtonHTMLAttributes<HTMLButtonElement>

import * as React from "react";
import { render } from "react-dom";

import "./styles.css";

interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant: 'yellow' | 'green';
}

function Button({ variant, ...props }: Props) {

  return (
    <button
      type="button"
      {...props}
    />
  );
}

const rootElement = document.getElementById("root");
render(<Button variant="green" />, rootElement);

Upvotes: 32

acrazing
acrazing

Reputation: 2284

You should use ButtonHTMLAttributes rather than HTMLProps like this:

interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant: 'yellow' | 'green';
}

function Button({ variant, ...props }: Props) {
  console.log(variant);

  return (
    <button
      type="button"
      {...props}
    />
  );
}

Upvotes: 11

Related Questions