78

What is the correct way to type and use the className prop in a custom component? I used to be able to do this:

class MyComponent extends React.Component<MyProps, {}> {
  ...
}

and then use my component via:

<MyComponent className="my-class" />

Note that I would not define className in MyProps, though React was previously typed to support this usage.

Now, I am now seeing this type error:

Property 'className' does not exist on type 'IntrinsicAttributes & 
IntrinsicClassAttributes<Component<{}, ComponentState>> & Readonly<{ 
childr...'

What is the correct way to define / type my component that will allow me to use className when using my component?

7 Answers 7

103

You can use the HTMLAttributes type, for example:

class MyComponent extends React.Component<MyProps & React.HTMLAttributes<HTMLDivElement>, {}> {
    render() {
        return <div className={ this.props.className }>My Div</div>
    }
}

That way you can pass any of the properties that a html element might need.

If you only need the className property then you can do this:

class MyComponent extends React.Component<MyProps & { className: string }, {}> {
    render() {
        return <div className={ this.props.className }>My Div</div>
    }
}

Or simply add it to your MyProps type.

Sign up to request clarification or add additional context in comments.

1 Comment

Amazing, thanks. Perfect to also use the spread operator and pass any prop that is valid for a <div> element.
52

For someone who are looking solution for functional components, as I was.

type Props = {
  className?: string
}

const MyComponent: React.FC<Props> = (props) => (
  <div className={props.className}>{props.children}</div>
)

export default MyComponent

or if you want to declare interface separately:

interface OnlyClassNameInterface extends React.FC<{className: string}> {}

const MyComponent: OnlyClassNameInterface = (props) => (
  <div className={props.className}>{props.children}</div>
)

export default MyComponent

and you can move interface to another file

import React from 'react'

type MixProps<P> = P & {className?: string}

export interface OnlyClassNameInterface<P = {}> extends React.FC<MixProps<P> {}

2 Comments

This answer gets to the point, specific and clean solution.
This is the correct solution for me. However, I had to extend the type on the functional component to be Props & React.HTMLAttributes<HTMLDivElement>
19

I myself always use the HTMLProps type by getting the className from it like this :

HTMLProps<HTMLElement>["className"];

It also works perfectly fine with TailwindCSS class names autocomplete functionality .

4 Comments

If the prop name is different than className (in the component) then this doesn't work (looses TS suggestions).
@Md.A.Apu The question is about correct way to type and use the className prop in a custom component so the prop name is always className .
@MehdiFaraji when the prop name is className then actually you only need string as type, I've tested this. Btw, do you know how to type it when prop name is something else?
@Md.A.Apu Yes you can extend the component interface with extra types like this : interface InputProps extends InputHTMLAttributes<HTMLInputElement>
2

add a react-native-class-name.polyfill.d.ts

import 'react-native';
// polyfill className prop for react-native Components
declare module 'react-native' {
  interface TextProps {
    className?: string;
  }
  interface PressableProps {
    className?: string;
  }
  interface TextInputProps {
    className?: string;
  }

  interface ViewProps {
    className?: string;
  }
  interface InputAccessoryViewProps {
    className?: string;
  }

  interface ImagePropsBase {
    className?: string;
  }

  interface TouchableWithoutFeedbackProps {
    className?: string;
  }
  // others StyleProp<?> in node_modules/@types/react-native extends up show, should not define again.
}

2 Comments

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
God bless you for this, thanks alot
1

For react-native typescript with nativewind, use

/// <reference types="nativewind/types" />

See from the documenation

Comments

0

Doing some search, I found that it is now suggested to use React.PropsWithChildren.

If you want to admit an attribute like classname, the code would be something like this

type AdditionalProps = {
    className?: string
}
type MyComponentProps = React.PropsWithChildren<AdditionalProps>;

export default function Text(props: MyComponentProps) {
    return (<p {...props}>
        {props.children}
    </p>)
}

This logrocket article is a good place to understand more about it.

Comments

0

How about:

import type { HTMLProps } from "react";

type Props = Pick<HTMLProps<HTMLElement>, "className">

class MyComponent extends React.Component<Props, {}> { }

// Or:
function MyComponent(props: Props) { }

Of course you can use {className?: string} as suggested by others, but using the method above gives you the ability to easily add other standard props as well, including children from React.

import type { HTMLProps } from "react";

type Props = Pick<HTMLProps<HTMLElement>, "className" | "children">

function MyComponent({className, children}: Props) {
  return (<div className={className}>{children}</div>);
}

You can also be more specific:

import type { ComponentProps } from "react";

type Props = Pick<ComponentProps<'div'>, "className" | "children", "title">

function MyComponent({className, children, title}: Props) {
  return (
     <div className={className} title={title}>
       {title && <h1>{title}</h1>
       {children}
     </div>
  );
}

and of course, you can extend with anything you like:

import type { ComponentProps } from "react";

type Props = Pick<ComponentProps<'div'>, "className" | "children"> & {
  count: number;
}

function MyComponent({className, children, count}: Props) {
  return (
     <div className={className} title={title}>
       {count && <h1>{count}</h1>
       {children}
     </div>
  );
}

In other words, it's a more useful way generally, so why not use get used to using it as a pattern?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.