TypeScript

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.

This guide assumes your project is already set up with TypeScript.

If your Next.js project does not currently use TypeScript, follow Next.js’s official TypeScript guide for existing projects before continuing.

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.

Copy
import type { Content } from '@prismicio/client'

Automatic type generation was added in Slice Machine v0.5.1

Update to the latest version for the best experience.

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.

  • TypeScript
  • JavaScript
TypeScript
prismicio.ts
Copy
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
}
JavaScript
prismicio.js
Copy
import * as prismic from '@prismicio/client'
import * as prismicNext from '@prismicio/next'

/** @param {import("@prismicio/next").CreateClientConfig} */
export function createClient({ previewData, req, ...config } = {}) {
  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.

Automatic client integration was introduced in @prismicio/client v6.6.0

Update to the latest version for the best experience.

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:

src/pages/index.tsx
Copy
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.

Copy
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.

  • TypeScript
  • JavaScript
TypeScript
Copy
import type { Content } from '@prismicio/client'

const drinks = await client.getAllByTag<Content.MenuItemDocument>('drinks')
JavaScript
Copy
/** @type {import('@prismicio/client').Content.MenuItemDocument[]} */
const drinks = await client.getAllByTag('drinks')

See the @prismicio/client Technical Reference to learn more about the package and its TypeScript support.

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:

  • TypeScript
  • JavaScript
TypeScript
src/pages/index.tsx
Copy
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,
    },
  }
}
JavaScript
src/pages/index.js
Copy
import { createClient } from '../prismicio'

/** @typedef {import('next').InferGetStaticPropsType<typeof getStaticProps>} PageProps */

/** @param {PageProps} */
export default function Page({ page }) {
  // `page` is typed as PageDocument.
}

/** @param {import('next').GetStaticPropsContext} */
export async function getStaticProps({ previewData }) {
  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:

  • TypeScript
  • JavaScript
TypeScript
src/pages/[uid].tsx
Copy
import type { GetStaticPropsContext } from 'next'

type PageParams = { uid: string }

// ...

export async function getStaticProps({
  params,
  previewData,
}: GetStaticPropsContext<PageParams>) {
  // `params` is typed as PageParams
}
JavaScript
src/pages/[uid].js
Copy
/**
 * @typedef {Object} PageParams
 * @property {string} uid
 */

// ...

/** @param {import('next').GetStaticPropsContext<PageParams>} */
export async function getStaticProps({
  params,
  previewData,
}) {
  // `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:

  • TypeScript
  • JavaScript
TypeScript
src/slices/Text/index.tsx
Copy
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.
}
JavaScript
src/slices/Text/index.js
Copy
/**
 * @typedef {import("@prismicio/client").Content.TextSlice} TextSlice
 * @typedef {import("@prismicio/react").SliceComponentProps<TextSlice>} TextProps
 *
 * @param {TextProps}
 */
export default function Text({ slice }) {
  // `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.

  • TypeScript
  • JavaScript
TypeScript
src/pages/index.tsx
Copy
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,
    },
  }
}
JavaScript
src/pages/index.js
Copy
import { createClient } from '../prismicio'

/** @param {import('next').GetStaticPropsContext} */
export async function getStaticProps({ previewData }) {
  const client = createClient({ previewData })

  /**
   * @typedef {import("@prismicio/client").Content} Content
   *
   * @typedef {object} Relation
   * @property {{ relatedBlogPost: { data: RelationData } }} data
   *
   * @typedef {Content.ArticleDocument["data"]} ArticleDocumentData
   * @typedef {Pick<ArticleDocumentData, "title" | "description">} RelationData
   *
   * @type {Content.PageDocument & Relation}
   */
  const page = await client.getByUID('page', 'home', {
    fetchLinks: [
      'my.relatedBlogPost.title',
      'my.relatedBlogPost.description',
    ],
  })

  return {
    props: {
      page,
    },
  }
}

Can't find what you're looking for?

Need technical Support? Spot an error in the documentation? Get in touch with us on our Community Forum.