Subscribe
May 16, 2023
3 min read
next.jstypescript

How to add type safety for Links in Next.js 13 with Typescript

How to incorporate type safety for links in Next.js using Typescript and the newly introduced statically typed routes.

Introduction

If you're like me, a Typescript fanatic, you always want to take advantage of all the cool things it can do to improve your code and make it easier to maintain. After all, that's what most developers are striving for.

Until recently, Next.js did not provide a built-in mechanism for ensuring type safety in its routing system. Unfortunately, this implies we needed a method to ensure the path we provide to Link is valid.

Next.js has implemented a new feature in version 13.2 that introduces static typing for links within the app directory. This feature aims to prevent typographical errors or potential issues that may arise when utilizing next/link.

Let's explore how we can implement this feature in a next.js app.

Setting up a Next.js app

Imagine our application consisting of three routes, a home page, a contact page, and an about page.

Since the routing system in the app directory is managed based on the folders contained within it. Structure the app folder as follows:

App folder structure with a simple routing system in Next.js 13

Let's create a simple navigation within the layout.tsx file so we can navigate between our defined pages.

app/layout.tsx
import Link from 'next/link';
 
export default function RootLayout({ children }: { children: React.ReactNode }) {
	return (
		<html lang="en">
			<body>
				<header>
					<h1><Link href="/">Next.js 13</Link></h1>
					<nav>
						<ul>
							<li><Link href={'/contact'}>Contact</Link></li>
							<li><Link href={'/about'}>About</Link></li>
						</ul>
					</nav>
				</header>
				{children}
			</body>
		</html>
	);
}
app/layout.tsx
import Link from 'next/link';
 
export default function RootLayout({ children }: { children: React.ReactNode }) {
	return (
		<html lang="en">
			<body>
				<header>
					<h1><Link href="/">Next.js 13</Link></h1>
					<nav>
						<ul>
							<li><Link href={'/contact'}>Contact</Link></li>
							<li><Link href={'/about'}>About</Link></li>
						</ul>
					</nav>
				</header>
				{children}
			</body>
		</html>
	);
}

To demonstrate how statically typed routes work, let's add a new navigation item that currently does not exist in our app. This way, we can simulate how the static typing feature can resolve such a scenario.

<header>
	<h1><Link href="/">Next.js - Statically typed links</Link></h1>
	<nav>
		<ul>
			<li><Link href={'/contact'}>Contact</Link></li>
			<li><Link href={'/about'}>About</Link></li>
			<li><Link href={'/blog'}>Blog</Link></li>
		</ul>
	</nav>
</header>
<header>
	<h1><Link href="/">Next.js - Statically typed links</Link></h1>
	<nav>
		<ul>
			<li><Link href={'/contact'}>Contact</Link></li>
			<li><Link href={'/about'}>About</Link></li>
			<li><Link href={'/blog'}>Blog</Link></li>
		</ul>
	</nav>
</header>

By default, TypeScript will not complain about errors related to the non-existent route during build time. You won't receive any warnings in your text editor either (as shown in the screenshot below).

No error in Visual Studio Code when a route does not exist in Next.js

This lack of type safety could lead to errors and bugs in the application.

Experimental typed routes

Enabling this feature is easy to set up; add typedRoutes: true under the experimental field in your next.config.js file.

const nextConfig = {
	experimental: {
		typedRoutes: true,
        ...
	},
    ...
};
const nextConfig = {
	experimental: {
		typedRoutes: true,
        ...
	},
    ...
};

When typedRoutes is true, Next.js will create a type corresponding to all possible paths in our application's link definition within the build source.

Since I'm using the 13.4 version on Next.js, I don't need to add appDir under the experimental field since it's finally stable and can be adopted for production. However, If you're using an earlier version, you must include it.

const nextConfig = {
	experimental: {
        appDir: true,
		typedRoutes: true,
        ...
	},
    ...
};
const nextConfig = {
	experimental: {
        appDir: true,
		typedRoutes: true,
        ...
	},
    ...
};

Now that TypeScript can leverage the link definition file, it can provide feedback within your text editor to help catch any invalid links before you ship to production.

Screenshot of Visual Studio Code error if a route does not exist in Next.js app

If you're Next.js application is running on an older version than 13.2, you can use the open-source next-routes package, which can also generate the route types with zero configuration.

Install this package by running the following command:

# npm
npm install nextjs-routes
# yarn
yarn add nextjs-routes
# pnpm
pnpm add nextjs-routes
# npm
npm install nextjs-routes
# yarn
yarn add nextjs-routes
# pnpm
pnpm add nextjs-routes

Update the next.config.js file to merge next-routes with the Next.js config.

const nextRoutes = require("nextjs-routes/config");
const withRoutes = nextRoutes();
const nextConfig = {
    ...
};
 
module.exports = withRoutes(nextConfig);
const nextRoutes = require("nextjs-routes/config");
const withRoutes = nextRoutes();
const nextConfig = {
    ...
};
 
module.exports = withRoutes(nextConfig);

Conclusion

With our new statically typed routes in place, we can now make changes to our app's folder structure without worrying much about breaking things in the production.

While this is still an experimental feature, it is worth exploring if you want to make the most of TypeScript.