TypeScript
Learn how to use TypeScript in Next.js with Prismic.
This article shows how to use TypeScript in your Next.js project. Types for your Prismic content are automatically integrated into your project using the latest Prismic tools.
Why use TypeScript?
TypeScript is an extra layer above JavaScript that helps you write resilient code. It is a language that understands the data moving throughout your app. By using TypeScript, you can more quickly and more confidently add, edit, and delete code.
If you do something wrong, your code editor and the TypeScript compiler prompt an error before deploying to production.
Using TypeScript does not add significant overhead to projects in most cases. Adopting TypeScript generally results in less time spent diagnosing bugs and refactoring legacy code later in a project’s life.
What is JSDoc?
JSDoc gives you most of TypeScript’s benefits while writing in JavaScript. When using JSDoc, types are declared using code comments.
Examples throughout this article are provided in TypeScript and JSDoc formats.
Generating content types
Slice Machine generates a prismicio-types.d.ts
file in your project containing types for your Prismic content. The file will be regenerated any time content models are added or modified.
All types are accessible by importing the Content
namespace from @prismicio/client
.
import type { Content } from "@prismicio/client";
Projects that do not use Slice Machine can use the prismic-ts-codegen
CLI to generate types. See the prismic-ts-codegen Technical Reference to learn more about the tool and its configuration options.
Typing createClient()
We recommend creating a prismicio.ts
file at the root of your project containing a createClient()
function. This function is used wherever a Prismic client is needed, including getStaticProps()
and API endpoints. See the Set up Prismic article for more details.
To type your createClient()
function, use @prismicio/next
’s CreateClientConfig
type.
import * as prismic from "@prismicio/client";
import * as prismicNext from "@prismicio/next";
export function createClient({
previewData,
req,
...config
}: prismicNext.CreateClientConfig = {}) {
const client = prismic.createClient(
"your-repo-name",
config,
);
prismicNext.enableAutoPreviews({
client,
previewData,
req,
});
return client;
}
Typing client methods
Types generated by prismic-ts-codegen
are automatically integrated into @prismicio/client
.
Queries that reference a page type, such as getByUID()
or getAllByType()
, are automatically TypeScript-typed.
In the following example, a Page document is queried and automatically typed with PageDocument
:
import type { GetStaticPropsContext } from "next";
import { createClient } from "../prismicio";
// ...
export async function getStaticProps({
previewData,
}: GetStaticPropsContext) {
const client = createClient({ previewData });
// ^ Automatically contains references to document types
const page = await client.getByUID("page", "home");
// ^ Typed as PageDocument
return {
props: {
page,
},
};
}
Client methods that do not reference a specific document type, such as getAllByTag()
or getFirst()
, are TypeScript-typed using a union of all generated document types, allowing you to narrow the type in your own code.
const drinks = await client.getAllByTag("drinks");
// ^ Typed as PageDocument[] | MenuItemDocument[] | SettingsDocument[]
All query methods accept a type parameter to override or specify a return type. This parameter is useful when a specific type is not automatically inferred.
import type { Content } from "@prismicio/client";
const drinks =
await client.getAllByTag<Content.MenuItemDocument>(
"drinks",
);
Typing page components
Page components can be typed using the inferred type from the page’s getStaticProps()
or getServerSideProps()
function.
Next.js provides InferGetStaticPropsType
and InferGetServerSidePropsType
helpers for this purpose.
In the following example, page props are typed in PageProps
:
import type {
InferGetStaticPropsType,
GetStaticPropsContext,
} from "next";
import { createClient } from "../prismicio";
type PageProps = InferGetStaticPropsType<
typeof getStaticProps
>;
export default function Page({ page }: PageProps) {
// `page` is typed as PageDocument.
}
export async function getStaticProps({
previewData,
}: GetStaticPropsContext) {
const client = createClient({ previewData });
const page = await client.getByUID("page", "home");
// ^ Typed as PageDocument
return {
props: {
page,
},
};
}
Pages with route parameters, such as src/pages/[uid].tsx
, can type their parameters by passing an object type to GetStaticPropsContext
or GetServerSidePropsContext
, depending on which data fetching API is used.
In the following example, route parameters are typed in PageParams
:
import type { GetStaticPropsContext } from "next";
type PageParams = { uid: string };
// ...
export async function getStaticProps({
params,
previewData,
}: GetStaticPropsContext<PageParams>) {
// `params` is typed as PageParams
}
Typing slice components
Slice components can be typed using @prismicio/react
’s SliceComponentProps
type which accepts a slice’s generated type as a type parameter.
Using SliceComponentProps
ensures the component is compatible with @prismicio/react
’s <SliceZone>
component.
In the following example, a Text slice’s component is fully typed:
import type { Content } from "@prismicio/client";
import type { SliceComponentProps } from "@prismicio/react";
export type TextProps =
SliceComponentProps<Content.TextSlice>;
export default function Text({ slice }: TextProps) {
// `slice` is typed as TextSlice.
}
Typing content relationships
Content relationship and link fields allow querying for a linked document’s content using the graphQuery
or fetchLinks
query options. These queries are not automatically typed, but they can be defined in your app’s code.
In the following example, a Page document is queried with a fetchLinks
query option. The relatedBlogPost
’s field value will include the selected Blog Post’s title
and description
fields within the data
property.
import type { GetStaticPropsContext } from "next";
import type { Content } from "@prismicio/client";
import { createClient } from "../prismicio";
export async function getStaticProps({
previewData,
}: GetStaticPropsContext) {
const client = createClient({ previewData });
// The `relatedBlogPost` field contains the blog post's `title` and
// `description` fields.
const page = await client.getByUID<
Content.PageDocument & {
data: {
relatedBlogPost: {
data: Pick<
Content.BlogPostDocument["data"],
"title" | "description"
>;
};
};
}
>("page", "home", {
fetchLinks: [
"my.relatedBlogPost.title",
"my.relatedBlogPost.description",
],
});
return {
props: {
page,
},
};
}