Overview
In this post, I will introduce a way to build a simple web application that is a simple blog with a famous headless CMS, contentful.
Sample
Here is my code for this post.
repo https://github.com/koji/typescript/tree/master/simple_blog_with_contentful
What is Headless CMS?
Let me tell you what a headless CMS briefly.
Roughly speaking, a headless CMS is a CMS without the functionality of displaying content.
Let's say it is like WordPress that has only a dashboard
.
Probably, some of you think why we need to use headless CMS instead of other CMSes.
-
merits
- Can focus on frontend and there is no limitation by a CMS(You can use anything you want to use and the development process is the same as normal frontend development)
- In terms of Contentful, it offers the npm package for fetching data
-
demerits
- It would be hard to change looks for non-tech people
- If a headless CMS gets issues, we need to wait until they will be fixed by a service provider
About Contentful
https://www.contentful.com/developers/
Contentful is great since it has good documentation, Slack group, and forum.
## Step 1 Define Contentful models
Loggin Contentful and define models to display data on react application.
In this post, I just created 3 items, title, eye-catch image, and body.
Data structure
{
"name": "easysite", <-- コンテンツモデル名
"description": "simple website like a blog",
"displayField": "title",
"fields": [
{
"id": "title",
"name": "title",
"type": "Symbol",
"localized": false,
"required": false,
"validations": [],
"disabled": false,
"omitted": false
},
{
"id": "image",
"name": "image",
"type": "Link",
"localized": false,
"required": false,
"validations": [],
"disabled": false,
"omitted": false,
"linkType": "Asset"
},
{
"id": "description",
"name": "Description",
"type": "Text",
"localized": false,
"required": false,
"validations": [],
"disabled": false,
"omitted": false
}
],
"sys": {
"space": {
"sys": {
"type": "Link",
"linkType": "Space",
"id": "if4se75018sp"
}
},
"id": "easysite",
"type": "ContentType",
"createdAt": "2020-10-01T15:28:51.896Z",
"updatedAt": "2020-10-01T15:28:52.158Z",
"environment": {
"sys": {
"id": "master",
"type": "Link",
"linkType": "Environment"
}
},
"publishedVersion": 1,
"publishedAt": "2020-10-01T15:28:52.158Z",
"firstPublishedAt": "2020-10-01T15:28:52.158Z",
"createdBy": {
"sys": {
"type": "Link",
"linkType": "User",
"id": "0EGNAqGfBgN849uaItzT7r"
}
},
"updatedBy": {
"sys": {
"type": "Link",
"linkType": "User",
"id": "0EGNAqGfBgN849uaItzT7r"
}
},
"publishedCounter": 1,
"version": 2,
"publishedBy": {
"sys": {
"type": "Link",
"linkType": "User",
"id": "0EGNAqGfBgN849uaItzT7r"
}
}
}
}
Step 2 Create content
This step only needs to create an entry that is almost the same as writing a post on dev.to
. You can use Markdown.
Content --> Add Entry --> easysite(In this case, I named entry model easysite
)
From the top, title, eye-catch image, and body.
One thing you should know is that if you publish the body, that doesn't mean you publish the eye-catch image.
You will need to publish them separately.
Step 3 Get the API-Key
You will need to generate API Key from Settings. Then get SpaceID
and Access Token
Step 4 Create React app
Operations on Contentful is over, from this step you just need to develop a react application.
- Create an app with
create-react-app
- Add a component for API-key. In this case, I hardcoded SpaceID and Access Token on the local environment, but if you want to publish this app, you should use environment variables on hosting services such as netlify.
- Install contentful by yarn/npm https://www.npmjs.com/package/contentful
- Use contentful to fetch data
- Parse data and display it
This sample uses
useEffect
to get data from Contentful and usecontent_type
to specify the target on Contentful (easysite).
- As you know, we should avoid using
any
lol
useEffect(() => {
fetchData();
// console.log(articles);
}, [articles]);
const fetchData = async() => {
try {
const resp = await client.getEntries ({content_type: 'easysite'});
setArticles(resp.items as any);
} catch (error) {
console.log(error);
}
}
Entry part
export const Post = ({ article }: IArticle) => {
// console.log(article);
const { title, image, description} =article.fields;
const postDescription = marked(description);
return (
<div className="post">
<h2 className="title">{title}</h2>
{image && <img className="featuredImage" src={image.fields.file.url} alt={title} title={title} /> }
<section dangerouslySetInnerHTML={{ __html: postDescription}} />
</div>
)
}