Skip to content

Build A Static Site With Dynamic Flair

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Building a new marketing site, a totally new greenfield project within an existing organization, is fun and exciting, but offers a unique set of challenges. Let's outline some of the most common scenarios and see how to tackle them head on.

The idea: we have a new site that has static content and maybe some forms. Some of the requirements from our collective bosses: everyone needs to be able contribute, but only the director or those bestowed with the power can publish content. The other big need is to be able to see changes, updates, new material on the site, pass around an internal url to gather feedback, before it ever goes live. There are a few more catches that will come up as we scaffold out this exciting new project.

Like all good sized projects, we've made some choices up front and want to make sure they will meet our needs before writing up the work and getting started. Based on a few of the requirements outlined earlier, we'll be using Sanity content platform. Everything will hopefully go there from importing existing content, rich media, and copy for the landing page. For our development choice and deployment, we've chosen Next.js paired with Vercel for deploy. The idea is to make the developer experience vastly superior to our current system: to make deployment and preview of the project as seamless and transparent as possible. Let's dive in.

Sanity Content Platform

Headless CMS offers several advantages over the traditional method of content. For a platform like Wordpress, the user-generated content, structure, persistence, layout, and style are all combined into a single application. This has benefits such as ease of use, a rich ecosystem of plugins and tools, and many more. It also tightly couples our process and curation of content with the site itself. What Sanity offers is a rich content creation experience, called Studio, separate from our end result, meaning we can distribute it quickly and easily across a global CDN network. That's where Vercel comes in, but we'll get to that.

With the Studio comes dedicated SDKs and a generated GraphQL endpoint to get that structured content in just the right way, at just the right time. While GROQ is similar to GraphQL queries, it offers a bit more specificity when required. We can give specific access to everyone in the company using roles that allow them to create, edit, or update curtain content. We'll reserve a role 'owner' for those with final sign off to hit publish and send changes out into the world. The real win with Sanity and other headless CMS's is we can have the development team build out a structured scheme for blog posts, marketing updates, product releases, landing pages, or anything else using Sanity's document based objects and then (and here's the key) hand it over the the rest of the team to build it using their expertise. They can then add titles, descriptions, the right copy, images with specific crops, highlights and metadata for a new project rollout. All that stuff developers are not involved in, the rest of the team can take the scheme and the studio and build just what they need, on their own terms.


Now, to get all that hard work from the content backend and out in to world, we're goning to use a React framework, Next.js. The biggest selling point for this project is two forms of pre-rendering: Static Generation and Server-side Rendering that Next.js offers. This gives a couple features right away. We can statically generate all that content on build time once, send it out to the edges and users will have a super fast, high quality experience. Once that snazzy page has loaded, we can selectively hydrate any dynamic content we might want to keep users engages, maybe load inventory of our latest project from Shopify. That's the other big win with Next.js: it's data hungry. We can load, fetch, and hydrate data from any source in a multitude of ways.

First and foremost, we can get content from Sanity on build, and then add sources as the project grows: maybe it's Shopify, or some legacy inventory service over a REST endpoint. The powerful idea that makes Next.js a good fit for this project is that it has opinions on when to fetch data, but the how and where are completely open to mix and match. As a bonus, we also get Server-side Rendering for a great preview experience that we'll look into shortly.

How to Query

We've chosen our respective platform, deployment method, framework, and content management system. As we begin to wire up the data on the backend to the client, there's a decision to make. Sanity offers a unique query language called groq and also a generated GraphQL endpoint based on the scheme that we create in our studio. So, which do we use?

Here's the great part: Next.js is unopinionated about the way we fetch data, only really where it's fetched, be that server side, at build, or client side. GROQ isn't totally foreign as a query language:

*[_type == 'movie' && releaseYear >= 1979] | order(releaseYear) { _id, title, releaseYear }

Using the provided Sanity SDK to build and fetch queries or mutations is similar enough to GraphQL, so it won't be a huge lift in learning. That being said, the team is already comfortable with GraphQL, so that makes for a logically choice to start with. However, if or when the need arises for multiple queries or some gnarly nested query, GROQ is in our tool belt and can be additive to the project without backtracking on the work already accomplished. That being said, we also have the flexibility to add additional datasources: maybe there's a DAM in the company that years of assets we need to use or an aggregate sales service that the backend team built for us with using SalesForce. We can selectively fetch this data when the user expects it.

By just relying on the components of Next.js, we even get prefetching on <Link> pages without having to make any configurations. These are a couple of the small wins we get when choosing a framework with some opinions baked in for us.

Datasets, Preview, and Production

Things are going great. We've got the studio up with structured schemes for the other teams in the company to add content. An early version of the site deployed on Vercel and tied to the main branch in our git repository. Teams are moving quickly and the site is automatically built and deployed on PR merges. So how do teams starting sharing content, work, ideas, before it goes out for the whole world to see? Preview Mode.

When we enable preview mode on our Next.js site, it's just a boolean flag in the cookie, but what it does when a request comes in with preview=true is rather than serve static data or props that we've fetched on build, we can have it fetch draft or unpublished data from our studio.

export async function getStaticProps(context) {
  // If context.preview is true, append "draft" to the API query
  // to request draft data instead of published data from Sanity
  const res = await fetch(`https://.../${context.preview ? 'preview' : ''}`)
  // ...

In practice, anyone with access to the studio can go in, update the layout and copy of a landing page, maybe give it a bit more flair and click the 'Live Preview' tab that we put right there in the studio! It's going to make an api request to our site at /api/preview passing the draft id and the super secret token that we generate and keep in our ENV list on Vercel.

Our endpoint will verify the token, maybe check that the draft id exists to be safe, sets that preview cookie to true, and finally redirects to the site page where getServerSideProps picks up on preview mode. Now we're looking at that super rad landing page rendered in the site with all the CSS styles, headers, footers, side nav, exactly what it will look like when it's published.

// Enable Preview Mode by setting the cookies

  // Redirect to the path from the fetched post
  // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities

Wrap up

"Service, [and] amplify, and give new skills to non technical people and users."
Guillermo Rauch

"The power, that in the past, only developers had." ~Kapehe

When it comes down to it, what we want in a successful project is to empower users and creators to make without limits. What we've gone over together and outlined above is to that service. As developers, we want to enable those around us, in our companies and projects, to bring their skillset and expertise to the table and contribute in a meaningful way. Do that with as little friction as possible, and you're off to a great start.

I want to thank my guests on Build IT Better, Kapehe, Devrel with Sanity CMS, and Guillermo Rauch, CEO of Vercel, for a fantastic conversation and sharing their knowledge with me. This article would not be possible without their time and insight. Thank you. 👋