- Integrations
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.
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/init
command performs the following:Installs Slice Machine,
@prismicio/next
,@prismicio/react
, and@prismicio/client
.Sets up content previews by creating endpoints at
/api/preview
and/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 models guide for instructions.
Set up content previews
Content writers can preview content on your website before publishing.
See the Live previews and Preview draft content sections for instructions.
Deploy your website
Before content writers can collaborate on the website, you’ll need to host it online.
See the Deploy section for 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.
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 |
Create slices
Page content is written using reusable page sections called slices. Slices are created in Slice Machine.
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>
);
}
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
prismic
tag is revalidated.
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 document.
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");
// ...
}
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 usingnext/link
.<PrismicNextImage>
- Display images usingnext/image
.<PrismicRichText>
- Display rich text.<PrismicText>
- Display plain text.<SliceZone>
- Display slices.
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-simulator
page depends on which Next.js router your website uses.Create a file at
app/slice-simulator/page.tsx
with the following contents.app/slice-simulator/page.tsximport { 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 document.
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-simulator
and 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>
toapp/layout.tsx
<PrismicPreview>
adds the Prismic toolbar and event listeners. The website automatically refreshes when a content draft is saved.app/layout.tsximport { 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 clientenableAutoPreviews()
configures a Prismic client to automatically fetch draft content during a preview. Include it in yourprismicio.ts
file.prismicio.tsimport { 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/preview
endpointThis endpoint enables Draft Mode and redirects a content writer to the previewed document. It uses
redirectToPreviewURL()
.app/api/preview/route.tsimport { 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-preview
endpointThis endpoint ends a preview session. It is called by the Prismic toolbar when closing a preview. It uses
exitPreview()
.app/api/exit-preview/route.tsimport { 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.ghttps://example.com/api/revalidate
).Triggers Only check “A document is published” and “A document is unpublished”. You do not need to set up a webhook with your hosting provider.
Create a
/api/revalidate
Route HandlerAdd the following
/api/revalidate
Route Handler to your Next.js app:app/api/revalidate/route.tsimport { NextResponse } from "next/server"; import { revalidateTag } from "next/cache"; export async function POST() { revalidateTag("prismic"); 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_image
field 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
’sgenerateMetadata
function.app/[uid]/page.tsximport { 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
negotiator
and@formatjs/intl-localematcher
packages are needed to support internationalization.npm install negotiator @formatjs/intl-localematcher
Create an
i18n.ts
fileThe
i18n.ts
file provides a set of internationalization helpers. The helpers will be used in your website’s middleware.Create an
i18n.ts
file next to to yourapp
directory with the following contents.i18n.tsimport 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.ts
contains a list of locales supported by your website.Update the
LOCALES
constant to match your Prismic repository’s locales. The first locale is used when a visitor’s locale is not supported.i18n.tsconst 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-us
locale will appear asen
in the URL (e.g./en/about
).i18n.tsconst 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.ts
file with the following contents, or modify your existingmiddleware.ts
to include the highlighted lines.middleware.tsimport 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).*)"], };
Nest routes under a
[lang]
dynamic route segmentCreate a directory at
app/[lang]
and nest all other routes under this directory.The
lang
route parameter will contain the visitor’s locale. For example,/en-us/about
setslang
to"en-us"
.Fetch content from the visitor’s locale
In your
page.tsx
files, forward thelang
parameter to your Prismic query.app/[lang]/[uid]/page.tsximport { 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
reverseLocaleLookup
helper fromi18n.ts
converts a shortened locale (e.g.en
) to its full version (e.g.en-us
).Fetch all locales in
generateStaticParams
Fetch documents from all locales using the Prismic client’s
lang: "*"
option. Include thelang
route parameter.app/[lang]/[uid]/page.tsxexport 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
}
}
}
Property | Description | Default |
---|---|---|
format optional | boolean Determines if generated files are formatted using Prettier. | true |
lazyLoadSlices optional | boolean Determines if slice components are lazy loaded with
| true |
typescript optional | boolean Determines if generated files are written in TypeScript or JavaScript.
Defaults to | |
jsxExtension optional | boolean Determines if generated JavaScript files should use a | false |
generatedTypesFilePath optional | string The filepath at which generated TypeScript types will be saved. | prismicio-types.d.ts |
environmentVariableFilePath optional | string The filepath at which the active Prismic environment is stored as an environment variable. | .env.local |