next-slicezone
Learn how to work with Slice Machine's next-slicezone to render slices, query documents, and build routes.
Introduction
The next-slicezone
package exports two lifecycle hooks (useGetStaticProps
, useGetStaticPaths
) that allow you to fetch associated data of documents on Prismic and get the static paths of a given route. It also exports a component (SliceZone
) that matches your Next.js components with Prismic slices.
Together they render front-end components for each of your Prismic documents. You’ll see how to use these later, but first, you should learn how to install and configure the package.
Dependencies installation
Install the next-slicezone
package via a package manager:
npm install next-slicezone
You should also install @prismicio/client
which will be used to query the Prismic API:
npm install @prismicio/client@^5
Project files configuration
You will need to configure a few project files to use this package.
The sm.json file
Create a file at the root of your project called sm.json
. This file is a configuration file for the slice-machine-ui
and your <SliceZone>
component which will use this file to find the location of your slice libraries.
Accepted Attributes | Usage |
---|---|
apiEndpoint | You can specify your Prismic API endpoint here and import it throughout your project. |
libraries | Used to specify a slices directory (@/my-slices), nested slice directories (@/slices/new-library) or library packages (like react essential-slices). |
_latest | Used to specify the version of slice-machine-ui. |
storybook | Used to specify the port in which to open a storybook integration. |
Example file
{
"apiEndpoint": "https://your-repo-name.cdn.prismic.io/api/v2",
"libraries": [
"@/slices",
"@/my-slices/new-library",
"essential-slices"
],
"_latest": "0.1.0",
"storybook": "http://localhost:8888"
}
createResolver and SliceResolver functions
The createResolver
function will generate an sm-resolver.js
file in the root of your project every time you change your slices structure (rename, add, delete a slice, add a library, etc.). This file contains the SliceResolver
function, which automatically matches slices and their content coming from Prismic to the local components in your project. You will need to import it when building a page and pass it to the <SliceZone>
component.
To do this, create a 〜/pages/_document.js
file and add the createResolver
method to its getInitialProps
method:
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { createResolver } from 'next-slicezone/resolver' // import the function here
export default class extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
/* In development, generate an sm-resolver.js file
that will map slices to components */
if (process.env.NODE_ENV === 'development') {
await createResolver()
}
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</div>
</Html>
)
}
}
Example sm-resolver.js file
This file contains the JavaScript SliceResolver
function which you need to import when building a page and pass it to the <SliceZone>
component. You can see how to do this further down the article where we discuss the <SliceZone>
.
import { Fragment } from "react";
import * as Slices from "./slices";
const __allSlices = { ...Slices };
const NotFound = ({ sliceName, slice, i }) => {
console.error(
`[sm-resolver] component "${sliceName}" not found at index ${i}.`,
);
console.warn(`slice data: ${slice}`);
return process.env.NODE_ENV !== "production" ? (
<div
style={{
height: "30vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexDirection: "column",
textAlign: "center",
background: "#FAFAFA",
}}
>
<h2>Slice "{sliceName}" not found.</h2>
<p style={{ maxWidth: "320px", fontSize: "16px" }}>
Check that you registered this component in your
slices library!
</p>
</div>
) : (
<Fragment />
);
};
export default function SliceResolver({
sliceName,
...rest
}) {
return __allSlices[sliceName]
? __allSlices[sliceName]
: () => <NotFound sliceName={sliceName} {...rest} />;
}
Data Fetching
Next.js offers two data fetching functions:
getStaticProps() | which gets the data for statically-generated pages |
getStaticPaths() | which determines all of the routes for statically-generated dynamic pages |
next-slicezone
extends the functionality of those hooks with useGetStaticPaths()
and useGetStaticProps()
,
useGetStaticProps
useGetStaticProps can be used on every page using the SliceZone. It’s responsible for:
- fetching content from Prismic
- returning a pre-written Next getStaticProps
apiParams
useGetStaticProps
takes an apiParams
object or function as an argument.
The object argument allows you to specify static props to send to your query such as a language code.
The function argument gives you access to the params
, previewData
, and preview
objects to help build your queries and send dynamic data such as document UIDs from the URL bar to the query. The returned data can also be used when building static paths which you can see in the useGetStaticPaths
example below.
client function (required) | Receives a Prismic client. Example:
|
apiParams object | Object or function passed to client apiOptions. The function gives you access to the params, previewData, and preview objects. Static Object Example:
|
queryType string | Defines whether the custom type is a singleton or repeatable. Defaults to ‘repeat’. Example:
|
type string | Custom type to query. Defaults to ‘page’. Example:
|
slicesKey string | Key of slices array in API response (doc.data.[slicesKey]) Defaults to slices. Example:
|
getStaticPropsParams | extra params used by getStaticProps, like notFound or redirect. Example:
|
import Prismic from "@prismicio/client";
import { useGetStaticProps } from "next-slicezone/hooks";
export const getStaticProps = useGetStaticProps({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
queryType: "repeat",
type: "page",
slicesKey: "MySliceZone",
getStaticPropsParams: {
notFound: false,
},
apiParams({ params }) {
// params are passed by getStaticPaths
return {
lang: params.lang,
uid: params.uid,
};
},
});
useGetStaticPaths
useGetStaticPaths
can be used in dynamic pages to define the static paths to be generated. It returns a function to be passed directly to Next.js’s getStaticPaths
function. It will fetch content from Prismic using dynamic properties.
useGetStaticPaths
takes a params object as an argument.
client | Same as useGetStaticProps |
type | Same as useGetStaticProps |
apiParams | Same as useGetStaticProps |
formatPath function (required) | Function to format the path object that must be returned from getStaticPaths in Next.js. Pass null to skip. Defaults to (doc) => null. Example:
|
import Prismic from "@prismicio/client";
import {
useGetStaticProps,
useGetStaticPaths,
} from "next-slicezone/hooks";
// Fetch content from prismic
export const getStaticProps = useGetStaticProps({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
type: "page",
apiParams({ params }) {
// params are passed by getStaticPaths
return {
lang: params.lang,
uid: params.uid,
};
},
});
// fetch all docs of type `page` and pass params accordingly
export const getStaticPaths = useGetStaticPaths({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
type: "page",
formatPath: (prismicDocument) => {
return {
params: {
lang: prismicDocument.lang,
uid: prismicDocument.uid,
},
};
},
});
<SliceZone />
Once slices have been fetched, they must be matched with Next.js components and rendered. The SliceZone accepts data from the API (fetched by useGetStaticProps
) as a prop. It also accepts a resolver, which defines how to match Prismic slices with Next.js components.
slices array (required) | The data components fetched from Prismic |
resolver function (required) | Resolves calls to components from the SliceZone |
sliceProps object || function | This allows you to pass props, like for Theme UI, to have more control of a globally available component on the page level. |
Example:
Here’s an example of a [uid].js
file with a slice zone component that is receiving slices, custom props for Theme UI and we’ve imported the sm-resolver.js
to pass to the SliceZone
component.
import Prismic from "@prismicio/client";
import SliceZone from "next-slicezone";
import {
useGetStaticProps,
useGetStaticPaths,
} from "next-slicezone/hooks";
import resolver from "../sm-resolver";
const Page = ({ slices, data }) => (
<SliceZone
slices={slices}
resolver={resolver}
sliceProps={({ slice, sliceName, i }) => ({
theme: i % 1 ? "light" : "dark",
alignLeft: data.keyTextTitle?.length > 35,
})}
/>
);
export const getStaticProps = useGetStaticProps({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
apiParams({ params }) {
return {
uid: params.uid,
};
},
});
export const getStaticPaths = useGetStaticPaths({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
formatPath: (prismicDocument) => {
return {
params: {
uid: prismicDocument.uid,
},
};
},
});
export default Page;
Examples
Take a look at the different use cases where you can make use of the SliceZone and the Lifecycle hooks.
Query by single type
In this example, we query a Singleton page of type “homepage” in an index.js
file:
import Prismic from "@prismicio/client";
import SliceZone from "next-slicezone";
import { useGetStaticProps } from "next-slicezone/hooks";
import resolver from "../sm-resolver"; // import from project root
const Page = ({ slices }) => (
<SliceZone resolver={resolver} slices={slices} />
);
export const getStaticProps = useGetStaticProps({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
type: "homepage",
queryType: "single",
});
export default Page;
Query by repeatable type
In this example, we dynamically query the Repeatable pages of type “post” using the uid
of each document in a [uid].js
file:
import Prismic from "@prismicio/client";
import SliceZone from "next-slicezone";
import {
useGetStaticProps,
useGetStaticPaths,
} from "next-slicezone/hooks";
import resolver from "../sm-resolver"; // import from project root
const Post = ({ slices }) => (
<SliceZone resolver={resolver} slices={slices} />
);
export const getStaticProps = useGetStaticProps({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
type: "post",
apiParams({ params }) {
return {
uid: params.uid,
};
},
});
export const getStaticPaths = useGetStaticPaths({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
formatPath: (prismicDocument) => {
return {
params: {
uid: prismicDocument.uid,
},
};
},
});
export default Post;
Query dynamically by language
In this example, we query a default repeatable type “page” using the uid
and lang
based in a 〜/pages/[lang]/[uid].js
file:
import Prismic from "@prismicio/client";
import SliceZone from "next-slicezone";
import {
useGetStaticProps,
useGetStaticPaths,
} from "next-slicezone/hooks";
import resolver from "../sm-resolver"; // import from project root
const Page = ({ slices }) => (
<SliceZone resolver={resolver} slices={slices} />
);
export const getStaticProps = useGetStaticProps({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
apiParams({ params }) {
return {
lang: params.lang,
uid: params.uid,
};
},
});
export const getStaticPaths = useGetStaticPaths({
client: Prismic.client(
"https://your-repo-name.cdn.prismic.io/api/v2",
),
formatPath: (prismicDocument) => {
return {
params: {
uid: prismicDocument.uid,
lang: prismicDocument.lang,
},
};
},
});
export default Page;