Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve sanity.io example #18227

Merged
merged 10 commits into from
Feb 9, 2021
3 changes: 2 additions & 1 deletion examples/cms-sanity/.env.local.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
NEXT_PUBLIC_SANITY_PROJECT_ID=
NEXT_PUBLIC_SANITY_DATASET=
SANITY_API_TOKEN=
SANITY_PREVIEW_SECRET=
SANITY_PREVIEW_SECRET=
2 changes: 2 additions & 0 deletions examples/cms-sanity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,15 @@ cp .env.local.example .env.local
Then set each variable on `.env.local`:

- `NEXT_PUBLIC_SANITY_PROJECT_ID` should be the `projectId` value from the `sanity.json` file created in step 2.
- `NEXT_PUBLIC_SANITY_DATASET` should be the `dataset` value from the `sanity.json` file created in step 2 - defaults to `production` if not set.
- `SANITY_API_TOKEN` should be the API token generated in the previous step.
- `SANITY_PREVIEW_SECRET` can be any random string (but avoid spaces), like `MY_SECRET` - this is used for [Preview Mode](https://nextjs.org/docs/advanced-features/preview-mode).

Your `.env.local` file should look like this:

```bash
NEXT_PUBLIC_SANITY_PROJECT_ID=...
NEXT_PUBLIC_SANITY_DATASET=...
rexxars marked this conversation as resolved.
Show resolved Hide resolved
SANITY_API_TOKEN=...
SANITY_PREVIEW_SECRET=...
```
Expand Down
8 changes: 7 additions & 1 deletion examples/cms-sanity/components/avatar.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { urlForImage } from '../lib/sanity'

export default function Avatar({ name, picture }) {
return (
<div className="flex items-center">
<img src={picture} className="w-12 h-12 rounded-full mr-4" alt={name} />
<img
src={urlForImage(picture).height(96).width(96).fit('crop').url()}
className="w-12 h-12 rounded-full mr-4"
alt={name}
/>
<div className="text-xl font-bold">{name}</div>
</div>
)
Expand Down
10 changes: 6 additions & 4 deletions examples/cms-sanity/components/cover-image.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import cn from 'classnames'
import Link from 'next/link'
import { imageBuilder } from '../lib/sanity'
import { urlForImage } from '../lib/sanity'

export default function CoverImage({ title, url, slug }) {
const image = (
export default function CoverImage({ title, slug, image: source }) {
const image = source ? (
<img
width={2000}
height={1000}
alt={`Cover Image for ${title}`}
className={cn('shadow-small', {
'hover:shadow-medium transition-shadow duration-200': slug,
})}
src={imageBuilder.image(url).height(1000).width(2000).url()}
src={urlForImage(source).height(1000).width(2000).url()}
/>
) : (
<div style={{ paddingTop: '50%', backgroundColor: '#ddd' }} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't need to block this PR, but we should use next/image here so we don't need to do paddingTop hacks. Follow up perhaps?

)

return (
Expand Down
2 changes: 1 addition & 1 deletion examples/cms-sanity/components/hero-post.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function HeroPost({
return (
<section>
<div className="mb-8 md:mb-16">
<CoverImage slug={slug} title={title} url={coverImage} />
<CoverImage slug={slug} title={title} image={coverImage} />
</div>
<div className="md:grid md:grid-cols-2 md:col-gap-16 lg:col-gap-8 mb-20 md:mb-28">
<div>
Expand Down
2 changes: 1 addition & 1 deletion examples/cms-sanity/components/meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function Meta() {
name="description"
content={`A statically generated blog example using Next.js and ${CMS_NAME}.`}
/>
<meta property="og:image" content={HOME_OG_IMAGE_URL} />
<meta property="og:image" content={HOME_OG_IMAGE_URL} key="ogImage" />
</Head>
)
}
2 changes: 1 addition & 1 deletion examples/cms-sanity/components/post-header.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function PostHeader({ title, coverImage, date, author }) {
<Avatar name={author.name} picture={author.picture} />
</div>
<div className="mb-8 md:mb-16 sm:mx-0">
<CoverImage title={title} url={coverImage} />
<CoverImage title={title} image={coverImage} />
</div>
<div className="max-w-2xl mx-auto">
<div className="block md:hidden mb-6">
Expand Down
2 changes: 1 addition & 1 deletion examples/cms-sanity/components/post-preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function PostPreview({
return (
<div>
<div className="mb-5">
<CoverImage slug={slug} title={title} url={coverImage} />
<CoverImage slug={slug} title={title} image={coverImage} />
</div>
<h3 className="text-3xl mb-3 leading-snug">
<Link as={`/posts/${slug}`} href="/posts/[slug]">
Expand Down
72 changes: 0 additions & 72 deletions examples/cms-sanity/lib/api.js

This file was deleted.

9 changes: 9 additions & 0 deletions examples/cms-sanity/lib/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const sanityConfig = {
// Find your project ID and dataset in `sanity.json` in your studio project
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
useCdn: process.env.NODE_ENV === 'production',
// useCdn == true gives fast, cheap responses using a globally distributed cache.
// Set this to false if your application require the freshest possible
// data always (potentially slightly slower and a bit more expensive).
}
37 changes: 37 additions & 0 deletions examples/cms-sanity/lib/queries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const postFields = `
_id,
name,
title,
date,
excerpt,
coverImage,
"slug": slug.current,
"author": author->{name, picture},
`

export const indexQuery = `
*[_type == "post"] | order(date desc, _updatedAt desc) {
${postFields}
}`

export const postQuery = `
{
"post": *[_type == "post" && slug.current == $slug] | order(_updatedAt desc) | [0] {
content,
${postFields}
},
"morePosts": *[_type == "post" && slug.current != $slug] | order(date desc, _updatedAt desc) | [0...2] {
content,
${postFields}
}
}`

export const postSlugsQuery = `
*[_type == "post" && defined(slug.current)][].slug.current
`

export const postBySlugQuery = `
*[_type == "post" && slug.current == $slug][0] {
${postFields}
}
`
32 changes: 11 additions & 21 deletions examples/cms-sanity/lib/sanity.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
import sanityClient from '@sanity/client'
import sanityImage from '@sanity/image-url'
import {
createImageUrlBuilder,
createPreviewSubscriptionHook,
} from 'next-sanity'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! 🚀

import { sanityConfig } from './config'

const options = {
// Find your project ID and dataset in `sanity.json` in your studio project
dataset: 'production',
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
useCdn: process.env.NODE_ENV === 'production',
// useCdn == true gives fast, cheap responses using a globally distributed cache.
// Set this to false if your application require the freshest possible
// data always (potentially slightly slower and a bit more expensive).
}
export const imageBuilder = createImageUrlBuilder(sanityConfig)

const client = sanityClient(options)
export const urlForImage = (source) =>
imageBuilder.image(source).auto('format').fit('max')

export const imageBuilder = sanityImage(client)

export const previewClient = sanityClient({
...options,
useCdn: false,
token: process.env.SANITY_API_TOKEN,
})

export default client
export const usePreviewSubscription = createPreviewSubscriptionHook(
sanityConfig
)
32 changes: 32 additions & 0 deletions examples/cms-sanity/lib/sanity.server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Server-side Sanity utilities. By having these in a separate file from the
* utilities we use on the client side, we are able to tree-shake (remove)
* code that is not used on the client side.
*/
import { createClient } from 'next-sanity'
import { sanityConfig } from './config'

export const sanityClient = createClient(sanityConfig)

export const previewClient = createClient({
...sanityConfig,
useCdn: false,
token: process.env.SANITY_API_TOKEN,
})

export const getClient = (preview) => (preview ? previewClient : sanityClient)

export function overlayDrafts(docs) {
const documents = docs || []
const overlayed = documents.reduce((map, doc) => {
if (!doc._id) {
throw new Error('Ensure that `_id` is included in query projection')
}

const isDraft = doc._id.startsWith('drafts.')
const id = isDraft ? doc._id.slice(7) : doc._id
return isDraft || !map.has(id) ? map.set(id, doc) : map
}, new Map())

return Array.from(overlayed.values())
}
3 changes: 1 addition & 2 deletions examples/cms-sanity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
},
"dependencies": {
"@sanity/block-content-to-react": "2.0.7",
"@sanity/client": "1.149.2",
"@sanity/image-url": "0.140.17",
"classnames": "2.2.6",
"date-fns": "2.10.0",
"next-sanity": "0.1.5",
"next": "latest",
"react": "^16.13.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow up could also update these libraries 😄

"react-dom": "^16.13.0"
Expand Down
9 changes: 6 additions & 3 deletions examples/cms-sanity/pages/api/preview.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getPreviewPostBySlug } from '../../lib/api'
import { postBySlugQuery } from '../../lib/queries'
import { previewClient } from '../../lib/sanity.server'

export default async function preview(req, res) {
// Check the secret and next parameters
Expand All @@ -10,8 +11,10 @@ export default async function preview(req, res) {
return res.status(401).json({ message: 'Invalid token' })
}

// Fetch the headless CMS to check if the provided `slug` exists
const post = await getPreviewPostBySlug(req.query.slug)
// Check if the post with the given `slug` exists
const post = await previewClient.fetch(postBySlugQuery, {
slug: req.query.slug,
})

// If the slug doesn't exist prevent preview mode from being enabled
if (!post) {
Expand Down
7 changes: 4 additions & 3 deletions examples/cms-sanity/pages/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Head from 'next/head'
import Container from '../components/container'
import MoreStories from '../components/more-stories'
import HeroPost from '../components/hero-post'
import Intro from '../components/intro'
import Layout from '../components/layout'
import { getAllPostsForHome } from '../lib/api'
import Head from 'next/head'
import { CMS_NAME } from '../lib/constants'
import { indexQuery } from '../lib/queries'
import { getClient, overlayDrafts } from '../lib/sanity.server'

export default function Index({ allPosts, preview }) {
const heroPost = allPosts[0]
Expand Down Expand Up @@ -36,7 +37,7 @@ export default function Index({ allPosts, preview }) {
}

export async function getStaticProps({ preview = false }) {
const allPosts = await getAllPostsForHome(preview)
const allPosts = overlayDrafts(await getClient(preview).fetch(indexQuery))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's just the naming of this (overlayDrafts) but I'm pretty confused as to what this does.

return {
props: { allPosts, preview },
}
Expand Down
Loading