In this article we will create a simple blog with React JS and Strapi.
Strapi is a content management system that is open source and easy to use, moreover, it works splendidly with React.
First let’s setup our workspace, create a folder where you will put your project, let’s call it strapi-blog, and then access it. I personally work with yarn more than npm but you can use whichever you like. So in your console write
yarn create strapi-app backend --quickstart --no-run
This single command line will create all you need for your back-end. Make sure to add the --no-run
flag as it will prevent your app from automatically starting the server because we will still need to add more plugins to strapi shortly.
Now that you know that we need to install some plugins to enhance your app, let’s install one of our most popular ones: the graphql
plugin.
yarn strapi install graphql
Once the installation is completed, you can finally start your Strapi server strapi dev
and signup.


Strapi runs on http://localhost:1337.
Now it is time to start the frontend part with React.
- Create a React
frontend
server by running the following command:
yarn create react-app frontend
Once the installation is completed, you can start your front-end app to make sure everything went ok.
cd frontend
yarn dev
We still have to add more stuff to it, so bare with me.
First let’s add react-router-dom by typing the following command yarn add react-router-dom
Now remove everything inside your src
folder, we don’t need it.
Create an index.js
 file in the src containing the following code:
import React from "react";
import ReactDOM from "react-dom";
import App from "./containers/App";
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
);
If you are wondering about the App file it will be put in a containers
folder.
- Create a
containers/App
folder and aindex.js
file inside containing the following code:
import React from "react";
function App() {
return <div className="App" />;
}
export default App;
To make your blog look pretty, we will use a popular CSS framework for styling: UiKit and Apollo to query Strapi with GraphQL.
Add the following line in your public/index.html
in order to use UIkit from their CDN
...
<title>React App</title>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Staatliches"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/css/uikit.min.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit-icons.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.2.0/js/uikit.js"></script>
...
- Create an
./src/index.css
file containing the following style:
a {
text-decoration: none;
}
h1 {
font-family: Staatliches;
font-size: 120px;
}
#category {
font-family: Staatliches;
font-weight: 500;
}
#title {
letter-spacing: 0.4px;
font-size: 22px;
font-size: 1.375rem;
line-height: 1.13636;
}
#banner {
margin: 20px;
height: 800px;
}
#editor {
font-size: 16px;
font-size: 1rem;
line-height: 1.75;
}
.uk-navbar-container {
background: #fff !important;
font-family: Staatliches;
}
img:hover {
opacity: 1;
transition: opacity 0.25s cubic-bezier(0.39, 0.575, 0.565, 1);
}
Now we have to add the apollo dependancy to our project in order to communicate with out backoffice.
Simply run the following command
yarn add apollo-boost @apollo/react-hooks graphql react-apollo
And we will need to make a file that would take care of the communication.
- Create a
./src/utils/apolloClient.js
file containing the following code:
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { HttpLink } from "apollo-link-http";
const cache = new InMemoryCache();
const link = new HttpLink({
uri: `${process.env.REACT_APP_BACKEND_URL}/graphql`
});
const client = new ApolloClient({
cache,
link
});
export default client;
Next create a .env
file in the root of your application containing REACT_APP_BACKEND_URL
environment variable as follows REACT_APP_BACKEND_URL="http://localhost:1337"
- Wrap your
App/index.js
inside theApolloProvider
and import yourindex.css
file:
import React from "react";
import ReactDOM from "react-dom";
import { ApolloProvider } from "react-apollo";
import App from "./containers/App";
import client from "./utils/apolloClient";
import { BrowserRouter as Router } from "react-router-dom";
import "./index.css";
ReactDOM.render(
<Router>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</Router>,
document.getElementById("root")
);
Awesome, everything went okay you should have a blank page, so let’s make some articles to display.
Dive in your Strapi admin panel and click on the Content Type Builder
link in the sidebar.


Click on Create new content-type
and call it article
.
Now you’ll be asked to create all the fields for your content-type:
- Create the following ones:
title
with type Text (required)content
with type Rich Text (required)image
with type Media (Single image) and (required)published_at
with type date (required)
Press Save! Here you go, your first content type has been created. Now you may want to create your first article, but we have one thing to do before that: Grant access to the article content type.
- Click on the Roles & Permission and click on the
public
role. - Check the article
find
andfindone
routes and save.


Now, create your first article !
If you go to http://localhost:1337/articles you’ll find it!
Create categories
You may want to assign a category to your article (news, trends, opinion). You are going to do this by creating another content type in Strapi.
- Create a
category
content type with the following field name
with type Text
Press save!
- Create a new field in the Article content type which is a Relation
Category has many Articles


- Click on the Roles & Permission and click on the
public
role. And check the categoryfind
andfindone
routes and save.
Now you’ll be able to select a category for your article in the right sidebox.


