Using Sanity.io and Next.js for Primo Bathroomware

Primo Bathroomware came to me as they were looking to refresh their website based on client feedback they were getting. They wanted a site that was easy to use and showcases their range of products.

The client was great to work with and was happy for me to take my advice on everything from layout to the technology stack. In the end I decided to use Sanity.io as the CMS as it is a great fit for a project like this as they have 100s of products and many attributes for each product.

The Stack

Sanity.io allows you to create a schema which defines the data structure of your content and then display it in the CMS how you best see fit. This meant I could spin up a CMS which isn't confusing to use and gives the client a great platform to build on.

Next.js was the obvious choice for the frontend as it is a great framework for building static/dynamic sites. It was a no brainer in this case as I've used it many times before so know all the tips and tricks. It also meant I didn't have to learn a new framework while juggling everything else.

Alongside these I used TailwindCSS for the styling (do people use anything else!), Vercel for hosting both the CMS and the frontend (using a monorepo), MailerSend for transaction emails and Vercel Anlytics for simple vistor tracking.

Structuring Sanity

Before I jumped into any code I actually spent some time sketching out the data structure using a good old fashioned pen. I wanted to make sure I had a good understanding of the data before I started in Sanity.

Schema in Sanity is defined using JSON and is very flexible and powerful. You can quickly create relationships between documents and the like. The site didn't require too many unique pages, the majority of data was all focused around products.

One of my favourite features of Sanity is while you're creating your schema you're also creating the fields that will be displayed in the CMS. This is great for efficiency and allows you to make quick decisions in development as everything is instantly refreshed - bliss.

// Example of Sanity schema
export default defineType({
  name: 'size',
  fields: [
    {
      name: 'images',
      title: 'Images',
      type: 'array',
      description: "The first image will be used as the hero",
      of: [{ type: figureType.name }],
      group: 'content',
      validation: (Rule) => Rule.unique()
      ...

Once you have your schema that is when the fun starts. To get data out of Sanity's "Data Lake" you use Groq queries. These are similar to GraphQL but in my opinion much easier to learn and maybe even more powerful? This is where Next.js comes into play.

// Example of a Groq query which gets common fields and different documents
export const currentStockPageQuery = groq`
{
  "page": *[_type == "currentStockPage"] [0]{
    ${pageFields}
    "sizes": sizes[]->{
      title,
      ...

Next.js for the frontend

Next.js is probably one of the most used frontend frameworks at the momnent and allows you to build websites many different ways (especially with the new App Directory). You basically decide how you want your site to be built using methods like:

  • Static Generation (SSG)
  • Server Side Rendering (SSR)
  • Incremental Static Regeneration (ISR)
  • Client Side Rendering (CSR)
  • App Directory - which allows all of these at a component level...

For the most part I've found that ISR + On demand ISR is the best combo for clients that make updates often. I could talk all day on the different methods but I'll leave that for another article.

Once you've made that decision however you simply use a bunch of API's that Next.js provides. For example a very common one is called getStaticProps. This is called at build time or whenever the page is regenerated (ISR).

// Example of getStaticProps
export async function getStaticProps({ preview = false, previewData = {} }) {
  // query Sanity using groq and fetch
  const { page } = await getClient(preview).fetch(currentStockPageQuery)
  ...
}

This is where you have the chance to fetch data from Sanity using GroQ queries. This allows you to structure the data exactly how you want it by writing a query that returns the data you need.

Aside from the data fetching this site is also utilising Serverless functions for sending transaction emails and for page revlidation. The form function basically grabs the form request and uses MailerSend to send an email to the admin recipient. The revlidation function gets triggered by a Sanity webhook. The webhook is triggered when specific changes are made to content.

The Results

The site is now live and the client is very happy with the results. It's also lightning fast and has great SEO scores. They've had great feedback from customers and already have a bunch of new features to add in the coming months. Check it out here: Primo Bathroomware or view some pics below!

Home page
Home page
Collections
Collections
Stock page
Stock page
Product form
Product form