Integrate DatoCMS with Next.js

    Next.js is an exceptional tool for building modern, universal frontend applications with the power of React. It lets you get started without having to write much boilerplate code and with a set of sane defaults from which you can build upon.

    Vercel is the easiest way to deploy a production-ready highly available Next.js website, with static assets being served through the CDN automatically and built-in support for Next.js’ automatic static optimization and API routes.

    DatoCMS is the perfect companion to Next.js since it offers content, images and videos on a globally-distributed CDN, much like Vercel does for the static assets of your website. With this combo, you can have an infinitely scalable website, ready to handle prime-time TV traffic spikes, at a fraction of the regular cost.

    Our marketplace features different demo projects on Next, so you can learn and get started easily:

    Fetching content from our GraphQL API

    Since Next 9.3, the way you fetch content from external sources is by exporting one of the following async functions from your page components:

    • getServerSideProps to fetch data on each request;
    • getStaticProps to fetch data at build time.
    // pages/index.js
    export async function getStaticProps(context) {
    return {
    props: {}, // will be passed to the page component as props
    }
    }
    export default Homepage(props) {

    Inside these functions, we can use any Node.JS GraphQL client (or HTTP client, really) to fetch content from DatoCMS and pass it down to your component.

    We suggest using lightweight libraries like graphql-request or unfetch since they're all you need to get the job done.

    Quick start using graphql-request

    First create a new Next.js application using create-next-app, which sets up everything automatically for you.

    To create a project, run the following command and follow the wizard:

    npm init next-app

    Then enter inside the project directory, install the graphql-request package, and start the development server:

    cd my-app
    yarn add graphql-request
    yarn dev

    First you'll need to setup a GraphQL client pointing to one of our GraphQL Content Delivery API endpoints. Create a new directory lib, and inside of it create a file called datocms.js:

    import { GraphQLClient } from "graphql-request";
    export function request({ query, variables, preview }) {
    const endpoint = preview
    ? `https://graphql.datocms.com/preview`
    : `https://graphql.datocms.com/`;
    const client = new GraphQLClient(endpoint, {
    headers: {
    authorization: `Bearer ${process.env.NEXT_DATOCMS_API_TOKEN}`
    }
    });
    return client.request(query, variables);
    }

    On line 10 we're using an environment variable starting with NEXT_ so that it will be embedded into the bundle.

    To create the API token for a DatoCMS project, go in the "Settings > API Tokens" section, making sure you only give it permissions to access the (read-only) Content Delivery API.

    Next, go to pages/index.js — that is, the component that renders the homepage of our project — and define the getStaticProps function:

    import { request } from "../lib/datocms";
    const HOMEPAGE_QUERY = `query HomePage($limit: IntType) {
    allBlogPosts(first: $limit) {
    title
    }
    }`;
    export async function getStaticProps() {
    const data = await request({
    query: HOMEPAGE_QUERY,
    variables: { limit: 10 }
    });
    return {
    props: { data }
    };
    }
    export default function Home({ data }) {
    return <div>{JSON.stringify(data, null, 2)}</div>;
    }

    The HOMEPAGE_QUERY is the GraphQL query, and of course it depends on the models available in your specific DatoCMS project.

    You can learn everything you need regarding how to build GraphQL queries on our Content Delivery API documentation.

    Loading responsive, progressive images from DatoCMS

    One of the mayor advantages of using DatoCMS instead of any other content management systems is its responsiveImage query, which will return pre-computed image attributes that will help you setting up responsive images in your frontend without any additional manipulation.

    To make it even easier to offer responsive, progressive, lazy-loaded images on your projects, we released a package called react-datocms that exposes an <Image /> component and pairs perfectly with the responsiveImage query:

    To take advantage of it, install the react-datocms package:

    yarn add react-datocms

    Then, inside your page, feed content coming from a responsiveImage query directly into the <Image /> component:

    import { request } from "../lib/datocms";
    import { Image } from "react-datocms";
    const HOMEPAGE_QUERY = `query HomePage($limit: IntType) {
    allBlogPosts(first: $limit) {
    id
    title
    coverImage {
    responsiveImage(imgixParams: { fit: crop, w: 300, h: 300, auto: format }) {
    srcSet
    webpSrcSet
    sizes
    src
    width
    height
    aspectRatio
    alt
    title
    base64
    }
    }
    }
    }`;
    export async function getStaticProps() {
    const data = await request({
    query: HOMEPAGE_QUERY,
    variables: { limit: 10 }
    });
    return {
    props: {
    data
    }
    };
    }
    export default function Home({ data }) {
    return (
    <div>
    {data.allBlogPosts.map(blogPost => (
    <article key={blogPost.id}>
    <Image data={blogPost.coverImage.responsiveImage} />
    <h6>{blogPost.title}</h6>
    </article>
    ))}
    </div>
    );
    }

    SEO management

    Similarly to what we offer with responsive images, our GraphQL API also offers a way to fetch pre-computed SEO meta tags based on the content you insert inside DatoCMS.

    You can easily use this information inside your Next app with the help of our react-datocms package.

    Here's a sample of the meta tags you can automatically generate:

    <title>DatoCMS Blog - DatoCMS</title>
    <meta property="og:title" content="DatoCMS Blog" />
    <meta name="twitter:title" content="DatoCMS Blog" />
    <meta name="description" content="Lorem ipsum..." />
    <meta property="og:description" content="Lorem ipsum..." />
    <meta name="twitter:description" content="Lorem ipsum..." />
    <meta property="og:image" content="https://www.datocms-assets.com/..." />
    <meta property="og:image:width" content="2482" />
    <meta property="og:image:height" content="1572" />
    <meta name="twitter:image" content="https://www.datocms-assets.com/..." />
    <meta property="og:locale" content="en" />
    <meta property="og:type" content="website" />
    <meta property="og:site_name" content="DatoCMS" />
    <meta property="article:modified_time" content="2020-03-06T15:07:14Z" />
    <meta name="twitter:card" content="summary" />
    <meta name="twitter:site" content="@datocms" />
    <link sizes="16x16" type="image/png" rel="icon" href="https://www.datocms-assets.com/..." />
    <link sizes="32x32" type="image/png" rel="icon" href="https://www.datocms-assets.com/..." />
    <link sizes="96x96" type="image/png" rel="icon" href="https://www.datocms-assets.com/..." />
    <link sizes="192x192" type="image/png" rel="icon" href="https://www.datocms-assets.com/..." />

    To do that, install the react-datocms package:

    yarn add react-datocms

    Then, inside your page, feed content coming from a faviconMetaTags or _seoMetaTags query directly into the renderMetaTags function:

    import { request } from "../lib/datocms";
    import { renderMetaTags } from "react-datocms";
    import Head from "next/head";
    const HOMEPAGE_QUERY = `
    {
    site: _site {
    favicon: faviconMetaTags {
    attributes
    content
    tag
    }
    }
    blog {
    seo: _seoMetaTags {
    attributes
    content
    tag
    }
    }
    }`;
    export async function getStaticProps() {
    const data = await request({
    query: HOMEPAGE_QUERY,
    variables: { limit: 10 }
    });
    return {
    props: {
    data
    }
    };
    }
    export default function Home({ data }) {
    return (
    <div>
    <Head>{renderMetaTags(data.blog.seo.concat(data.site.favicon))}</Head>
    {/* ... */}
    </div>
    );
    }

    Accessing draft/updated content in Next.js "preview mode"

    If you're using getStaticProps, props will be generated at build time, which is great from a performance point of view, but not ideal when you’re writing a draft on DatoCMS and want to preview the draft immediately on your page.

    Next.js has the feature called Preview Mode which solves this problem. Here’s an instruction on how to use it.

    Step1: Create a preview API route

    First, create a preview API route. It can have any name - e.g. pages/api/preview.js. In this API route, you need to call setPreviewData on the response object. The argument for setPreviewData should be an object, and this can be used by getStaticProps (more on this later).

    For the sake of this example, we’ll just use {}:

    // pages/api/preview.js
    export default (req, res) => {
    res.setPreviewData({});
    res.writeHead(307, { Location: '/' });
    res.end();
    }

    You can test this manually accessing the route from your browser from http://localhost:3000/api/preview. You’ll notice that you'll be redirected to the homepage, and the __prerender_bypass and __next_preview_data cookies will be set.

    Step 2: Update getStaticProps

    The next step is to update getStaticProps to support the preview mode. If you request a page which has getStaticProps with the preview mode cookies set via res.setPreviewData, then getStaticProps will be called at request time instead of at build time.

    Furthermore, it will be called with a context object where:

    • context.preview will be true.
    • context.previewData will be the same as the argument used for res.setPreviewData.
    export async function getStaticProps(context) {
    // If you request this page with the preview mode cookies set:
    //
    // - context.preview will be true
    // - context.previewData will be the same as
    // the argument used for `setPreviewData`.
    }

    In this case, we can use the https://graphql.datocms.com/preview endpoint to access records at their latest version available, instead of the currently published.

    Both endpoints offer exactly the same queries, the only thing that will change will be the returned content:

    import { request } from "../lib/datocms";
    const HOMEPAGE_QUERY = `query HomePage($limit: IntType) {
    allBlogPosts(first: $limit) {
    title
    }
    }`;
    export async function getStaticProps(context) {
    const data = await request({
    query: HOMEPAGE_QUERY,
    variables: { limit: 10 },
    preview: context.preview
    });
    return {
    props: { data }
    };
    }
    export default function Home({ data }) {
    return <div>{JSON.stringify(data, null, 2)}</div>;
    }

    You can read more details regarding preview mode in Next.js docs page.