— Next.js, React, GraphQL — 5 min read
With so many choices for which technology to use when building a website, it can get overwhelming. You need to consider who is going to use it, what content to display and who will maintain it. A static website is a great choice when creating a blog, band website or e-commerce store. Static websites are an ode to the past when websites were just plain-ol files on a server you accessed via URL. They provide benefits like being fast, having great SEO and not being dependent on a certain runtime like PHP. This is in comparison to a server-rendered website like what you would have with Wordpress, Drupal or Ruby on Rails.
Static websites are built using static assets. The next question becomes where to store (and manage) this content. If you are a solo webmaster, the content can be files in a Git repo. If you have clients or other developers who will want to manage the content, a CMS (Content Management System) is what you need. A CMS is a service which stores your website content, for example blog posts and concert dates.
With Next.js SSG, we are using the CMS in a "headless" fashion. After trying a bunch of Headless CMS offerings, one I've stuck with is Cosmic. Cosmic is an intuitive, powerful, and simple-to-use service which lets us get up and running quickly. They provide many starter apps that you can preview or fork. For example, I chose the Next.js Static Blog and had a production version of the website running in under 5 minutes.
Next.js with GraphQL is my personal choice when it comes to Static site development. Next.js is a hybrid React framework which supports building static websites. It also lets you build server-side rendered pages when the need arises. It handles routing, has a large community supporting it making it one of the best ways to build a React app in 2020. The other tech you may have heard also does this is Gatsby.js. Gatsby is more user-friendly but is more opinionated with its technology choices (forced use of GraphQL versus it being a choice).
We are choosing to use GraphQL over the Cosmic NPM module. GraphQL is a standardized way to get data from services and is a great choice when needing to get data from a CMS. As you create custom data types in Cosmic, you are able to query for it in the GraphQL API. One of the benefits of using GraphQL is that you are less dependent on a specific SDK.
For reference, I forked the example Cosmic Next.js project here.
After creating an account on Cosmic and going through the product tour, you will be shown the “Create New Bucket” screen.
Click "Start with an App" then search and select "Next.js Static Blog" from the list of apps presented. This will do many of things.
Here is what the created bucket looks like on your Cosmic dashboard
The next thing we need to do is clone the Next.js code to our local environments. This will enable us to run the Next.js locally and pull content from the demo Cosmic bucket we created.
You can also choose to create a GitHub repository for yourself using the template.
Once inside the new directory, make sure you are using the correct Node.js version by using NVM.
1nvm use v12.18.3
Install Yarn and install the project dependencies.
1brew install yarn && yarn
Run the app!
1yarn dev
Almost there!
Before we are able to query the Cosmic GraphQL API, our app needs to know where it lives. Environment Variables are deployment specific values which contain sensitive things like API keys.
There are three env vars we need to define to have the app work locally. Create a file named .env.local
(don't worry it's ignored by Git), it should look like this:
1COSMIC_BUCKET_SLUG=demo-nextjs-static-blog2COSMIC_READ_KEY=77H1zN7bTktdsgekxyB9FTpOrlVNE3KUP0UTptn5EqA7T0J8Qt3# Preview secret can be anything you choose4COSMIC_PREVIEW_SECRET=iwvrzpakhaavqbihwlrv
To get these values, head over to the Settings sidebar menu in your bucket, and click "Basic Settings".
Run the app again with yarn dev
And we are up!
There are two things that we need to understand when it comes to Statically-Generated Next.js apps, pages and routes. Pages are content which depend on external data, and routes are URL routes which depend on external data. Both have you defining special Next.js specific functions, getStaticProps
and getStaticPaths
.
The file which contains the logic for generating page content based on the Cosmic GraphQL API is located at pages/posts/[slug].js.
1export async function getStaticProps({ params, preview = null }) {2 // Get the data from the API3 const data = await getPostAndMorePosts(params.slug, preview);4 // Convert markdown content to HTML content5 const content = await markdownToHtml(data.post?.metadata?.content || "");6 return {7 props: {8 preview,9 post: {10 ...data.post,11 content,12 },13 morePosts: data.morePosts || [],14 },15 };16}
1export async function getPostAndMorePosts(slug, preview) {2 // Query for the data through the Cosmic GraphQL API using Apollo Client3 ...4 const moreObjectsResults = await client.query({5 query: gql`6 query getPostQuery(7 $bucketSlug: String!8 $readKey: String!9 $status: status10 ) {11 getObjects(12 bucket_slug: $bucketSlug13 input: {14 read_key: $readKey15 type: "posts"16 status: $status17 limit: 318 }19 ) {20 objects {21 _id22 slug23 title24 metadata25 created_at26 }27 }28 }29 `,30 variables: {31 bucketSlug: BUCKET_SLUG,32 readKey: READ_KEY,33 status,34 },35 });
This is one example of a page using getStaticProps
. It is also used in the Index page for getting all the blog post titles and excerpts.
pages/posts/[slug].js
also contains getStaticPaths
which tells our Next.js app which routes to generate.
1export async function getStaticPaths() {2 // Get all post data (including content)3 const allPosts = (await getAllPostsWithSlug()) || [];4 return {5 // Tell Next.js all of the potential URL routes based on slugs6 paths: allPosts.map((post) => `/posts/${post.slug}`),7 fallback: true,8 };9}
After understanding these two aspects, the blog is just a regular React app.
Now that we have our website working locally, let's deploy it to Vercel. The first step is making sure you have the code in a Git repository.
I recommend you have the code in GitHub. You can use the GitHub CLI to create a repo in your current directory with gh repo create
.
We now need to sign up for Vercel and have it use the code from the GitHub repo. You can sign up for Vercel with your GitHub account here. You can import the code from GitHub using the "Import Project" feature.
When importing the project, make sure you define the environment variables, COSMIC_BUCKET_SLUG
, COSMIC_READ_KEY
, and COSMIC_PREVIEW_SECRET
.
When deployed, all pushes to your default Git branch will have Vercel deploy a new version of your website!
The Next.js docs on preview mode are right here.
Local development and deploying the website to production will cover most of your use-cases. Another common workflow is saving a draft of changes on your CMS and then previewing those changes on your local machine. To do so, we will enable "Preview" mode both on Cosmic and our Next.js app.
First thing we will need to do is have Cosmic know that the Posts object type will be preview-able. On the Posts setting page, add the preview link.
1http://localhost:3000/api/preview?secret=iwvrzpakhaavqbihwlrv&slug=[object_slug]
When finished, click "Save Object Type".
Let's try editing a post and see it show up on our local machine. Try changing the title of "Learn How to Pre-render Pages Using Static Generation with Next.js" and click "Save Draft" instead of "Publish".
The Save Draft
button
We now have unpublished changes. Run the app locally with yarn dev
and then click "Preview" right under "Save Draft".
Our preview mode!
Note this feature requires a Cosmic paid plan
The only way to deploy new content to our blog is to have a developer push to the default git branch. This action will trigger Vercel take the new code and push a new version of our website. We ideally want our content creators to have the same control. Webhooks are a way we can do this.
Let's set up a webhook which lets Vercel know when our posts in Cosmic have new updates. This will let us deploy new versions of the website without developers needing to do anything.
Go to the Git integration settings page (https://vercel.com/[project]/settings/git-integration) in your Vercel project and create a new Deploy Hook named "Cosmic Hook".
What your settings should look like when the webhook is created
Now over in Cosmic settings, we can add this webhook. Let's add Cosmic notify Vercel when changes get published. You can see how we can do this for previews as well if we wanted to in the future.
Edited/Created and Published!
To test this go to the same post we tested Previews with and add some content to the end of the article and publish. You should see a deploy happen on Vercel with the new content deployed to the live version of your website!
Want to see what the final website looks like? Click here to check it out.