Next.js provides a TypeScript-first development experience for building your React application.
It comes with built-in TypeScript support for automatically installing the necessary packages and configuring the proper settings, as well as a TypeScript Plugin for your editor.
🎥 Watch: Learn about the new built-in TypeScript plugin → YouTube (3 minutes).
create-next-app
now ships with TypeScript by default.
npx create-next-app@latest --experimental-app
Add TypeScript to your project by renaming a file to .ts
/ .tsx
. Run next dev
and next build
to automatically install the necessary dependencies and add a tsconfig.json
file with the recommended config options.
Next.js includes a custom TypeScript plugin and type checker, which VSCode and other code editors can use for advanced type-checking and auto-completion.
The first time you run next dev
with a TypeScript file open, you will receive a prompt to enable the plugin.
If you miss the prompt, you can enable the plugin manually by:
Ctrl/⌘
+ Shift
+ P
)Now, when editing files, the custom plugin will be enabled. When running next build
, the custom type checker will be used. Further, we automatically create a VSCode settings file for you to automate this process.
The TypeScript plugin can help with:
use client
directive is used correctly.useState
) are only used in Client Components.Note: More features will be added in the future.
Note: Statically typed links are available in beta with
13.2
.
Next.js can statically type links to prevent typos and other errors when using next/link
, improving type safety when navigating between pages.
To opt-into this feature, experimental.appDir
and experimental.typedRoutes
need to be enabled, and the project needs to be using TypeScript.
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
typedRoutes: true,
},
};
module.exports = nextConfig;
Next.js will generate a link definition in .next/types
that contains information about all existing routes in your application, which TypeScript can then use to provide feedback in your editor about invalid links.
Currently, experimental support includes any string literal, including dynamic segments. For non-literal strings, you currently need to manually cast the href
with as Route
:
import type { Route } from 'next';
import Link from 'next/link'
// ✅
<Link href="/about" />
// ✅
<Link href="/blog/nextjs" />
// ✅
<Link href={`/blog/${slug}`} />
// ✅
<Link href={('/blog' + slug) as Route} />
// ❌ TypeScript errors if href is not a valid route
<Link href="/aboot" />
To accept href
in a custom component wrapping next/link
, use a generic:
import type { Route } from 'next';
import Link from 'next/link';
function Card<T extends string>({ href }: { href: Route<T> | URL })
return (
<Link href={href}>
<div>My Card</div>
</Link>
);
}
When running next dev
or next build
, Next.js generates a hidden .d.ts
file inside .next
that contains information about all existing routes in your application (all valid routes as the href
type of Link
). This .d.ts
file is included in tsconfig.json
and the TypeScript compiler will check that .d.ts
and provide feedback in your editor about invalid links.
Next.js 13 has enhanced type safety. This includes:
fetch
directly in components, layouts, and pages on the server. This data does not need to be serialized (converted to a string) to be passed to the client side for consumption in React. Instead, since app
uses Server Components by default, we can use values like Date
, Map
, Set
, and more without any extra steps. Previously, you needed to manually type the boundary between server and client with Next.js-specific types._app
in favor of root layouts, it is now easier to visualize the data flow between components and pages. Previous, data flowing between individual pages
and _app
were difficult to type and could introduce confusing bugs. With colocated data fetching in Next.js 13, this is no longer an issue.Data Fetching in Next.js now provides as close to end-to-end type safety as possible without being prescriptive about your database or content provider selection.
We’re able to type the response data as you would expect with normal TypeScript. For example:
async function getData() {
const res = await fetch('https://api.example.com/...');
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
return res.json();
}
export default async function Page() {
const name = await getData();
return '...';
}
For complete end-to-end type safety, this also requires your database or content provider to support TypeScript. This could be through using an ORM or type-safe query builder.
An async
Server Components will cause a 'Promise<Element>' is not a valid JSX element
type error where it is used.
This is a known issue with TypeScript and is being worked on upstream.
As a temporary workaround, you can add {/* @ts-expect-error Async Server Component */}
above the component to disable type checking for it.
import { ExampleAsyncComponent } from './ExampleAsyncComponent';
export default function Page() {
return (
<>
{/* @ts-expect-error Async Server Component */}
<ExampleAsyncComponent />
</>
);
}
This does not apply to Layout and Page components.
We are tracking this issue here.
When passing data between a Server and Client Component through props, the data is still serialized (converted to a string) for use in the browser. However, it does not need a special type. It’s typed the same as passing any other props between components.
Further, there is less code to be serialized, as un-rendered data does not cross between the server and client (it remains on the server). This is only now possible through support for Server Components.