Skip to content

Commit

Permalink
Improve sanity.io example (#18227)
Browse files Browse the repository at this point in the history
  • Loading branch information
rexxars committed Feb 9, 2021
1 parent 5d58626 commit 394bbf6
Show file tree
Hide file tree
Showing 18 changed files with 183 additions and 150 deletions.
3 changes: 2 additions & 1 deletion examples/cms-sanity/.env.local.example
@@ -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
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=...
SANITY_API_TOKEN=...
SANITY_PREVIEW_SECRET=...
```
Expand Down
8 changes: 7 additions & 1 deletion examples/cms-sanity/components/avatar.js
@@ -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
@@ -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' }} />
)

return (
Expand Down
2 changes: 1 addition & 1 deletion examples/cms-sanity/components/hero-post.js
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
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
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
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
@@ -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
@@ -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
@@ -1,24 +1,14 @@
import sanityClient from '@sanity/client'
import sanityImage from '@sanity/image-url'
import {
createImageUrlBuilder,
createPreviewSubscriptionHook,
} from 'next-sanity'
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
@@ -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
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",
"react-dom": "^16.13.0"
Expand Down
9 changes: 6 additions & 3 deletions examples/cms-sanity/pages/api/preview.js
@@ -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
@@ -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))
return {
props: { allPosts, preview },
}
Expand Down

0 comments on commit 394bbf6

Please sign in to comment.