Skip to content

Commit 756b365

Browse files
ijjkchibicodeLuis Alvarez
authored
Add CMS example for Sanity (vercel#10907)
* Add start of Sanity CMS example * Update cover-image * Clean up example * Apply suggestions from code review Co-Authored-By: Shu Uesugi <[email protected]> * Make changes from review * Remove extra style * Apply suggestions from code review Co-Authored-By: Shu Uesugi <[email protected]> * Make tweaks for preview mode and normalize env naming * Update viewing preview step * Delete author.jpg * Delete image.jpg * Update README * Fix environment variable names * Project ID is needed by lambda * Improved steps * Fixed issue with the project URL example * Clarify line * Get the preview post correctly * preview = false * Get unique posts * Show preview mode on index page * typo fix and use next latest * Added related examples section Co-authored-by: Shu Uesugi <[email protected]> Co-authored-by: Luis Alvarez <[email protected]>
1 parent 43c1190 commit 756b365

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1060
-0
lines changed

docs/advanced-features/preview-mode.md

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ https://<your-site>/api/preview?secret=<token>&slug=<path>
154154
Take a look at the following examples to learn more:
155155

156156
- [DatoCMS Example](https://github.com/zeit/next.js/tree/canary/examples/cms-datocms) ([Demo](https://next-blog-datocms.now.sh/))
157+
- [Sanity Example](https://github.com/zeit/next.js/tree/canary/examples/cms-sanity) ([Demo](https://next-blog-sanity.now.sh/))
157158

158159
## More Details
159160

docs/basic-features/data-fetching.md

+1
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ Take a look at the following examples to learn more:
489489

490490
- [Blog Starter using markdown files](https://github.com/zeit/next.js/tree/canary/examples/blog-starter) ([Demo](https://next-blog-starter.now.sh/))
491491
- [DatoCMS Example](https://github.com/zeit/next.js/tree/canary/examples/cms-datocms) ([Demo](https://next-blog-datocms.now.sh/))
492+
- [Sanity Example](https://github.com/zeit/next.js/tree/canary/examples/cms-sanity) ([Demo](https://next-blog-sanity.now.sh/))
492493

493494
## Learn more
494495

docs/basic-features/pages.md

+1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ Take a look at the following examples to learn more:
248248

249249
- [Blog Starter using markdown files](https://github.com/zeit/next.js/tree/canary/examples/blog-starter) ([Demo](https://next-blog-starter.now.sh/))
250250
- [DatoCMS Example](https://github.com/zeit/next.js/tree/canary/examples/cms-datocms) ([Demo](https://next-blog-datocms.now.sh/))
251+
- [Sanity Example](https://github.com/zeit/next.js/tree/canary/examples/cms-sanity) ([Demo](https://next-blog-sanity.now.sh/))
251252

252253
## Learn more
253254

examples/blog-starter/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ To create the blog posts we use [`remark`](https://github.com/remarkjs/remark) a
1313
### Related examples
1414

1515
- [DatoCMS](/examples/cms-datocms)
16+
- [Sanity](/examples/cms-sanity)
1617

1718
## How to use
1819

examples/cms-datocms/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This example showcases Next.js's [Static Generation](https://nextjs.org/docs/bas
99
### Related examples
1010

1111
- [Blog Starter](/examples/blog-starter)
12+
- [Sanity](/examples/cms-sanity)
1213

1314
## How to use
1415

examples/cms-sanity/.env.example

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
NEXT_EXAMPLE_CMS_SANITY_PREVIEW_SECRET=
2+
NEXT_EXAMPLE_CMS_SANITY_PROJECT_ID=
3+
NEXT_EXAMPLE_CMS_SANITY_API_TOKEN=

examples/cms-sanity/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.env
2+
.now

examples/cms-sanity/README.md

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# A statically generated blog example using Next.js and Sanity
2+
3+
This example showcases Next.js's [Static Generation](https://nextjs.org/docs/basic-features/pages) feature using [Sanity](https://www.sanity.io/) as the data source.
4+
5+
## Demo
6+
7+
### [https://next-blog-sanity.now.sh/](https://next-blog-sanity.now.sh/)
8+
9+
### Related examples
10+
11+
- [Blog Starter](/examples/blog-starter)
12+
- [DatoCMS](/examples/cms-datocms)
13+
14+
## How to use
15+
16+
### Using `create-next-app`
17+
18+
Execute [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
19+
20+
```bash
21+
npm init next-app --example cms-sanity cms-sanity-app
22+
# or
23+
yarn create next-app --example cms-sanity cms-sanity-app
24+
```
25+
26+
### Download manually
27+
28+
Download the example:
29+
30+
```bash
31+
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/cms-sanity
32+
cd cms-sanity
33+
```
34+
35+
## Configuration
36+
37+
### Step 1. Create an account and a project on Sanity
38+
39+
First, [create an account on Sanity](https://sanity.io).
40+
41+
After creating an account, install the Sanity cli from npm `npm i -g @sanity/cli`.
42+
43+
### Step 2. Create a new Sanity project
44+
45+
In a separate folder run `sanity init` to initialize a new studio project.
46+
47+
This will be where we manage our data.
48+
49+
When going through the init phase make sure to select **Yes** to the **Use the default dataset configuration** step and select **Clean project with no predefined schemas** for the **Select project template** step.
50+
51+
### Step 3. Generate an API token
52+
53+
Log into https://manage.sanity.io/ and choose the project you just created. Then from **Settings**, select **API**, then click **Add New Token** and create a token with the **Read** permission.
54+
55+
### Step 4. Set up environment variables
56+
57+
Copy the `.env.example` file in this directory to `.env` (which will be ignored by Git):
58+
59+
```bash
60+
cp .env.example .env
61+
```
62+
63+
Then set each variable on `.env`:
64+
65+
- `NEXT_EXAMPLE_CMS_SANITY_PREVIEW_SECRET` can be any random string (but avoid spaces), like `MY_SECRET` - this is used for [the Preview Mode](/docs/advanced-features/preview-mode.md).
66+
- `NEXT_EXAMPLE_CMS_SANITY_PROJECT_ID`: Get the `projectId` value from the `sanity.json` file created in step 2.
67+
- `NEXT_EXAMPLE_CMS_SANITY_API_TOKEN`: Copy the API token generated in the previous step.
68+
69+
Your `.env` file should look like this:
70+
71+
```bash
72+
NEXT_EXAMPLE_CMS_SANITY_PREVIEW_SECRET=...
73+
NEXT_EXAMPLE_CMS_SANITY_PROJECT_ID=...
74+
NEXT_EXAMPLE_CMS_SANITY_API_TOKEN=...
75+
```
76+
77+
### Step 5. Prepare project for previewing
78+
79+
Go to https://www.sanity.io/docs/preview-content-on-site and follow the three steps on that page. It should be done inside the studio project generated in Step 2.
80+
81+
When you get to the second step about creating a file called `resolveProductionUrl.js`, copy the following instead:
82+
83+
```js
84+
const previewSecret = 'MY_SECRET' // Copy the string you used for NEXT_EXAMPLE_CMS_SANITY_PREVIEW_SECRET
85+
const projectUrl = 'http://localhost:3000'
86+
87+
export default function resolveProductionUrl(document) {
88+
return `${projectUrl}/api/preview?secret=${previewSecret}&slug=${document.slug.current}`
89+
}
90+
```
91+
92+
### Step 6. Copy the schema file
93+
94+
After initializing your Sanity studio project there should be a `schemas` folder.
95+
96+
Replace the contents of `schema.js` in the Sanity studio project directory with [`./schemas/schema.js`](./schemas/schema.js) in this example directory. This will set up the schema we’ll use this for this example.
97+
98+
### Step 7. Populate Content
99+
100+
To add some content go to your Sanity studio project directory and run `sanity start`.
101+
102+
After the project has started and you have navigated to the URL given in the terminal, select **Author** and create a new record.
103+
104+
- You just need **1 Author record**.
105+
- Use dummy data for the text.
106+
- For the image, you can download one from [Unsplash](https://unsplash.com/).
107+
108+
Next, select **Post** and create a new record.
109+
110+
- We recommend creating at least **2 Post records**.
111+
- Use dummy data for the text.
112+
- You can write markdown for the **Content** field.
113+
- For the images, you can download ones from [Unsplash](https://unsplash.com/).
114+
- Pick the **Author** you created earlier.
115+
116+
**Important:** For each post record, you need to click **Publish** after saving. If not, the post will be in the draft state.
117+
118+
### Step 8. Run Next.js in development mode
119+
120+
```bash
121+
npm install
122+
npm run dev
123+
124+
# or
125+
126+
yarn install
127+
yarn dev
128+
```
129+
130+
Your blog should be up and running on [http://localhost:3000](http://localhost:3000)! If it doesn't work, post on [GitHub discussions](https://github.com/zeit/next.js/discussions).
131+
132+
### Step 9. Try preview mode
133+
134+
On Sanity, go to one of the posts you've created and:
135+
136+
- **Update the title**. For example, you can add `[Draft]` in front of the title.
137+
- As you edit the document it will be saved as a draft, but **DO NOT** click **Publish**. By doing this, the post will be in the draft state.
138+
139+
Now, if you go to the post page on localhost, you won't see the updated title. However, if you use the **Preview Mode**, you'll be able to see the change ([Documentation](/docs/advanced-features/preview-mode.md)).
140+
141+
To view the preview, go to the post edit page on Sanity, click the three dots above the document and select **Open preview** ([see the instruction here](https://www.sanity.io/docs/preview-content-on-site))
142+
143+
You should now be able to see the updated title. To exit the preview mode, you can click on _"Click here to exit preview mode"_ at the top.
144+
145+
### Step 10. Deploy on ZEIT Now
146+
147+
You can deploy this app to the cloud with [ZEIT Now](https://zeit.co/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
148+
149+
To deploy on ZEIT Now, you need to set the environment variables with **Now Secrets** using [Now CLI](https://zeit.co/download) ([Documentation](https://zeit.co/docs/now-cli#commands/secrets)).
150+
151+
Install [Now CLI](https://zeit.co/download), log in to your account from the CLI, and run the following commands to add the environment variables. Replace each variable with the corresponding strings in `.env`.
152+
153+
```
154+
now secrets add next_example_cms_sanity_preview_secret <NEXT_EXAMPLE_CMS_SANITY_PREVIEW_SECRET>
155+
now secrets add next_example_cms_sanity_api_token <NEXT_EXAMPLE_CMS_SANITY_API_TOKEN>
156+
now secrets add next_example_cms_sanity_project_id <NEXT_EXAMPLE_CMS_SANITY_PROJECT_ID>
157+
```
158+
159+
Then push the project to GitHub/GitLab/Bitbucket and [import to ZEIT Now](https://zeit.co/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) to deploy.
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Container from './container'
2+
import cn from 'classnames'
3+
import { EXAMPLE_PATH } from '../lib/constants'
4+
5+
export default function Alert({ preview }) {
6+
return (
7+
<div
8+
className={cn('border-b', {
9+
'bg-accent-7 border-accent-7 text-white': preview,
10+
'bg-accent-1 border-accent-2': !preview,
11+
})}
12+
>
13+
<Container>
14+
<div className="py-2 text-center text-sm">
15+
{preview ? (
16+
<>
17+
This page is a preview.{' '}
18+
<a
19+
href="/api/exit-preview"
20+
className="underline hover:text-cyan duration-200 transition-colors"
21+
>
22+
Click here
23+
</a>{' '}
24+
to exit preview mode.
25+
</>
26+
) : (
27+
<>
28+
The source code for this blog is{' '}
29+
<a
30+
href={`https://github.com/zeit/next.js/tree/canary/examples/${EXAMPLE_PATH}`}
31+
className="underline hover:text-success duration-200 transition-colors"
32+
>
33+
available on GitHub
34+
</a>
35+
.
36+
</>
37+
)}
38+
</div>
39+
</Container>
40+
</div>
41+
)
42+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default function Avatar({ name, picture }) {
2+
return (
3+
<div className="flex items-center">
4+
<img src={picture} className="w-12 h-12 rounded-full mr-4" alt={name} />
5+
<div className="text-xl font-bold">{name}</div>
6+
</div>
7+
)
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Container({ children }) {
2+
return <div className="container mx-auto px-5">{children}</div>
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import cn from 'classnames'
2+
import Link from 'next/link'
3+
import { imageBuilder } from '../lib/api'
4+
5+
export default function CoverImage({ title, url, slug }) {
6+
const image = (
7+
<img
8+
width={2000}
9+
height={1000}
10+
alt={`Cover Image for ${title}`}
11+
className={cn('shadow-small', {
12+
'hover:shadow-medium transition-shadow duration-200': slug,
13+
})}
14+
src={imageBuilder
15+
.image(url)
16+
.height(1000)
17+
.width(2000)
18+
.url()}
19+
/>
20+
)
21+
22+
return (
23+
<div className="-mx-5 sm:mx-0">
24+
{slug ? (
25+
<Link as={`/posts/${slug}`} href="/posts/[slug]">
26+
<a aria-label={title}>{image}</a>
27+
</Link>
28+
) : (
29+
image
30+
)}
31+
</div>
32+
)
33+
}
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { parseISO, format } from 'date-fns'
2+
3+
export default function Date({ dateString }) {
4+
const date = parseISO(dateString)
5+
return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time>
6+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import Container from './container'
2+
import { EXAMPLE_PATH } from '../lib/constants'
3+
4+
export default function Footer() {
5+
return (
6+
<footer className="bg-accent-1 border-t border-accent-2">
7+
<Container>
8+
<div className="py-28 flex flex-col lg:flex-row items-center">
9+
<h3 className="text-4xl lg:text-5xl font-bold tracking-tighter leading-tight text-center lg:text-left mb-10 lg:mb-0 lg:pr-4 lg:w-1/2">
10+
Statically Generated with Next.js.
11+
</h3>
12+
<div className="flex flex-col lg:flex-row justify-center items-center lg:pl-4 lg:w-1/2">
13+
<a
14+
href="https://nextjs.org/docs/basic-features/pages"
15+
className="mx-3 bg-black hover:bg-white hover:text-black border border-black text-white font-bold py-3 px-12 lg:px-8 duration-200 transition-colors mb-6 lg:mb-0"
16+
>
17+
Read Documentation
18+
</a>
19+
<a
20+
href={`https://github.com/zeit/next.js/tree/canary/examples/${EXAMPLE_PATH}`}
21+
className="mx-3 font-bold hover:underline"
22+
>
23+
View on GitHub
24+
</a>
25+
</div>
26+
</div>
27+
</Container>
28+
</footer>
29+
)
30+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Link from 'next/link'
2+
3+
export default function Header() {
4+
return (
5+
<h2 className="text-2xl md:text-4xl font-bold tracking-tight md:tracking-tighter leading-tight mb-20 mt-8">
6+
<Link href="/">
7+
<a className="hover:underline">Blog</a>
8+
</Link>
9+
.
10+
</h2>
11+
)
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import Avatar from '../components/avatar'
2+
import Date from '../components/date'
3+
import CoverImage from '../components/cover-image'
4+
import Link from 'next/link'
5+
6+
export default function HeroPost({
7+
title,
8+
coverImage,
9+
date,
10+
excerpt,
11+
author,
12+
slug,
13+
}) {
14+
return (
15+
<section>
16+
<div className="mb-8 md:mb-16">
17+
<CoverImage slug={slug} title={title} url={coverImage} />
18+
</div>
19+
<div className="md:grid md:grid-cols-2 md:col-gap-16 lg:col-gap-8 mb-20 md:mb-28">
20+
<div>
21+
<h3 className="mb-4 text-4xl lg:text-6xl leading-tight">
22+
<Link as={`/posts/${slug}`} href="/posts/[slug]">
23+
<a className="hover:underline">{title}</a>
24+
</Link>
25+
</h3>
26+
<div className="mb-4 md:mb-0 text-lg">
27+
<Date dateString={date} />
28+
</div>
29+
</div>
30+
<div>
31+
<p className="text-lg leading-relaxed mb-4">{excerpt}</p>
32+
<Avatar name={author.name} picture={author.picture} />
33+
</div>
34+
</div>
35+
</section>
36+
)
37+
}

0 commit comments

Comments
 (0)