Frameworks

Use Next.js with Prismic

Last updated

Overview

Prismic has a first-party Next.js integration that supports all of Prismic’s features:

Here’s what a Prismic page looks like in Next.js with the Data Cache:

app/[uid]/page.tsx
import { SliceZone } from "@prismicio/react";
import { createClient } from "@/prismicio";
import { components } from "@/slices";

export default async function Page({ params }: PageProps<"/[uid]">) {
  // 1. Fetch a page from Prismic
  const { uid } = await params;
  const client = createClient();
  const page = await client.getByUID("page", uid);

  // 2. Display the page's slices
  return <SliceZone slices={page.data.slices} components={components} />;
}

Next.js websites use @prismicio/client, @prismicio/react, and @prismicio/next.

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 pages

Website pages are managed in Prismic using page types. Page types are created in Slice Machine.

Learn how to create page types

Write page components

Each page type needs a Next.js page component. With the App Router’s file-system based routing, you create a page file at each page’s path.

The example below shows a page component for a Page page type.

app/[uid]/page.tsx

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.

prismicio.ts
// `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 pathNext.js file-system route
/app/page.tsx
/:uidapp/[uid]/page.tsx
/blog/:uidapp/blog/[uid]/page.tsx
/:grandparent/:parent/:uidapp/[...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 a rich text field and a link field.

src/slices/CallToAction/index.tsx
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. This file contains route resolvers and default Prismic client settings.

The fetch cache is configured using the client’s fetchOptions.

prismicio.ts
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: {
      next: { tags: ["prismic"] },
      cache: "force-cache",
    },
    ...config,
  });

  enableAutoPreviews({ client });

  return client;
}

The above fetchOptions includes the following Next.js cache settings:

  • next.tags: Tags all Prismic API calls with prismic.
  • cache: API calls are cached forever 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.

This example page fetches content for a /[uid] dynamic route.

src/app/[uid]/page.tsx
import type { Metadata } from "next";
import { SliceZone } from "@prismicio/react";
import { createClient } from "@/prismicio";
import { components } from "@/slices";

export default async function Page({ params }: PageProps<"/[uid]">) {
  const { uid } = await params;
  // Caching is configured via fetchOptions in prismicio.ts.
  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.

src/slices/ContactForm/index.tsx
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

Fetch new content

When pages are cached with fetchOptions, you may see stale content as you work on your website.

You can fetch new content when working locally by hard-refreshing the page in your browser. In Chrome, you can hard-refresh using Shift+⌘R.

In production, you will need to set up content revalidation to fetch new content.

Learn how to set up content revalidation

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.

    A screenshot of the API & Security settings in a Prismic
repository.

    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.

    FieldValue
    NameA name to identify your website.
    Callback URLLeave blank.

    Click Add this application.

  • Pass the access token to your client

    Save the access token as an environment variable in a .env file.

    .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,
        ...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:

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 screenshot of a page with live previews in the Page Builder.

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.

  • Add /slice-simulator

    Create a file at app/slice-simulator/page.tsx with the following contents.

    app/slice-simulator/page.tsx
    import {
      SliceSimulator,
      SliceSimulatorParams,
      getSlices,
    } from "@prismicio/next";
    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.

    A screenshot of the live preview settings menu option in the Page Builder.

    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.

  • 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.ts file.

    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/preview endpoint

    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-preview endpoint

    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.

    A screenshot of the previews settings page.

    The previews settings page.

  • Create a preview

    In the Manage your previews section, create a preview using the following values:

    FieldValue
    Site nameDevelopment
    Domain for your applicationhttp://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 when content changes in Prismic.

  • Create a /api/revalidate Route Handler

    Add the following /api/revalidate Route Handler to your Next.js app. It clears the Next.js cache when content changes in Prismic.

    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() });
    }

    The "prismic" tag matches the tag used in page components and the Prismic client. When revalidateTag() is called, Next.js clears all cache entries with this tag.

  • Create a webhook

    Add a Prismic webhook that is called when content changes in Prismic.

    Follow the Create a webhook instructions in our webhooks documentation using these values:

    FieldValue
    Name”Display published content”
    URLYour app’s deployed URL + /api/revalidate (e.g https://example.com/api/revalidate).
    TriggersOnly check “A page is published” and “A page is unpublished”.

    You do not need to set up a webhook with your hosting provider.

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.

Internationalization

Prismic supports websites with multiple languages.

To learn more about Prismic’s locale management, see Locales.

Configure Slice Machine

Slice Machine can be configured in slicemachine.config.json. Add options to the adapter.options property.

slicemachine.config.json
{
  "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 next/dynamic.

true
typescript
optional
boolean

Determines if generated files are written in TypeScript or JavaScript.

true if a project has a tsconfig.json file, false otherwise.

jsxExtension
optional
boolean

Determines if generated JavaScript files should use a .jsx file extension. Has no effect when TypeScript is used.

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
Was this page helpful?