Use Next.js with Prismic
Learn how to build websites with Next.js and Prismic.
Overview
Prismic has a first-party Next.js integration that supports all of Prismic’s features:
- Build pages using slices and page types. 
- Write content alongside live previews. 
- Preview pages before publishing with full-website previews. 
- Model content with Slice Machine. 
- Fetch and display Prismic content using SDKs. 
- Code with confidence using generated TypeScript types. 
- Write content in multiple languages with locales. 
Your favorite Next.js features are also supported:
- Build with the App Router or the Pages Router. 
- Optimize images using - next/image.
- Prioritize website performance using Server Components. 
- Quickly deploy new content using Incremental Static Revalidation. 
- Cache Prismic API requests with data caching. 
This page teaches you how to set up a Next.js website with Prismic.
Set up a Next.js website
Prismic can be added to new or existing Next.js websites. Follow these steps to set up a Next.js project.
- Create a Prismic repository- From the Prismic dashboard, create a Prismic repository. Select Next.js.  - The Prismic dashboard where you can create a new repository. - When asked to select a starter, select Connect your own web app. 
- Set up a Next.js project- Follow the setup instructions shown in your Prismic repository.  - Project setup steps in a Prismic repository. - The instructions guide you through creating a new Next.js project (if needed) and adding Prismic using - @slicemachine/init.- The - @slicemachine/initcommand performs the following:- Installs Slice Machine, - @prismicio/next,- @prismicio/react, and- @prismicio/client.
- Sets up content previews by creating endpoints at - /api/previewand- /api/exit-preview.
- Sets up content revalidation by creating an endpoint at - /api/revalidate.
- Sets up live previews by creating a page at - /slice-simulator.
- Creates a Prismic client at - prismicio.ts.
- Saves Slice Machine configuration at - slicemachine.config.json.
 
- Model page content- Content modeling is the process of converting page designs into structured fields. We recommend adding support for a homepage and general pages. - See the recommended page models 
- Set up content previews- Content writers can preview content on your website before publishing. - @slicemachine/initperforms most of the setup for you. However, there are a few steps to complete manually.- See the preview setup instructions 
- Deploy your website- Before content writers can collaborate on the website, you’ll need to host it online. See the deploy instructions
- Publish content- Your Next.js website is now set up and ready for content. Visit your Prismic repository to build pages. - To recap, you completed the following: - Created a Prismic repository to store your website’s content. 
- Set up a Next.js project and integrated Prismic. 
- Prepared your website for a homepage and general pages. 
- Added support for live and full-website previews. 
- Deployed your website to your hosting provider. 
 - Continue adding slices and other content models as you develop your website. 
