CodeSandbox Example for this issue.
I have defined an icon library that exports several icons. Each Icon forwards a ref to its internal svg. Here's an example:
type ReactIconProps = SVGProps<SVGSVGElement>;
function CheckIconInner(props: ReactIconProps, ref: Ref<SVGSVGElement>) {
return (
<svg
fill="none"
height="1em"
ref={ref}
stroke="currentColor"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M20 6L9 17L4 12"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
/>
</svg>
);
}
const CheckIcon = forwardRef(CheckIconInner);
I want to use these icons as a prop in a button component so that the button can include an icon. See below:
type ReactButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
export interface ButtonProps extends ReactButtonProps {
Icon?: ComponentType<ReactIconProps>;
}
function Button({ children, Icon, ...props }: ButtonProps) {
return (
<button {...props}>
{Icon !== undefined && <Icon />}
{children}
</button>
);
}
export default function App() {
return <Button Icon={CheckIcon}>OK</Button>;
}
However, this approach throws a typescript error when an icon is passed to the button (<Button Icon={CheckIcon}>OK</Button>
):
TS2322: Type 'ForwardRefExoticComponent<Omit<ReactIconProps, "ref"> & RefAttributes<SVGSVGElement>>' is not assignable to type 'ComponentType<ReactIconProps> | undefined'.
Type 'ForwardRefExoticComponent<Omit<ReactIconProps, "ref"> & RefAttributes<SVGSVGElement>>' is not assignable to type 'FunctionComponent<ReactIconProps>'.
Types of parameters 'props' and 'props' are incompatible.
Type 'ReactIconProps' is not assignable to type 'Omit<ReactIconProps, "ref"> & RefAttributes<SVGSVGElement>'.
Type 'SVGProps<SVGSVGElement>' is not assignable to type 'RefAttributes<SVGSVGElement>'.
Types of property 'ref' are incompatible.
Type 'LegacyRef<SVGSVGElement> | undefined' is not assignable to type 'Ref<SVGSVGElement> | undefined'.
Type 'string' is not assignable to type 'Ref<SVGSVGElement> | undefined'.
The issue is that the icon requires a ref, but we are not passing it one.
Two questions:
- How can we specify that the ref passed to icon component is optional?
- Is the use of ref in the Icon component a good practice? I have seen lots of advice that refs should not be overused, but see that may component libraries use it extensively.
Edit
I found the answer to #2 in an issue in the heroicons repository - plenty of use cases!