Building SPA using React and Contentful
Hello, Colleagues. My blog about web-programming starts today. I hope it would be useful and informative for you. So, let’s start.
Contentful is how modern services work with content. For me as a developer Contentful is the best CMS solution for Single Page Applications. I would like to tell how content infrastructure lifts the limits of a traditional CMS and why you should use it in your projects in pair with React.
Main idea of Contentful
Contentful is CMS solution, that allows us to avoid using SQL database, connected to a website and hosted at some terrible server. Its admin dashboard is userfriendly and great API is much faster than other CMS solutions like Wordpress.
Our goal is to split application content into different models and then create content based on those models. With the help of Contentful client we can fetch content as JSON format and then render it as a React.Component. So don’t lose time and let’s practice!
Contentful dashboard
First of all you should create account at Contentful. There is a test project for you from the beginning. But we are going to create our own Content models.
In the image above I’ve created a new Content model Article with field’s types Short text, Rich text and Media.
For now Contentful Supports such types of fields:
- Short text — maximum 255 characters. Use it for titles, names, tags, URLs, e-mail addresses
- Rich text — maximum 50k characters. Use for descriptions, text paragraphs, articles. It’s editable with markup. For example you can use bold or italic fonts
- Number — just Integer or Decimal number
- Date — date field with different time formats (date only, with timezone or without timezone) and time mode (AP/PM, 24 hours)
- Location — location coordinates
- Media — field that supports almost all types of files (images, videos, PDF, docx, code, archives, etc.)
- Boolean — consists of true/false value and its label
- JSON object — any data in JSON format
- Reference — link to another Content model
These fields cover the needs of developers in SPA. After the model is done we’ve got a JSON format preview that will be fetched with Contentful client.
Next step is to fetch data from Contentful. For this task we should install Contentful client .
npm install --save contentful
Further step is to initialize client using access token and space id from setting.
// Contentful Client exampleimport { createClient } from "contentful";export default class ContentfulClient {
getClient = async () => {
const client = await createClient({
space: CONTENTFUL_SPACE,
accessToken: CONTENTFUL_TOKEN,
});
return client;
};
These data is private, so I don’t recommend to store it right in code (for example you can use .env file). Contentful provides awesome delivery API but in the example project I will fetch the data by content id. You can find content id directly on the page of these content at the section named Info.
// Add method to ContentfulClient class
// getEntry method fetches data by content IdgetEntry = async id => {
const client = await this.getClient();
const entry = await client.getEntry(id, { include: 10 });
return entry;
};
Now we are ready to get Contentful data to our Application but before we need to create new content based on Article content model.
A big plus of the Content is that we can store all media files right here. Just go to tab Media and upload your files.
We’ve received a response from API. That’s cool. But we have one problem with the Rich text field:
"body": {
"data": {},
"content": [
{
"data": {},
"content": [
{
"data": {},
"marks": [],
"value": "This content is using ",
"nodeType": "text"
},
{
"data": {},
"marks": [
{
"type": "bold"
}
],
"value": "Article",
"nodeType": "text"
},
{
"data": {},
"marks": [],
"value": " Content model.",
"nodeType": "text"
}
],
"nodeType": "paragraph"
},
{
"data": {},
"content": [
{
"data": {},
"content": [
{
"data": {},
"marks": [],
"value": "I was working ...",
"nodeType": "text"
}
],
"nodeType": "paragraph"
},
{
"data": {},
"content": [
{
"data": {},
"marks": [],
"value": "Sascha KonietzkeCo-Founder & CEO",
"nodeType": "text"
}
],
"nodeType": "paragraph"
}
],
"nodeType": "blockquote"
},
{
"data": {},
"content": [
{
"data": {},
"marks": [],
"value": "",
"nodeType": "text"
}
],
"nodeType": "paragraph"
}
],
"nodeType": "document"
}
My first question when I saw it was: “Hmmm, what are the opportunities to render this one…?”. And we have a solution here. rich-text-react-renderer is a library by Contentful developers which is used for rendering Rich text fields into React components.
npm install --save @contentful/rich-text-react-renderer
Let’s implement React component Article for all content based on model Article!
import React from 'react';
import PropTypes from 'prop-types';
import { BLOCKS, MARKS } from '@contentful/rich-text-types';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import './article.css';const Article = ({
document: {
fields: {
body,
background,
title
}
}
}) => {
const options = {
renderNode: {
[BLOCKS.PARAGRAPH]: (node, children) => children,
[BLOCKS.QUOTE]: (node, children) => (
<div className="quotation">{children}</div>
),
[MARKS.BOLD]: (node, children) => (
<span className="bold-title">{children}</span>
),
},
}; const article = documentToReactComponents(
body,
options,
); return (
<div
className="article-wrapper"
style={{ backgroundImage: `url(${background &&
background.fields.file.url})` }}
>
<div className="rich-text-content">
<h1>{title}</h1>
{article}
</div>
</div>
);
};export default Article;
Document object is that what we got from Contentful API. It store all fields that we’ve described at content model.
Now we’ve got a universal parser for the Article model. But if you add any other types of markup to Rich text field (for example underline), you will need to describe parsing rules for this one in documentToReactComponents options object.
That was a short course on how to use Contentful with React. The project code is on github. I hope that the information was useful and will be grateful for your support here on Medium and on github!
I will be glad to answer all your questions and comments.🤘