Ibrahima Ndaw JavaScript enthusiast, full-stack developer, and blogger who also dabbles in UI/UX design.

Using Next.js with TypeScript

5 min read 1479

Next.js Typescript

Next.js allows you to build static and dynamic apps using React. It ships with handy functionalities such as API Routes, automatic code-splitting, internationalization, image optimization, and more. TypeScript is a superset of JavaScript that increases your code quality with types.

TypeScript and Next.js are a match made in heaven. Next’s features make building full-stack React apps easier than ever, and TypeScript’s type system helps you catch errors during development. Using types in TypeScript is optional because a valid JavaScript code is also valid TypeScript code; the TypeScript compiler infers the types for your variables and functions.

In this tutorial, we’ll demonstrate how to use Next.js with TypeScript and introduce you to an exciting and modern stack for building high-quality, search-optimized, and predictable apps.

We’ll cover the following in detail:

To show Next.js and TypeScript in action, we’ll walk through how to build a simple article manager app. Our example app with retrieve data from JSON placeholder.

What is Next.js?

Next.js is a production-ready framework built on top of React and Node.js. It ships with all the features you need to get your React app up and running in no time.

You can use Next.js to build static or dynamic apps since it supports both client- and server-side rendering. Next.js 9 introduced API routes, which allow you to extend your Next.js app with a real backend (serverless) built with Node.js, Express.js, GraphQL, and so on. The most recent version at the time of writing is Next.js 10.

Next.js uses automatic code-splitting (lazy loading) to render only the JavaScript needed for your website. As an added bonus, this makes Next.js great for SEO.

What is TypeScript?

TypeScript is a popular language created and maintained by Microsoft. It’s a superset of JavaScript, which makes all its features optional.

You can convert your existing JavaScript app to TypeScript and it should work as expected as long as your code is valid JavaScript. TypeScript allows you to set types on your variables and functions so you can type-check your code statically and catch errors at compile time.

We made a custom demo for .
No really. Click here to check it out.

You can also use modern features that are not yet supported in JavaScript. And don’t worry about browser support — TypeScript compiles to plain JavaScript, which means your TypeScript code will never ship in the browser.

Using TypeScript in a Next.js app

To create a new Next.js app, you can use Create Next App.

Begin by opening your command-line interface (CLI) and running the command below:

    npx create-next-app next-typescript-example

The command will generate a fresh Next.js app. Now, let’s structure the project as follows:

src
├── components
|  ├── AddPost.tsx
|  └── Post.tsx
├── pages
|  ├── index.tsx
|  └── _app.tsx
├── styles
|  └── index.css
├── tsconfig.json
├── types
|  └── index.ts
├── next-env.d.ts
└── package.json

To enable TypeScript in a Next.js app, add a tsconfig.json file to the root of the project. Next.js will recognize the file and use TypeScript for the project.

With this in place, we can now create files with .ts or .tsx extensions. Next.js handles the compilation of the TypeScript code to JavaScript and then serves our app as usual in the browser.

Creating types

// types/index.ts

export interface IPost {
  id: number
  title: string
  body: string
}

This interface reflects the shape of a Post object. It expects id, title, and body properties.

Creating components

Now that this TypeScript type is ready for use, let’s create the React components and set the types.

// components/AddPost.tsx

import * as React from 'react'
import { IPost } from '../types'

type Props = {
  savePost: (e: React.FormEvent, formData: IPost) => void
}

const AddPost: React.FC<Props> = ({ savePost }) => {
  const [formData, setFormData] = React.useState<IPost>()

  const handleForm = (e: React.FormEvent<HTMLInputElement>): void => {
    setFormData({
      ...formData,
      [e.currentTarget.id]: e.currentTarget.value,
    })
  }

  return (
    <form className='Form' onSubmit={(e) => savePost(e, formData)}>
      <div>
        <div className='Form--field'>
          <label htmlFor='name'>Title</label>
          <input onChange={handleForm} type='text' id='title' />
        </div>
        <div className='Form--field'>
          <label htmlFor='body'>Description</label>
          <input onChange={handleForm} type='text' id='body' />
        </div>
      </div>
      <button
        className='Form__button'
        disabled={formData === undefined ? true : false}
      >
        Add Post
      </button>
    </form>
  )
}

