Yes, it is quite common to write components in such a way that they accept a className
property (but which must be applied to the component root explicitly by the component author). E.g. all components from material-ui
accept a classname (I'm not a big fan of material-ui but at least this aspect they got right).
You can either add it explicitly to your prop type
interface IPanel {
title?: string;
children: React.ReactNode;
className?: string;
}
...and use it in your component
import classnames from 'clsx'; // this is a popular package to combine classnames
const Panel = ({ title, children, className }: IPanel) => {
return (
<div className={classnames(styles.main, className)}>
{title && <Label type={LabelType.Title} bold text={title} />}
{children}
</div>
);
};
...or you can use one of the utility types from React. E.g. I often just allow all common HTML attributes plus children to be added as props (Yes, I know, my types are not overly precise)
import type { FunctionComponent, HTMLAttributes } from 'react';
export type CommonPropsFunctionComponent<TProps = {}> = FunctionComponent<
TProps & HTMLAttributes<Element>
>;
import { CommonPropsFunctionComponent } from './types';
interface IPanel {
title?: string;
}
const Panel: CommonPropsFunctionComponent<IPanel> = ({ title, children, className }) => {
return (
<div className={classnames(styles.main, className)}>
{title && <Label type={LabelType.Title} bold text={title} />}
{children}
</div>
);
};
Using the spread operator
You might be tempted to use it here; but beware: you must be careful to not overwrite your own local classname accidently.
To illustrate this, in this example:
- local
tabIndex
can be overwritten
- local
className
list cannot be overwitten, only extended
const SomeComponent= ({ children, className, ...rest }) => {
return (
<div tabIndex={0} {...rest} className={classnames(styles.root, className)}>
{children}
</div>
);
};