Typing Next.JS components using TypeScript

Konstantin Lebedev
May 22, 2019 · 4 min read
Image for post
Image for post

Note: This post covers typing Next.JS applications that use versions prior to v9. Starting with version 9, Next.JS comes with its own types by default, and their names may differ from those used in DefinitelyTyped package. If you use Next.JS version 9 and older, please refer to the official documentation. For earlier versions, continue reading :)

In this article, we’ll talk about typing Next.JS components. We’ll be using this Next.JS application that connects to Reddit API and displays a list of top posts in a given subreddit. Right now, the main component Posts.tsx doesn’t have any type-safety, so we’re going to fix that.

Note: the project already contains TypeScript configured, so if you don’t know how to do that, please read the official guide here, it’s as easy as installing a few dependencies and dropping in some configuration files.

Now let’s clone the project from GitHub and get started.

First, let’s take a look at how we type React components in general.

If we write our components as functions, we can use FunctionComponent type from React library. This type takes a generic type argument that describes the shape of the props. Our Posts component takes a subreddit name and a list of posts, so props object is going to look like this:

Now when we destructure props into posts and subreddit, we get full type safety. Pretty neat, right?

Now let’s look at Next.JS components.

One thing that makes Next.JS components different is a static getInitialProps function. If we try to assign it to our regular React component, we’ll get a type error:

To fix this problem, we need to use a special component type from Next.JS package called NextFunctionComponent. This type extends standard React’s FunctionComponent type with Next.JS-specific static lifecycle methods (well, only one method, really). So now our code will look like this:

To make our types more robust, we can infer the shape of props returned from getInitialProps function instead of defining them manually. To do that, first, we want to extract getInitialProps function into a separate variable. This step is required to avoid circular type reference when we start inferring the shape of our props:

Next, we can use ReturnType helper type to get the type of the value returned from getInitialProps function:

Since getInitialProps function is asynchronous, the return type is going to be a promise, so we also need to extract its value. We can define a global helper type that will use conditional type magic to unwrap our promise:

Now we can put everything together:

Let’s make this example more interesting by taking the name of a subreddit from a query string.

To achieve that, we can use a context argument that gets passed to getInitialProps function that we haven’t used so far. We will use NextContext<T> type to type this argument. The typeT allows us to specify the parameters we know we will have in a query string (subreddit in our case):

We got type-safety inside getInitialProps function, but now we run into another type error talking about incompatible types of Context.

The reason is that by default getInitialProps expects a context to be of a generic type NextContext, but we specified a stricter, more specific type — NextContext<{ subreddit: string }. To resolve this issue, we need to pass a few more type arguments to NextFunctionComponent type. Its full signature looks like this:

As you can see, NextFunctionComponent can take up to 3 types — Props, InitialProps, and Context. InitialProps should only contain props returned from getInitialProps function. Props should contain all the props that component has access to, which include own component props, props passed through higher order components (such as connect from Redux), plus the initial props. And finally, Context specifies the shape of the context used by our component.

When we put everything together, we’ll get a fully typed Next.JS component:

You can find the source code for the fully typed component in “final” branch.

In this article, we’ve explored how to type Next.JS components. The approach is different from typing regular React functional components because of the special getInitialProps function that Next.JS uses to prepare props data server-side. For that reason, we need to use special NextFunctionComponent and NextContext types that come with Next.JS typing package.

PS: If you’re curious why we used type aliases everywhere instead of interfaces, make sure to check this article.

Shameless plug: if you want to learn how to use Redux without writing a ton of boilerplate, check my “State management with Rematch” course on Youtube.

The Startup

Medium's largest active publication, followed by +771K people. Follow to join our community.

Konstantin Lebedev

Written by

Full-time learner, part-time educator. Find more programming tutorials at https://konstantinlebedev.com

The Startup

Medium's largest active publication, followed by +771K people. Follow to join our community.

Konstantin Lebedev

Written by

Full-time learner, part-time educator. Find more programming tutorials at https://konstantinlebedev.com

The Startup

Medium's largest active publication, followed by +771K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store