An alternative to default props for React functional components

18 September 2019

I read recently that default props for functional components will one day be deprecated in React. It’s currently possible (as of September 2019) to set the default props for functional components like this:

/* --- greet.tsx --- */
import React from "react";

interface Props {
  name: string
  greeting: string
}

export function Greet({ name, greeting }: Props) {
	return (
		<h1>{greeting} {name}!</h1>
	);
}

const defaultProps: Pick<Props, "greeting"> = {
	greeting: "Hello"
}
Greet.defaultProps = defaultProps;

I quite like this approach, but I can understand why it would be deprecated. Adding a custom property onto a function ‘object’ does feel a little strange. Fortunately, we can use TypeScript’s / JavaScript’s default values and object deconstruction as an alternative.

In the code above, we have already deconstructed the props argument to easily access the name and greeting props (i.e. { name, greeting }: Props). We can use that deconstruction to also set a default value for greeting without using defaultProps:

/* --- greet.tsx --- */
import React from "react";

interface Props {
  name: string
  greeting: string 
}

export function Greet({ name, greeting = "Hello" }: Props) {
	return (
		<h1>{greeting} {name}!</h1>
	);
}

Just like with the defaultProps, greeting now has a default value of "Hello". This code for the component appears to be ok, but you’ll notice that TypeScript will not recognise the default value like it does for a default prop. If you don’t set the greeting prop when using the Greet component you will get an error similar to this:

To make TypeScript happy we need to mark the greeting property in the Prop interface as an optional property by appending a question mark to the end of the name:

/* --- greet.tsx --- */
import React from "react";

interface Props {
  name: string
  greeting?: string 
  // `greeting` is now marked as an optional property
}

export function Greet({ name, greeting = "Hello" }: Props) {
	return (
		<h1>{greeting} {name}!</h1>
	);
}

Now everything should be working 🎉

I’ve started using this technique in real applications to future-proof components and I’m actually starting to prefer it over the defaultProps approach. The Props interface now clearly communicates to other developers which props are required to be set and which ones are optional. It also requires fewer lines of code to implement than defaultProps. As an added bonus I’ve found that developers who a fairly new to TypeScript can get confused by the Pick type, so removing the need to use it makes the component more accessible for the entire team.