Create pages
Website pages are managed in Prismic using page types. Page types are created in Slice Machine.
Learn how to model pages and create Next.js page files in the Content Modeling guide. In the guide, you’ll find code snippets to bootstrap your pages.
See our recommended page models
Define routes
Prismic needs to know your website’s routes to fill in link URLs. Configure the routes constant in your project’s prismicio.ts with a set of route resolvers.
This example includes routes for a homepage, general pages, and a blog.
// `type` is the API ID of a page type.
// `path` determines the URL for a page of that type.
const routes: Route[] = [
  { type: "homepage", path: "/" },
  { type: "page", path: "/:uid" },
  { type: "blog_post", path: "/blog/:uid" },
];Your route resolvers should match your Next.js file-system-based routes. Here are some commonly used routes:
| Route resolver path | Next.js file-system route | 
|---|---|
| / | app/page.tsx | 
| /:uid | app/[uid]/page.tsx | 
| /blog/:uid | app/blog/[uid]/page.tsx | 
| /:grandparent/:parent/:uid | app/[...path]/page.tsx | 
Learn more about route resolvers
Create slices
Page content is written using reusable page sections called slices. Slices are created in Slice Machine.
Learn how to create slices
Write React components
Slice Machine generates a bootstrapped React component when a slice is created. You can find the generated files in src/slices or whichever slice library was selected.
Once your slice is configured with fields, edit the slice’s index.tsx file to display the slice’s content.
Here is an example of a Call to Action slice. It displays some text and a link using a rich text and link field.
import type { Content } from "@prismicio/client";
import { PrismicRichText, type SliceComponentProps } from "@prismicio/react";
import { PrismicNextLink } from "@prismicio/next";
type CallToActionProps = SliceComponentProps<Content.CallToActionSlice>;
export default function CallToAction({ slice }: CallToActionProps) {
  return (
    <section className="flex flex-col gap-4 p-8">
      <PrismicRichText field={slice.primary.text} />
      <PrismicNextLink field={slice.primary.link} className="button" />
    </section>
  );
}Learn how to display content
Fetch content
Use @prismicio/client and its methods to fetch page content.
Set up a Prismic client
Create a prismicio.ts file to centralize your Prismic client setup. How you set up a Prismic client depends on which Next.js router your website uses.
This file contains route resolvers, Next.js data cache settings, and default Prismic client settings.
import {
  createClient as baseCreateClient,
  type ClientConfig,
  type Route,
} from "@prismicio/client";
import { enableAutoPreviews } from "@prismicio/next";
import sm from "../slicemachine.config.json";
export const repositoryName = sm.repositoryName;
// TODO: Update with your route resolvers.
const routes: Route[] = [
  { type: "homepage", path: "/" },
  { type: "page", path: "/:uid" },
  { type: "blog_post", path: "/blog/:uid" },
];
export function createClient(config: ClientConfig = {}) {
  const client = baseCreateClient(repositoryName, {
    routes,
    fetchOptions:
      process.env.NODE_ENV === "production"
        ? { next: { tags: ["prismic"] }, cache: "force-cache" }
        : { next: { revalidate: 5 } },
    ...config,
  });
  enableAutoPreviews({ client });
  return client;
}The above fetchOptions includes the following Next.js cache settings:
- In development: Caches Prismic API calls for 5 seconds.
- In production: Caches Prismic API calls indefinitely until the prismictag is revalidated.
See how to set up content revalidation
Fetch content in pages and slices
Import createClient() from prismicio.ts and create a client. Use the client to fetch content.
How you fetch content depends on which Next.js router your website is using.
This example page fetches content for a /[uid] dynamic route.
import type { Metadata } from "next";
import { SliceZone } from "@prismicio/react";
import { createClient } from "@/prismicio";
import { components } from "@/slices";
type Params = { uid: string };
export default async function Page({ params }: { params: Promise<Params> }) {
  const { uid } = await params;
  const client = createClient();
  const page = await client.getByUID("page", uid);
  return <SliceZone slices={page.data.slices} components={components} />;
}You can fetch content in slices the same way. This example fetches a Settings page.
import { Content } from "@prismicio/client";
import { SliceComponentProps } from "@prismicio/react";
import { createClient } from "@/prismicio";
type ContactFormProps = SliceComponentProps<Content.ContactFormSlice>;
export default async function ContactForm({ slice }: ContactFormProps) {
  const client = createClient();
  const settings = await client.getSingle("settings");
  // ...
}Learn more about fetching content
Secure with an access token
Published content is public by default. You can require a private access token to secure the API.
Learn more about content visibility
- Open your API & Security settings- Navigate to your Prismic repository and go to Settings > API & Security.  - The API & Security settings in a Prismic repository. 
- Change the API access- Under the Repository security section, change the API access dropdown to “Private API.” - Click Change the API visibility. 
- Generate an access token- Under the Generate an Access Token section, fill out the form. - Field - Value - Name - A name to identify your website. - Callback URL - Leave blank. - Click Add this application. 
- Pass the access token to your client- Save the access token as an environment variable in a - .envfile..env- PRISMIC_ACCESS_TOKEN=my-access-token- Then, pass the access token to the client in - prismicio.ts.prismicio.ts- export function createClient(config: ClientConfig = {}) { const client = baseCreateClient(repositoryName, { accessToken: process.env.PRISMIC_ACCESS_TOKEN, routes, fetchOptions: process.env.NODE_ENV === "production" ? { next: { tags: ["prismic"] }, cache: "force-cache" } : { next: { revalidate: 5 } }, ...config, }); enableAutoPreviews({ client }); return client; }- The Client ID and Secret values on the API & Security page can be ignored. 
Display content
Prismic content can be displayed using @prismicio/react and @prismicio/next.
Here are the most commonly used components in Next.js websites:
- <PrismicNextLink>- Display links using- next/link.
- <PrismicNextImage>- Display images using- next/image.
- <PrismicRichText>- Display rich text.
- <PrismicText>- Display plain text.
- <SliceZone>- Display slices.
Learn how to display content from a field
Live previews in the Page Builder
Content writers can preview content live while editing in the Page Builder. Each slice in a page is shown as a live-updating thumbnail.