Now that we are good with Strapi let’s work on the frontend part!
Create the Query component
We will use Apollo to fetch your data from different pages. We don’t want you to rewrite the same code every time in your pages. This is why we are going to write a Query
component that will be reusable!
- Let’s create a
./src/components/Query/index.js
file containing the following code:
import React from "react";
import { useQuery } from "@apollo/react-hooks";
const Query = ({ children, query, id }) => {
const { data, loading, error } = useQuery(query, {
variables: { id: id }
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {JSON.stringify(error)}</p>;
return children({ data });
};
export default Query;
We are using the useQuery
hook to call your Strapi server at this address http://localhost:1337/graphql
. We are sending an id
if it exists (it will be necessary when you’ll want to fetch just one article).
If the request is successful, you will return the child component with the retrieved data as prop.
Let’s try it out by creating your navbar that will fetch all our categories but first let’s create the GraphQL query
- Let’s create a
./src/queries/category/categories.js
file containing the following code:
import gql from "graphql-tag";
const CATEGORIES_QUERY = gql`
query Categories {
categories {
id
name
}
}
`;
export default CATEGORIES_QUERY;
Let’s use this query to display your categories inside your navbar
- Let’s create a
./src/components/Nav/index.js
file containing the following code:
import React from "react";
import Query from "../Query";
import { Link } from "react-router-dom";
import CATEGORIES_QUERY from "../../queries/category/categories";
const Nav = () => {
return (
<div>
<Query query={CATEGORIES_QUERY} id={null}>
{({ data: { categories } }) => {
return (
<div>
<nav className="uk-navbar-container" data-uk-navbar>
<div className="uk-navbar-left">
<ul className="uk-navbar-nav">
<li>
<Link to="/">Strapi Blog</Link>
</li>
</ul>
</div>
<div className="uk-navbar-right">
<ul className="uk-navbar-nav">
{categories.map((category, i) => {
return (
<li key={category.id}>
<Link
to={`/category/${category.id}`}
className="uk-link-reset"
>
{category.name}
</Link>
</li>
);
})}
</ul>
</div>
</nav>
</div>
);
}}
</Query>
</div>
);
};
export default Nav;
Since we want our navigation bar to be in every pages of our application we are going to use it inside our App container
- Import and declare your navigation component inside your
containers/App/index.js
import React from "react";
import Nav from "../../components/Nav";
function App() {
return (
<div className="App">
<Nav />
</div>
);
}
export default App;
You should now be able to see your brand new navbar containing your categories. But the links are not working right now. We’ll work on this later in the tutorial, don’t worry.
Create the Articles containers
This container will wrap a component that will display all your articles
- Let’s create a
containers/Articles/index.js
file containing the following code:
import React from "react";
import Articles from "../../components/Articles";
import Query from "../../components/Query";
import ARTICLES_QUERY from "../../queries/article/articles";
const Home = () => {
return (
<div>
<div className="uk-section">
<div className="uk-container uk-container-large">
<h1>Strapi blog</h1>
<Query query={ARTICLES_QUERY}>
{({ data: { articles } }) => {
return <Articles articles={articles} />;
}}
</Query>
</div>
</div>
</div>
);
};
export default Home;
Let’s write the query that fetch all your articles
- Let’s create a
./src/queries/articles/articles.js
file containing the following code:
import gql from "graphql-tag";
const ARTICLES_QUERY = gql`
query Articles {
articles {
id
title
category {
id
name
}
image {
url
}
}
}
`;
export default ARTICLES_QUERY;
Now we need to create an Articles
component that will display all of your articles and an Card component that will display each of your article:
- Let’s create a
components/Articles/index.js
file containing the following:
import React from "react";
import Card from "../Card";
const Articles = ({ articles }) => {
const leftArticlesCount = Math.ceil(articles.length / 5);
const leftArticles = articles.slice(0, leftArticlesCount);
const rightArticles = articles.slice(leftArticlesCount, articles.length);
return (
<div>
<div className="uk-child-width-1-2" data-uk-grid>
<div>
{leftArticles.map((article, i) => {
return <Card article={article} key={`article__${article.id}`} />;
})}
</div>
<div>
<div className="uk-child-width-1-2@m uk-grid-match" data-uk-grid>
{rightArticles.map((article, i) => {
return <Card article={article} key={`article__${article.id}`} />;
})}
</div>
</div>
</div>
</div>
);
};
export default Articles;
- Let’s create a
components/Card/index.js
file containing the following code:
import React from "react";
import { Link } from "react-router-dom";
const Card = ({ article }) => {
const imageUrl =
process.env.NODE_ENV !== "development"
? article.image.url
: process.env.REACT_APP_BACKEND_URL + article.image.url;
return (
<Link to={`/article/${article.id}`} className="uk-link-reset">
<div className="uk-card uk-card-muted">
<div className="uk-card-media-top">
<img
src={imageUrl}
alt={article.image.url}
height="100"
/>
</div>
<div className="uk-card-body">
<p id="category" className="uk-text-uppercase">
{article.category.name}
</p>
<p id="title" className="uk-text-large">
{article.title}
</p>
</div>
</div>
</Link>
);
};
export default Card;
Everything is ready, we just need to import the Articles container inside the App container.
- Import and declare your
Articles
container inside yourcontainers/App/index.js
:
import React from "react";
import Articles from "../Articles";
import Nav from "../../components/Nav";
function App() {
return (
<div className="App">
<Nav />
<Articles />
</div>
);
}
export default App;


Create the Article container
We can see that if you click on the article, there is nothing. Let’s create the article container together! But first, you’ll need two packages:
- Install
react-moment
andreact-markdown
by running the following command:
yarn add react-moment react-markdown moment
react-moment
will give you the ability to display the publication date of your article, and react-markdown
will be used to display the content of your article in markdown.
- Let’s create a
./containers/Article/index.js
file containing the following:
import React from "react";
import { useParams } from "react-router";
import Query from "../../components/Query";
import ReactMarkdown from "react-markdown";
import Moment from "react-moment";
import ARTICLE_QUERY from "../../queries/article/article";
const Article = () => {
let { id } = useParams();
return (
<Query query={ARTICLE_QUERY} id={id}>
{({ data: { article } }) => {
const imageUrl =
process.env.NODE_ENV !== "development"
? article.image.url
: process.env.REACT_APP_BACKEND_URL + article.image.url;
return (
<div>
<div
id="banner"
className="uk-height-medium uk-flex uk-flex-center uk-flex-middle uk-background-cover uk-light uk-padding uk-margin"
data-src={imageUrl}
data-srcset={imageUrl}
data-uk-img
>
<h1>{article.title}</h1>
</div>
<div className="uk-section">
<div className="uk-container uk-container-small">
<ReactMarkdown source={article.content} />
<p>
<Moment format="MMM Do YYYY">{article.published_at}</Moment>
</p>
</div>
</div>
</div>
);
}}
</Query>
);
};
export default Article;
Let’s write the query for just one article now!
- Let’s create a
./src/queries/article/article.js
containing the following code:
import gql from "graphql-tag";
const ARTICLE_QUERY = gql`
query Articles($id: ID!) {
article(id: $id) {
id
title
content
image {
url
}
category {
id
name
}
published_at
}
}
`;
export default ARTICLE_QUERY;
Article page is ready, we just need to add this new Article container inside the App container. You are going to use the Switch and Route components from react-router-dom
in order to establish a routing system for your article page
- Import and declare your
Article
container inside yourcontainers/App/index.js
:
import React from "react";
import { Switch, Route } from "react-router-dom";
import Nav from "../../components/Nav";
import Articles from "../Articles";
import Article from "../Article";
function App() {
return (
<div className="App">
<Nav />
<Switch>
<Route path="/" component={Articles} exact />
<Route path="/article/:id" component={Article} exact />
</Switch>
</div>
);
}
export default App;
Now you should be able to get your article.
Categories
You may want to separate your article depending on the categories! Let’s create a page for each category then:
- Let’s create a
./containers/Category/index.js
file containing the following:
import React from "react";
import { useParams } from "react-router";
import Articles from "../../components/Articles";
import Query from "../../components/Query";
import CATEGORY_ARTICLES_QUERY from "../../queries/category/articles";
const Category = () => {
let { id } = useParams();
return (
<Query query={CATEGORY_ARTICLES_QUERY} id={id}>
{({ data: { category } }) => {
return (
<div>
<div className="uk-section">
<div className="uk-container uk-container-large">
<h1>{category.name}</h1>
<Articles articles={category.articles} />
</div>
</div>
</div>
);
}}
</Query>
);
};
export default Category;
- Let’s create a
./src/queries/category/articles.js
file containing the following:
import gql from 'graphql-tag';
const CATEGORY_ARTICLES_QUERY = gql`
query Category($id: ID!){
category(id: $id) {
name
articles {
id
title
content
image {
url
}
category {
id
name
}
}
}
}
`;
export default CATEGORY_ARTICLES_QUERY;
Category page is ready, we just need to add this new container inside the App container.
- Import and declare your
Category
container inside yourcontainers/App/index.js
:
import React from "react";
import { Switch, Route } from "react-router-dom";
import Nav from "../../components/Nav";
import Articles from "../Articles";
import Article from "../Article";
import Category from "../Category";
function App() {
return (
<div className="App">
<Nav />
<Switch>
<Route path="/" component={Articles} exact />
<Route path="/article/:id" component={Article} exact />
<Route path="/category/:id" component={Category} exact />
</Switch>
</div>
);
}
export default App;
If you all is followed and done correctly, I tip my hat to you for you have finished your strapi react blog and should be able to use it. I hope you enjoyed this tutorial, and come back again for other ones soon.