Starting 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.
Warning
This guide contains code examples for an older version of Sanity Studio (v2), which is deprecated.
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:
Set up a basic React SPA using create-react-app
Set up our own content management system using Sanity
Learn about navigating our app using React Router
Learn how to query for remote data
Get started with styling using Tailwind CSS
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!
Powered by Sanity
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.
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.
GROQ
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.
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:
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
Protip
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!
Setting up Sanity
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!
Create new project — Hit Enter.
Your project name: — We can name it whatever we would like. Let's “My Sanity Blog” for this project.
Use the default dataset configuration? — The default dataset configuration has a public dataset named “production”, let's stick with that. So type in “Y” and hit Enter.
Project output path: — This will show us the path where our sanity project will live. The path should show the path that leads to this: /sanity-react/mysanityblog. Hit Enter.
Select project template: — Here we are going to choose “Blog (schema)”. Using the arrow keys, navigate to that so it’s showing blue. Hit Enter once there. Success!
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.
Adding content to Sanity Studio
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.
Protip
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!
Connecting Sanity to our React app
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 lives
sanity-react/src -- where our React app lives
We 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:
npminstall @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";exportdefaultsanityClient({projectId:"Your Project ID Here",// find this at manage.sanity.io or in your sanity.jsondataset:"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.
For 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”.
Protip
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!
Building our React components
In this tutorial, we are going to have two pages for templates:
All blog posts in a tile format
One blog post and all the content for it
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.
Setting up React Router
To get React Router going, we are going to need to install our dependency. In the project, run the following command:
npminstall --save react-router-dom
Within the App.js file, change the code to include the router components:
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:
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”.
Protip
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!
Building the page for all the blog posts
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.jsimport React,{ useEffect, useState }from"react";import{ Link }from"react-router-dom";import sanityClient from"../client.js";exportdefaultfunctionAllPosts(){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!
Top of the file
We are going to be using hooks that are built into React for state management, so we have imported useEffect and useState.
Imported Link for linking to single blog posts.
Imported the sanityClient so we can fetch our data
AllPosts()
Setting our state
Using useEffect and sanityClient to fetch our data
The return
Setting a couple of headers
mapping over our blog posts and displaying the mainImage and title of each
Making those clickable and setting post.slug.current so that it sends us to the correct blog post
If we hit save and navigate to “localhost:3000”, we should now see all of our data!
Our 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.
Building the page for an individual blog post
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:
@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:
We have imported the dependencies that we installed earlier
Imported our Sanity Client
Imported all the things we need from React
Wrote out a function for 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.
OnePost()
Set our state
We are using useParams() for our slug. This is a React Hook that the react-router-dom package gives to pull URL parameters.
In the useEffect() we are again fetching our data from sanityClient
This GROQ query is a little different than our previous call. This one is pulling a particular post by its slug. Remember, this slug was set in our Sanity Studio. We are going to be pulling out the title, slug, mainImage, body, and then the name and image of the author!
We then set all the data and catch any errors!
If no post shows up, we'll show “Loading…” text.
The return
Display the title of the post
Show the author’s image and then name. We are using our 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.
We then display the main image of the blog post. Again, we are using urlFor() here.
Lastly, we have used 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!
Putting it all together
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.
Styling our application
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:
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.
Protip
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
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!
We built a blog!
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 – The Content Operating System that ends your CMS nightmares
Sanity replaces rigid content systems with a developer-first operating system. Define schemas in TypeScript, customize the editor with React, and deliver content anywhere with GROQ. Your team ships in minutes while you focus on building features, not maintaining infrastructure.
Sanity scales from weekend projects to enterprise needs and is used by companies like Puma, AT&T, Burger King, Tata, and Figma.
Automatically track when content was first published with a timestamp that sets once and never overwrites, providing reliable publication history for analytics and editorial workflows.
AI-powered automatic tagging for Sanity blog posts that analyzes content to generate 3 relevant tags, maintaining consistency by reusing existing tags from your content library.