Build a Dynamic Avatar Component with React & TypeScript Tutorial (14 exercises)

Bonus: Using PropsWithChildren

Learn how to use PropsWithChildren a generic type provided by React to typing component props that use the children prop.

One thing we didn’t get a chance to talk about but is handy to know is how to type children , which is common when you create a wrapper component or other component that renders other components.

One lesser-known way to do this is using a generic type provided by TypeScript. Think of it like a function that takes an argument and returns something.

Let’s try this out. Create a new file /src called Wrapper.test.tsx and add the following:

import { describe, expect, it, beforeEach } from "vitest";
import { render, screen, cleanup, fireEvent } from "@testing-library/react";

import Avatar from "./Avatar"
import Wrapper from "./Wrapper"

describe("Wrapper", () => {
    it("should render a caption and an image", () => {
        const url = "https://cataas.com/cat/says/hello%20world!";
        const alt = "@github-handle";
        const cap = "a beautiful cat"
        render(<Wrapper><Avatar url={url} alt={alt} /></Wrapper>);
        const img = screen.getByAltText(alt)
        const caption = screen.getByText(cap)

Now, let’s implement this. Create a new file under /src called Wrapper.tsx and just return a div. We’ll make it compile and fail and then pass.

Now let’s import PropsWithChildren , and we’ll use it.

We’ll do this…then this, and you can see it works! We don’t have to manually type children. Very handy.


The following resources provide more information for this lesson:


Inside of source, create a new file called, Wrapper.test.tsx. Paste this in. All we're doing is creating a new task called Wrapper and it should render a caption and an image. We're going to render the Avatar inside of our Wrapper. We're going to get the image, get the caption, and make sure that they're in the document.

Now, open up your terminal and run yarn test. That will run vitest and we'll see that it fails, because Wrapper doesn't exist yet, "Failed to load." Close that, go into source again, and create a new file called, Wrapper.tsx.

For now, all we're going to do, create a Wrapper component, which will return an empty div. We'll do export default Wrapper. We'll open it backup and we'll hit save again. It should run and this time, it fails for the real reason, "Unable to find an element with the alt text," blah, blah, blah.

Let's now fix that. Go inside of Wrapper. First thing we're going to do is this is going to return a figure. Right here, we're going to render children and below that we're going to do figcaption. That will be the caption. Where are we going to get this? Well, these are going to come through props, children and caption.

Now, what we're going to do is we're going to import {PropsWithChildren} from "react". Now, time to use it. We're going to do type WrapperProps =...PropsWithChildren, you can think of it like a function. We're going to call our function. Because it's a generic, we're going to use these angle brackets and then we're going to pass in our argument.

In this case, children will already be on there. We want to add caption, which is going to be of type string. If we come over here, we'll add the colon and add WrapperProps. You will see all the swigleys have gone away.

If we hover over WrapperProps, you'll see this is what's happening under the hood. We're creating a new type called WrapperProps, which has caption: string. That's the prop that we added and you have this & symbol, which creates this inner section, which is like combining multiple types into one.

Because of that, WrapperProps has both of this properties, caption and children. This is very handy when you want a component to accept children. Now, if we save, back, scroll all the way down, it's still failing. Let's see, why, "Unable to find an element with the test: a beautiful cat."

Let's go back to our task. We're importing Wrapper. Let's double check it's in the right place. It is. We'll redo that. That goes away. Wrapper...Ah, it's missing the caption. Let's add caption = and we'll pass {cap}. Now, we see that it's passing. That's how you use PropsWithChildren. Very useful tip for working with react in TypeScript.