export default AddPost

As you can see, we start by importing the IPost type. After that, we create another type named Props that mirrors the props received as a parameter by the component.

Next, we set the type IPost on the useState hook. Then, we use it to handle the form data. Once the form is submitted, we rely on the function savePost to save the data on the array of posts.

Now, we can create and save a new post.

Let’s move on to the component responsible for displaying the Post object.

// components/Post.tsx

import * as React from 'react'
import { IPost } from '../types'

type Props = {
  post: IPost
  deletePost: (id: number) => void
}

const Post: React.FC<Props> = ({ post, deletePost }) => {
  return (
    <div className='Card'>
      <div className='Card--body'>
        <h1 className='Card--body-title'>{post.title}</h1>
        <p className='Card--body-text'>{post.body}</p>
      </div>
      <button className='Card__button' onClick={() => deletePost(post.id)}>
        Delete
      </button>
    </div>
  )
}

export default Post

This Post component receives as props the post object to show and a function to delete it. The arguments have to match the Props to make TypeScript happy.

We are now able to add, show, and delete posts. Let’s import the components into the App.tsx file and create the logic to handle the posts.

import * as React from 'react'
import { InferGetStaticPropsType } from 'next'
import AddPost from '../components/AddPost'
import Post from '../components/Post'
import { IPost } from '../types'

const API_URL: string = 'https://jsonplaceholder.typicode.com/posts'

export default function IndexPage({
  posts,
}: InferGetStaticPropsType<typeof getStaticProps>) {
  const [postList, setPostList] = React.useState(posts)

  const addPost = async (e: React.FormEvent, formData: IPost) => {
    e.preventDefault()
    const post: IPost = {
      id: Math.random(),
      title: formData.title,
      body: formData.body,
    }
    setPostList([post, ...postList])
  }

  const deletePost = async (id: number) => {
    const posts: IPost[] = postList.filter((post: IPost) => post.id !== id)
    console.log(posts)
    setPostList(posts)
  }

  if (!postList) return <h1>Loading...</h1>

  return (
    <main className='container'>
      <h1>My posts</h1>
      <AddPost savePost={addPost} />
      {postList.map((post: IPost) => (
        <Post key={post.id} deletePost={deletePost} post={post} />
      ))}
    </main>
  )
}

export async function getStaticProps() {
  const res = await fetch(API_URL)
  const posts: IPost[] = await res.json()

  return {
    props: {
      posts,
    },
  }
}

In this component, we first import the types and components created earlier. The type InferGetStaticPropsType, provided by Next.js, allows us to set the type on the method getStaticProps. It will infer the type defined on the props returned by getStaticProps.

After that, we initialize the state with the posts array using the useState hook. Next, we declare the function addPost to save the data on the array of posts. The deletePost method receives as an argument the id of the post, which allows us to filter the array and remove the post.

Finally, we pass in the expected props to the components. Then, we loop through the response data and display it using the Todo component. The data is retrieved from the JSON placeholder API with the help of the getStaticProps method provided by Next.js. The posts fetched have to be an array that matches IPost. Otherwise, TypeScript will throw an error. You can alternatively use the getServerSideProps method, Fetch, or a library to fetch the data. It’s just a matter of how you want to render your Next.js app.

Testing your Next.js app

With this final touch, the app is ready to be tested on the browser.

Begin by browsing into the root of the project and running this command:

    yarn dev

Or, if using npm:

    npm run dev

If everything works as expected, you should see the Next app at http://localhost:3000/:

App Preview

And that’s it!

Conclusion

In this tutorial, we covered how to use TypeScript with Next.js by building an article manager app. You can preview the finished project on GitHub.

Next.js has really good support for TypeScript and is easy to set up. That makes it simple to build strongly typed React apps with Next.js and TypeScript that run on either the client or the server. To put it plainly, Next.js and TypeScript is a very exciting stack to try on your next React project.

LogRocket: Full visibility into production Next.js apps

Debugging Next applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your Next app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your Next.js apps — .

Ibrahima Ndaw JavaScript enthusiast, full-stack developer, and blogger who also dabbles in UI/UX design.

Leave a Reply