A page with live previews in the Page Builder.
Set up live previewing
Live previews require a special /slice-simulator route in your Next.js website.
- Create a page at- /slice-simulator- How you set up the - /slice-simulatorpage depends on which Next.js router your website uses.- Create a file at - app/slice-simulator/page.tsxwith the following contents.app/slice-simulator/page.tsx- import { SliceSimulator, SliceSimulatorParams, getSlices, } from "@slicemachine/adapter-next/simulator"; import { SliceZone } from "@prismicio/react"; import { components } from "@/slices"; export default async function SliceSimulatorPage({ searchParams, }: SliceSimulatorParams) { const { state } = await searchParams; const slices = getSlices(state); return ( <SliceSimulator> <SliceZone slices={slices} components={components} /> </SliceSimulator> ); }
- Set the simulator URL in the Page Builder- Navigate to your Prismic repository and open a page. - Click the ”…” button next to the Publish/Unpublish button in the top-right corner. Select Live preview settings.  - The live preview settings menu option. - In the modal, enter - http://localhost:3000/slice-simulatorand click Save.
Preview draft content
Content writers can preview content on your website before publishing.
@slicemachine/init performs most of the setup for you during project setup. However, there are a few steps to complete manually.
Set up previews in Next.js
@prismicio/next provides helpers and a component to support content previews. How you set up content previews depends on which Next.js router your website uses.
- Add- <PrismicPreview>to- app/layout.tsx- <PrismicPreview>adds the Prismic toolbar and event listeners. The website automatically refreshes when a content draft is saved.app/layout.tsx- import { type ReactNode } from "react"; import { PrismicPreview } from "@prismicio/next"; import { repositoryName } from "@/prismicio"; export default function RootLayout({ children }: { children: ReactNode }) { return ( <html lang="en"> <body>{children}</body> <PrismicPreview repositoryName={repositoryName} /> </html> ); }
- Call- enableAutoPreviews()with your Prismic client- enableAutoPreviews()configures a Prismic client to automatically fetch draft content during a preview. Include it in your- prismicio.tsfile.prismicio.ts- import { createClient as baseCreateClient, ClientConfig, } from "@prismicio/client"; import { enableAutoPreviews } from "@prismicio/next"; import sm from "../slicemachine.config.json"; export const repositoryName = sm.repositoryName; export function createClient(config: ClientConfig = {}) { const client = baseCreateClient(repositoryName, config); enableAutoPreviews({ client }); return client; }
- Create an- /api/previewendpoint- This endpoint enables Draft Mode and redirects a content writer to the previewed page. It uses - redirectToPreviewURL().app/api/preview/route.ts- import { NextRequest } from "next/server"; import { redirectToPreviewURL } from "@prismicio/next"; import { createClient } from "@/prismicio"; export async function GET(request: NextRequest) { const client = createClient(); return await redirectToPreviewURL({ client, request }); }
- Create an- /api/exit-previewendpoint- This endpoint ends a preview session. It is called by the Prismic toolbar when closing a preview. It uses - exitPreview().app/api/exit-preview/route.ts- import { exitPreview } from "@prismicio/next"; export function GET() { return exitPreview(); }
Set up previews in Prismic
After setting up previews in your Next.js project, set up previews in Prismic.
- Open your preview settings- Navigate to your Prismic repository and go to Settings > Previews.  - The previews settings page. 
- Create a preview- In the Manage your previews section, create a preview using the following values: - Field - Value - Site name - Development - Domain for your application - http://localhost:3000- Preview route - /api/preview- Click Create my preview. 
Deploy
To deploy your website, follow the instructions for deploying Next.js with your chosen hosting provider:
Handle content changes
Your app needs to be notified or rebuilt when your content changes in Prismic. How you set up your website to handle content changes depends on which Next.js router your website uses.
- Create a webhook- Add a Prismic webhook to clear the Next.js - fetch()cache when your content changes in Prismic.- Follow the Create a webhook instructions in our webhooks documentation using these values: - Field - Value - Name - ”Display published content” - URL - Your app’s deployed URL + - /api/revalidate(e.g- https://example.com/api/revalidate).- Triggers - Only check “A page is published” and “A page is unpublished”. - You do not need to set up a webhook with your hosting provider. 
- Create a- /api/revalidateRoute Handler- Add the following - /api/revalidateRoute Handler to your Next.js app:app/api/revalidate/route.ts- import { NextResponse } from "next/server"; import { revalidateTag } from "next/cache"; export async function POST() { revalidateTag("prismic", "max"); return NextResponse.json({ revalidated: true, now: Date.now() }); }
SEO
Prismic websites can be optimized for search engines using meta_title and meta_descriptions fields. These fields provide metadata and may improve your website’s ranking.
- Add SEO fields to your page types- SEO fields are added to page types by default in an SEO tab. - If your page type does not have this tab, create a tab named SEO and add the following fields: - Label - API ID - Type - Description - Meta Title - meta_title- Rich Text - The title shown in search results. - Meta Description - meta_description- Text - The description shown in search results. - Meta Image - meta_image- Image - The image shown in link previews. - The - meta_imagefield is not typically used by search engines, but it can be used as a preview when linking to your website on some platforms.
- Add metadata to pages- How you add metadata to pages depends on which Next.js router your website uses. - Use the metadata fields in a - page.tsx’s- generateMetadatafunction.app/[uid]/page.tsx- import { Metadata } from "next"; import { asText, asImageSrc } from "@prismicio/client"; import { createClient } from "@/prismicio"; type Params = { uid: string }; export async function generateMetadata({ params, }: { params: Promise<Params>; }): Promise<Metadata> { const { uid } = await params; const client = createClient(); const page = await client.getByUID("page", uid); return { title: asText(page.data.meta_title), description: page.data.meta_description, openGraph: { images: [{ url: asImageSrc(page.data.meta_image) ?? "" }], }, }; }
Internationalization
Prismic supports websites with multiple languages. How you set up internationalization depends on which Next.js router your website uses.
To learn more about Prismic’s locale management, see Locales.
- Install the necessary packages- The - negotiatorand- @formatjs/intl-localematcherpackages are needed to support internationalization.- npm install negotiator @formatjs/intl-localematcher
- Create an- i18n.tsfile- The - i18n.tsfile provides a set of internationalization helpers. The helpers will be used in your website’s middleware.- Create an - i18n.tsfile next to to your- appdirectory with the following contents.i18n.ts- import type { NextRequest } from "next/server"; import { match } from "@formatjs/intl-localematcher"; import Negotiator from "negotiator"; /** * A record of locales mapped to a version displayed in URLs. The first entry is * used as the default locale. */ // TODO: Update this object with your website's supported locales. Keys // should be the locale IDs registered in your Prismic repository, and values // should be the string that appears in the URL. const LOCALES = { "en-us": "en-us", "fr-fr": "fr-fr", }; /** Creates a redirect with an auto-detected locale prepended to the URL. */ export function createLocaleRedirect(request: NextRequest): Response { const headers = { "accept-language": request.headers.get("accept-language"), }; const languages = new Negotiator({ headers }).languages(); const locales = Object.keys(LOCALES); const locale = match(languages, locales, locales[0]); request.nextUrl.pathname = `/${LOCALES[locale]}${request.nextUrl.pathname}`; return Response.redirect(request.nextUrl); } /** Determines if a pathname has a locale as its first segment. */ export function pathnameHasLocale(request: NextRequest): boolean { const regexp = new RegExp(`^/(${Object.values(LOCALES).join("|")})(\/|$)`); return regexp.test(request.nextUrl.pathname); } /** * Returns the full locale of a given locale. It returns `undefined` if the * locale is not in the master list. */ export function reverseLocaleLookup(locale: string): string | undefined { for (const key in LOCALES) { if (LOCALES[key] === locale) { return key; } } }
- Define locales in- i18n.ts- i18n.tscontains a list of locales supported by your website.- Update the - LOCALESconstant to match your Prismic repository’s locales. The first locale is used when a visitor’s locale is not supported.i18n.ts- const LOCALES = { "en-us": "en-us", "fr-fr": "fr-fr", };- You can define how the locale appears in the URL by changing the object’s values. In the following example, the - en-uslocale will appear as- enin the URL (e.g.- /en/about).i18n.ts- const LOCALES = { "en-us": "en", "fr-fr": "fr", };
- Create or modify- middleware.ts- Website visitors will be directed to the correct locale using Next.js middleware. - Create a - middleware.tsfile with the following contents, or modify your existing- middleware.tsto include the highlighted lines.middleware.ts- import type { NextRequest } from "next/server"; import { createLocaleRedirect, pathnameHasLocale } from "@/i18n"; export async function middleware(request: NextRequest) { if (!pathnameHasLocale(request)) { return createLocaleRedirect(request); } } export const config = { matcher: ["/((?!_next|api|slice-simulator|icon.svg).*)"], };- Learn more about Next.js dynamic routes 
- Nest routes under a- [lang]dynamic route segment- Create a directory at - app/[lang]and nest all other routes under this directory.- The - langroute parameter will contain the visitor’s locale. For example,- /en-us/aboutsets- langto- "en-us".- Learn about Next.js dynamic routes 
- Fetch content from the visitor’s locale- In your - page.tsxfiles, forward the- langparameter to your Prismic query.app/[lang]/[uid]/page.tsx- import { createClient } from "@/prismicio"; import { reverseLocaleLookup } from "@/i18n"; type Params = { lang: string; uid: string }; export default async function Page({ params }: { params: Promise<Params> }) { const { lang, uid } = await params; const client = createClient(); const page = await client.getByUID("page", uid, { lang: reverseLocaleLookup(lang), }); // ... }- The - reverseLocaleLookuphelper from- i18n.tsconverts a shortened locale (e.g.- en) to its full version (e.g.- en-us).
- Fetch all locales in- generateStaticParams- Fetch pages from all locales using the Prismic client’s - lang: "*"option. Include the- langroute parameter.app/[lang]/[uid]/page.tsx- export async function generateStaticParams() { const client = createClient(); const pages = await client.getAllByType("page", { lang: "*", }); return pages.map((page) => ({ lang: page.lang, uid: page.uid, })); }
Configure Slice Machine
Slice Machine can be configured in slicemachine.config.json. Add options to the adapter.options property.
{
  "repositoryName": "example-prismic-repo",
  "libraries": ["./src/slices"],
  "localSliceSimulatorURL": "http://localhost:3000/slice-simulator",
  "adapter": {
    "resolve": "@slicemachine/adapter-next",
    "options": {
      "typescript": true
    }
  }
}typescriptbooleanDetermines if generated files are written in TypeScript or JavaScript.
true if a project has a tsconfig.json file, false otherwise.
jsxExtensionbooleanDetermines if generated JavaScript files should use a .jsx file
extension. Has no effect when TypeScript is used.
falsegeneratedTypesFilePathstringThe filepath at which generated TypeScript types will be saved.
prismicio-types.d.tsenvironmentVariableFilePathstringThe filepath at which the active Prismic environment is stored as an environment variable.
.env.local