You can find a summary of the code updates in this pull request . Read on for explanations.
1. Card props
Let’s start with the props specified for the Card
component (we’ll get to children
in a minute). These will both be typed as strings. We could do some extra checking on the string format with a tool like zod (for example, verifying HEX or HSL or other valid CSS color formats), but that’s outside the scope of this workshop.
Card.tsx
2. Button props (literal type)
The spec described two possible values for the Button
variant
prop: “filled” and “outline”. This is a good case for a literal type that explicitly specifies the restricted values.
Button.tsx
3. Optional prop with default
variant
is supposed to be an optional property, and should default to “filled” if no value is specified. Here’s what that looks like for Button
:
Button.tsx
Note the ?
in the type declaration (see TypeScript docs for optional properties ) and the default value specified on destructuring (see MDN default values for destructuring assignment )
4. Semantic HTML
Now let’s update the default div
top-level element (which came from the new-component
template code) to something more descriptive, using semantic HTML .
For the Button
component, the button
element is the best choice (since this component’s job is to add styles to a standard button
).
Button.tsx
The element choice isn’t as clear for the Card
component. Given the options, article
seems like the best fit to me, since a card falls under the “independent item of content” description in the MDN docs .
Card.tsx
5. children prop
Each of these components will take children , and then render the children within a top-level element. For example, if someone declared:
The children
block would be the string “Press me!“. We want to be able to include the children when we render the component.
For the children
prop type, we can use React.ComponentProps
(as described in Matt Pocock’s article ).
React.ComponentProps
takes an argument, a string representing the element name for which we want to find props (for example, React.ComponentProps<"button">
). For now, since we’re only destructuring children
, that might not seem needed (in fact, using ComponentProps
instead of defining a single children
prop may seem like overkill). However, in the future, React.ComponentProps
will be useful to be able to accept all the possible props for a particular element (for example, type
or onClick
for button
). This is a real advantage to using ComponentProps
for the type.
To add all of the props for the element in question (including children
), we can extend the interface
(as described in the Matt Pocock articles on ComponentProps and type vs interface for React props ).
Button.tsx
Card.tsx
Then we can use the children
prop in the returned JSX:
Button.tsx
Card.tsx
In the next workshop
What if someone wants to specify, say, type="submit"
for our Button
component? The best user experience would be that the user can pass any button
prop to Button
(for example, <Button type="submit">Press me!</Button>
) and that prop-plus-value would get passed along to the button
element at the top level of the Button
component.
Do we need to destructure every possible prop and then explicitly add them to the button
element? Thank heavens, no. We’ll talk about delegating props in the next workshop.