Sanity Exchange: Community Appreciation Program
Overview of Sanity's Community Appreciation Program
Go to Sanity Exchange: Community Appreciation ProgramStarting from scratch, build your first blog using ReactJS. We'll learn about the basic setup for a React SPA, query for our data, and have all of our blog content be managed by Sanity. To finish off, we'll style our blog with Tailwind CSS.
This guide contains code examples for an older version of Sanity Studio (v2), which is deprecated.
Learn how to migrate to the new Studio v3 →TL;DR: Starting from scratch, we will build a blog using React and Sanity. We will create our React and Sanity projects, walk through the Sanity Studio, connect the React app to Sanity, and use React Routing to navigate through our app.
This guide will start from scratch, but if you would like to test out Sanity a little quicker, there are starters that you can find at sanity.io/create.
By starting from nothing, let's go over what we are going to cover in detail:
create-react-app
let’s see how you can fetch (structured) content from Sanity and use it in your own React application. This method will work for an existing React app or one that is at the create-react-app stage.
To find the completed project, head on over to this GitHub repo!
Sanity is how we can manage our structured content. Whether our application is a blog, a portfolio, or the next spaceship data cloud, we need all of our content to be handled in an organized and simple way. With Sanity, we can manage our content’s title, image, body text, etc. Anything content, Sanity can help manage.
Sanity can manage the content for many different use cases. For example, an e-commerce site has products, prices for each product, and a description of the product. Sanity can hold all that information in one main location. In the case of Sanity, we would use the Sanity Studio.
Not only do we get the power of Sanity but we also get Sanity Studio. Sanity Studio is an interface where we can view our content. The studio allows for image editing, the editing of fields, and can be used by developers and non-developers! All of this can be viewed in a nicely laid out UI/UX. In this tutorial, we will use Sanity Studio.
To learn more about the Sanity Studio, we can find the docs here.
Sanity offers two different ways to query for your content:
We are going to also be using GROQ today. GROQ stands for Graph-Relational Object Queries. This is designed to query collections of schema-less JSON documents. GROQ can do many things, for example, join several documents, expressive filtering, and create the response into something the application can use.
To learn more about GROQ visit the docs here.
Let’s get building!
Before we begin let's talk about how our project is going to be organized. Once we create our React app using create-react-app
, we will have our base React app. Once inside that application, we will create our Sanity Studio. Our folder structure will look like the following:
sanity-react (main project) |--- mysanityblog (Sanity project) |--- src (React application)
Our goal today is to:
Let's first set up a new React SPA (single-page application) in our local developer environment.
Note: If you already have an application, feel free to skip this setup part!
Run the following command in your shell, which you can find in Terminal.app if you are on Mac or CMD if you're on Windows:
npx create-react-app sanity-react
Note: npx
will download and run the command that follows it. So instead of installing create-react-app
with npm
, we can use it once with npx
. To make sure all of this works, be sure to download Node.js for your computer.
Once that is built out, run this command to go into the new project directory that the command made:
cd sanity-react
Open our newly created project in your code editor of choice. Once open, we should see our new React app! To double-check that everything is running smoothly, run the following command to view the React app in the browser:
npm start
We should be able to see our application running at localhost:3000!
Now that we have our application ready for us, let's get Sanity up and running! We may need to install the Sanity CLI globally on our local developer environment. To do that, run the command:
npm i -g @sanity/cli
The Sanity CLI can do many things! Some of those include initiating a new project, interacting with a current project, or upgrading the studio. We will be using the CLI today to initiate a new project.
Our next command will be activating Sanity to this particular project. We will be asked a bunch of questions, these are there to get our Sanity set up just right. To start, run the following command within our home folder (sanity-react
) to set up the Studio application:
sanity init
Note: You may need to log into your Sanity account or create a new Sanity account in the browser first. There will be prompts.
We are going to see a list of questions for us to answer in the next steps. Let's walk through those!
production
”, let's stick with that. So type in “Y” and hit Enter./sanity-react/mysanityblog
. Hit Enter.Once finished, we should see this:
To get the Sanity Studio running in our browser, we'll run the following commands to get into the new studio directory and start the development server:
cd mysanityblog
sanity start
Once it has compiled, we can go visit our new Sanity Studio at localhost:3333!
If we were to click on “Posts”, it would say, “No documents of this type found”. In the next section, we’ll walk through adding one in.
If we were to click on the edit icon next to our Studio name, we will be able to create a new Post, Author, or Category.
Choose “Post” and an “Untitled” page will appear with a bunch of empty text boxes! Filling these in helps us create our first blog post stored in the Studio. You may notice the “syncing” indicator in the bottom right. That means that all your changes are synced to Sanity’s content store in real-time and will be instantly available once you publish.
Go ahead and fill in the fields with any content you would like. It should look something like this:
When finished, be sure to hit the blue “Publish” button in the bottom right-hand corner.
Note: To do the Author field, we will need to add an author to the “Author” section under “Content” the same way we are adding a blog post.
Feel free to add as many blog posts as desired! The awesome thing is Sanity will be handling the management of this content, we’ll make a GROQ call for these posts, and display it in our React app.
Once we have finished adding in all the blog posts we could ever imagine, let’s head on over to our code and get these blog posts live in React!
Now that we are back in our React app, let’s start creating our blog! We are going to see multiple folders, the two we’re going to navigate between are:
sanity-react/mysanityblog
-- where our Sanity project livessanity-react/src
-- where our React app livesWe need to connect our React app to our Sanity project. We are going to want to bring in the Sanity Client. To do that run this command in the project:
npm install @sanity/client
Once done, we’ll first want to create a file in the React application that will import that package. Open src
and create a new file: src/client.js
and open it up. Inside that file let’s add the following code:
import sanityClient from "@sanity/client";
export default sanityClient({
projectId: "Your Project ID Here", // find this at manage.sanity.io or in your sanity.json
dataset: "production", // this is from those question during 'sanity init'
useCdn: true,
});
We’ll see a couple of comments here, the one we'll focus on right now is the projectId
.
There are two ways to find the projectId
.
sanity.json
fileFor the first way, navigate to mysanityblog/sanity.json
, under the api
section we should see the following code:
"api": {
"projectId": "Your Project ID Here",
"dataset": "production"
},
For the second method, we'll need to go to our Sanity Manage page. Find the tile that says “My Sanity Blog” (what we named it earlier) and click into it. We should then be taken to our dashboard for our project.
We'll see at the top, under the name of the project, “PROJECT ID”. Copy that and paste it into the src/client.js
file where it says projectId: “Your Project ID Here”
.
In that same dashboard view, we are going to want to add “localhost:3000” to our API settings so it knows that link can be trusted! In the “My Sanity Blog” dashboard, click on “Settings”. Once there, click on “API” on the left and we’ll see “CORS Origins”. Click on “ADD NEW ORIGIN” and add “http://localhost:3000”.
If you would like to add this with the CLI run the command sanity cors add http://localhost:3000
within your studio project.
Now our React app and Sanity Project are connected!
In this tutorial, we are going to have two pages for templates:
Let’s create those files. In the src/
folder, create the following folder: src/components
. Within that newly created folder, let’s create two files:
src/components/AllPosts.js
src/components/OnePost.js
We are going to be using React Router to navigate around our app. What this will do is in our App.js
file, we will declare “routes” for our components. For example, if we wanted to route to an About page, we could use the URL route /about
. By declaring our URL route and matching it up with a component, we are able to navigate to the proper page.
To set this up, let’s navigate to the src/App.js
file.
To get React Router going, we are going to need to install our dependency. In the project, run the following command:
npm install --save react-router-dom
Within the App.js
file, change the code to include the router components:
// src/App.js
import React from "react";
import { BrowserRouter, Route } from "react-router-dom";
import AllPosts from "./components/AllPosts.js";
import OnePost from "./components/OnePost.js";
function App() {
return (
<BrowserRouter>
<div>
<Route component={AllPosts} path="/" exact />
<Route component={OnePost} path="/:slug" />
</div>
</BrowserRouter>
);
}
export default App;
Let’s break this down.
By adding in the path=“/”
we are stating that when we are at the base URL for our website, display the AllPosts
component. Adding in the exact
makes it so there’s no confusion for other routes that have additional info after the “/”; “/additional-info”. In our case, we have /:slug
which will show the OnePost
component, but more specifically, which blog post to display from the corresponding blog post.
Within our two components, add the following code:
// src/components/AllPosts.js
import React from "react";
export default function AllPosts() {
return <h2>AllPosts Page</h2>;
}
// src/components/OnePost.js
import React from "react";
export default function OnePost() {
return <h2>OnePost Page</h2>;
}
Once we have all that code, restart the server (ctrl + c, npm start) and navigate to “localhost:3000”. At “localhost:3000/” we should see the text “AllPosts Page” and if we navigate to “localhost:3000/:slug” (replacing :slug with a slug) we should see the text “OnePost Page”.
If you want to test the slug part now, go grab a slug from your Sanity Studio. For example, localhost:3000/first-blog
Awesome! Our routing is set up!
Let's go ahead and display all of our posts’ titles and their main image on the “All Posts” page. Head on over to src/components/AllPosts.js
and paste the following code in there (we’ll go through it step-by-step after):
// src/components/AllPosts.js
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import sanityClient from "../client.js";
export default function AllPosts() {
const [allPostsData, setAllPosts] = useState(null);
useEffect(() => {
sanityClient
.fetch(
`*[_type == "post"]{
title,
slug,
mainImage{
asset->{
_id,
url
}
}
}`
)
.then((data) => setAllPosts(data))
.catch(console.error);
}, []);
return (
<div>
<h2>Blog Posts</h2>
<h3>Welcome to my blog posts page!</h3>
<div>
{allPostsData &&
allPostsData.map((post, index) => (
<Link to={"/" + post.slug.current} key={post.slug.current}>
<span key={index}>
<img src={post.mainImage.asset.url} alt="" />
<span>
<h2>{post.title}</h2>
</span>
</span>
</Link>
))}
</div>
</div>
);
}
Let’s walk through this section by section!
useEffect
and useState
.Link
for linking to single blog posts.sanityClient
so we can fetch our datauseEffect
and sanityClient
to fetch our datamapping
over our blog posts and displaying the mainImage
and title
of eachpost.slug.current
so that it sends us to the correct blog postOur images might be very large, we’ll style those soon and make it look nice!
There is a Sanity plugin for handling images that we will use in components/OnePost.js
, but in this component, we wanted to show how to GROQ for an image URL and display it without the plugin.
Now that we have our main blog page set up, let’s make those Link's navigate to an actual blog post and display all the information!
Before we write any code, we are going to want to install a couple of dependencies. Run the following command within the project:
npm install @sanity/block-content-to-react @sanity/image-url
Let's talk about these two:
@sanity/block-content-to-react
— This will render an array of block text from Sanity with React. To learn more about this, we can find the docs here. There are libraries for React, Vue, HTML, and HyperText.@sanity/image-url
— This library will help us with the image URL. Remember, in AllPosts.js
we did not use this, but rather saw how to without. We are going to use this plugin for OnePost.js
. To learn more about this, please visit the docs here.
Now that we have our dependencies installed, let’s go ahead and build out our component! Head on over to src/components/OnePost.js
and input the following code:
// src/components/OnePost.js
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import sanityClient from "../client.js";
import BlockContent from "@sanity/block-content-to-react";
import imageUrlBuilder from "@sanity/image-url";
const builder = imageUrlBuilder(sanityClient);
function urlFor(source) {
return builder.image(source);
}
export default function OnePost() {
const [postData, setPostData] = useState(null);
const { slug } = useParams();
useEffect(() => {
sanityClient
.fetch(
`*[slug.current == $slug]{
title,
slug,
mainImage{
asset->{
_id,
url
}
},
body,
"name": author->name,
"authorImage": author->image
}`,
{ slug }
)
.then((data) => setPostData(data[0]))
.catch(console.error);
}, [slug]);
if (!postData) return <div>Loading...</div>;
return (
<div>
<div>
<h2>{postData.title}</h2>
<div>
<img
src={urlFor(postData.authorImage).width(100).url()}
alt="Author is Kap"
/>
<h4>{postData.name}</h4>
</div>
</div>
<img src={urlFor(postData.mainImage).width(200).url()} alt="" />
<div>
<BlockContent
blocks={postData.body}
projectId={sanityClient.clientConfig.projectId}
dataset={sanityClient.clientConfig.dataset}
/>
</div>
</div>
);
}
Let’s walk through this code!
urlFor
so that we can use it below. Here is where we pulled in imageUrlBuilder
from @sanity/image-url
and used that within urlFor
. We'll use that lower in the file.useParams()
for our slug. This is a React Hook that the react-router-dom
package gives to pull URL parameters.useEffect()
we are again fetching our data from sanityClient
urlFor
here. Notice we chained multiple things here. First, the image, we then set width, and then added url()
to create that URL. To learn all about presenting images, visit this page.urlFor()
here.BlockContent
for the body of the blog. This will take all the text and images that are in our Sanity Studio and display it nicely!Now that we have our components set up, we can head on over to “localhost:3000” and see a list of our blog posts. If we click on any of those, it will redirect us to that particular blog post and display our information! That URL should look something like this: http://localhost:3000/sanity-react
And we now have a functioning blog with blog posts! All of our content is living in our Sanity Studio and if we want to add more, remove one, or edit the content in an existing one, we can head there to do so! Remember, our Sanity Studio is living at “localhost:3333” and we must run sanity start
in our folder sanity-react/mysanityblog
.
Let’s add some styling!
Feel free to skip this section if you would like to do your own styling!
We are going to be using Tailwind CSS! Tailwind is a CSS framework that can be highly-customizable! But also provides amazing building blocks right out of the box.
Let's go ahead and add that in. To do so, head to our src/index.css
file, clear out the existing code, and input the following code:
/* src/index.css */
@import url("https://unpkg.com/@tailwindcss/typography@0.2.x/dist/typography.min.css");
@import url("https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css");
@import url("https://fonts.googleapis.com/css2?family=Amatic+SC:wght@700&display=swap");
.cursive {
font-family: "Amatic SC", cursive;
font-weight: 700;
}
.prose img {
margin-left: auto;
margin-right: auto;
border-radius: 5px;
}
We are importing the necessary Tailwind packages. Also, a Google font that we’ll use.
The prose comes from that first import (typography). This will help our body in our “OnePost” render correctly. Tailwind needs this when pulling from Sanity Studio.
There are other ways to import Tailwind so you don’t need to import the entire package every time. You can find that information here: https://tailwindcss.com/docs/installation
Here is the AllPosts()
component with Tailwind:
// src/components/AllPosts.js
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import sanityClient from "../client.js";
export default function AllPosts() {
const [allPostsData, setAllPosts] = useState(null);
useEffect(() => {
sanityClient
.fetch(
`*[_type == "post"]{
title,
slug,
mainImage{
asset->{
_id,
url
}
}
}`
)
.then((data) => setAllPosts(data))
.catch(console.error);
}, []);
return (
<div className="bg-green-100 min-h-screen p-12">
<div className="container mx-auto">
<h2 className="text-5xl flex justify-center cursive">Blog Posts</h2>
<h3 className="text-lg text-gray-600 flex justify-center mb-12">
Welcome to my blog posts page!
</h3>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{allPostsData &&
allPostsData.map((post, index) => (
<Link to={"/" + post.slug.current} key={post.slug.current}>
<span
className="block h-64 relative rounded shadow leading-snug bg-white
border-l-8 border-green-400"
key={index}
>
<img
className="w-full h-full rounded-r object-cover absolute"
src={post.mainImage.asset.url}
alt=""
/>
<span
className="block relative h-full flex justify-end items-end pr
-4 pb-4"
>
<h2
className="text-gray-800 text-lg font-bold px-3 py-4 bg-red-700
text-red-100 bg-opacity-75 rounded"
>
{post.title}
</h2>
</span>
</span>
</Link>
))}
</div>
</div>
</div>
);
}
Once that is all in, we should see something like this at “localhost:3000”:
Looks great!
Here is the OnePost()
component with Tailwind:
// src/component/OnePost.js
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import sanityClient from "../client.js";
import BlockContent from "@sanity/block-content-to-react";
import imageUrlBuilder from "@sanity/image-url";
const builder = imageUrlBuilder(sanityClient);
function urlFor(source) {
return builder.image(source);
}
export default function OnePost() {
const [postData, setPostData] = useState(null);
const { slug } = useParams();
useEffect(() => {
sanityClient
.fetch(
`*[slug.current == "${slug}"]{
title,
slug,
mainImage{
asset->{
_id,
url
}
},
body,
"name": author->name,
"authorImage": author->image
}`
)
.then((data) => setPostData(data[0]))
.catch(console.error);
}, [slug]);
if (!postData) return <div>Loading...</div>;
return (
<div className="bg-gray-200 min-h-screen p-12">
<div className="container shadow-lg mx-auto bg-green-100 rounded-lg">
<div className="relative">
<div className="absolute h-full w-full flex items-center justify-center p-8">
{/* Title Section */}
<div className="bg-white bg-opacity-75 rounded p-12">
<h2 className="cursive text-3xl lg:text-6xl mb-4">
{postData.title}
</h2>
<div className="flex justify-center text-gray-800">
<img
src={urlFor(postData.authorImage).url()}
className="w-10 h-10 rounded-full"
alt="Author is Kap"
/>
<h4 className="cursive flex items-center pl-2 text-2xl">
{postData.name}
</h4>
</div>
</div>
</div>
<img
className="w-full object-cover rounded-t"
src={urlFor(postData.mainImage).url()}
alt=""
style={{ height: "400px" }}
/>
</div>
<div className="px-16 lg:px-48 py-12 lg:py-20 prose lg:prose-xl max-w-full">
<BlockContent
blocks={postData.body}
projectId={sanityClient.clientConfig.projectId}
dataset={sanityClient.clientConfig.dataset}
/>
</div>
</div>
</div>
);
}
Notice we are using the prose for our BlockContent
!
Awesome! Go ahead and navigate to “localhost:3000” and click on the blog post card. We’ll then be navigated to the blog post! Here’s an example of how it should look!
Using React from create-react-app
, using Sanity to manage our content, and using Tailwind CSS for styling, we have created a pretty cool blog!
The great thing about this is all we have to do is add blog posts to our Sanity Studio and it will automatically show up in our React application!
Remember, the final code for this project can be found here!
What do you think of your blog built with React and Sanity? Let us know in the Community or on Twitter! We would love to see it! The sky’s the limit with your new blog! Happy blogging!
Sanity Composable Content Cloud is the headless CMS that gives you (and your team) a content backend to drive websites and applications with modern tooling. It offers a real-time editing environment for content creators that’s easy to configure but designed to be customized with JavaScript and React when needed. With the hosted document store, you query content freely and easily integrate with any framework or data source to distribute and enrich content.
Sanity scales from weekend projects to enterprise needs and is used by companies like Puma, AT&T, Burger King, Tata, and Figma.
Overview of Sanity's Community Appreciation Program
Go to Sanity Exchange: Community Appreciation ProgramWhen using Sanity, it's important to understand these 3 things when getting started with Portable Text.
Go to 3 things you need to know when getting started with Portable Text