πConditional React props with TypeScript
a prop which should only be set when another prop has a specific value.
Relationships between React component props can make you feel the pinch. This article will be your road-map to conditional props pattern employed using Typescript. I will propose different situations and demonstrate the answers to these questions:
How can we create a dependent relationship between several props using TypeScript?
What can we do to have it generate TypeScript errors when a relationship is broken?
Conflicting properties
Working on a design system, I had to create an avatar component. To pass props to the avatar component, different conditions were present:
If i pass the
iconprop i can't pass thesrcpropIf i pass the
srcprop i can't pass theiconprop
Here an example for the simple avatar component without the conditions
type AvatarProps = {
icon?: JSX.Element;
src?: string;
children:React.ReactNode;
};
export const Avatar = (props: AvatarProps): JSX.Element => {
const { icon, src } = props;
return (
<div>
{icon && icon}
{JSON.stringify(src)}
{children}
</div>
);
};If we import the component while passing both props, the component will not raise any errors.
Therefore, we have to provide an indication for the developer to tell them that passing the two in the same time is forbidden by just throwing a typescript error.
To achieve that , we can create union type using two types that reflect the two scenarios our component supports:
For those of you who are already familiar with TypeScript, that should be sufficient information
However, in just a few lines of code, there is a lot going on. Let's break it down into chunks if you're wondering about what it all means and how it all works.
CommonProps is your typical props definition in TypeScript. Itβs for all of the βCommonβ props that figure in all scenarios and that arenβt dependent on other props. In addition to children, there might be shadow, size, shape, etc.
ConditionalProps is where the magic happens. Itβs whatβs called a βdiscriminated union.β Itβs union of object definitions.
Letβs break it down further and weβll come back to see how the discriminated union works for us.
The first part of the discriminated union is when the icon prop is defined, In this case, we want the src prop to be invalid. It shouldnβt be able to be set.
The second part is when the icon prop is unspecified (undefined). Then we can pass the src props with no problems
So now back to the entire discriminated union. Itβs saying that the configuration for the icon and src props can either be the first case or the second case.
Props becomes the intersection of CommonProps and ConditionalProps.
Props is the combination of the two types. So itβll have all the properties from CommonProps as well as this dependent relationship we created with ConditionalProps.
Now finally, in the Avatar component, both the icon and src props will be of there type respectively JSX.Element | undefined and string | undefined So their types come out straightforward as if you hadnβt created the dependent relationship.
Now if we try to provide both props, we will see a TypeScript error:

Conditional prop variation
I needed to create a component with different variants, for each variant we have a set of props .
We want those props to be provided only when a matching variant is selected.
in our case we have 3 variants "text" | "number" | "element"
If the we chose to set the
varianttotext, we need to have amessageprop of typestring, and we can't setcomponentNamepropIf the we chose to set the
varianttonumber, we need to have amessageprops of typenumber, and we can't setcomponentNamepropIf we do pass the
variantaselement, here we can use finallycomponentNamealso themessageprop will become of typeJSX.Element
Lets take a look at this example
Once we set the variant prop , TypeScript narrows down componentβs type to their respective desired properties and tells you what you need to provide
Conditional props for collection with generic type
For our next use case, letβs try defining conditional props for a Select component. Our component needs to be flexible enough to accept an array of strings or objects for its options property.
If the component receives an array of objects, we want the developer to specify which fields of those objects we should use as a label and value.
Conditional types for collection property
To match the object that the user provide to the select. we can use generics in TypeScript.
In our second type, we change the options prop from Array<Object> to Array<T> for our generic object. The client has to provide an array of items of the generic object type.
We're using the keyof keyword to tell TypeScript that we're expecting labelProp and valueProp to be generic object fields.
Now when you try to provide valueProp or labelProp, youβll see a nice autocomplete suggestion based on the fields of the options items.
However, there is a minor change that we must make in order to avoid certain issues. We want to make sure that the generic object we've been given is a custom object rather than a primitive, such as a string:
Here we changed the union type by ternary operator to check if our generic type is a string, and based on that, we set our componentβs type to the appropriate option.
Hereβs a link to the code sandbox for this tutorial.
Last updated
Was this helpful?