Create Timeline with Reactjs and Contentful
Tags: Contentful, Reactjs, Tutorial, Typescript

I saw the following article on dev.to and it very impressive to me.

Then, I found a good library, react-chrono .

Looks good and also it’s super easy to use.

First, I tried react-chrono with create-react app.

Step1 Set up a react app

$ yarn create react-app mytimeline --template typescript

$ cd mytimeline

$ yarn add react-chrono

Step2 Add sample data

data.ts

I use this data from react-chrono's sample

const items = [

{

title: "May 1940",

cardTitle: "Dunkirk",

cardSubtitle:

"Men of the British Expeditionary Force (BEF) wade out to a destroyer during the evacuation from Dunkirk.",

cardDetailedText: `On 10 May 1940, Hitler began his long-awaited offensive in the west by invading neutral Holland and Belgium and attacking northern France. Holland capitulated after only five days of fighting, and the Belgians surrendered on 28 May. With the success of the German ‘Blitzkrieg’, the British Expeditionary Force and French troops were in danger of being cut off and destroyed.`

},

{

title: "25 July 1940",

cardTitle: "The Battle of Britain",

cardSubtitle: `RAF Spitfire pilots scramble for their planes`,

cardDetailedText: `After France’s surrender in June 1940, Churchill told the British people, “Hitler knows that he will have to break us in this island or lose the war”. To mount a successful invasion, the Germans had to gain air superiority. The first phase of the battle began on 10 July with Luftwaffe attacks on shipping in the Channel.

The following month, RAF Fighter Command airfields and aircraft factories came under attack. Under the dynamic direction of Lord Beaverbrook, production of Spitfire and Hurricane fighters increased, and despite its losses in pilots and planes, the RAF was never as seriously weakened as the Germans supposed.`

}, {

title: "February 1945",

cardTitle: "The Big Three",

cardSubtitle: `Churchill, Roosevelt and Stalin sit for a group photograph during the Yalta conference`,

cardDetailedText: `Between June 1940 and June 1941, Britain stood alone against Hitler. But then, after the German invasion of Russia and the Japanese attack on Pearl Harbor, she gained two powerful allies.

For the next four years Churchill did his utmost to foster ‘The Grand Alliance’ against the Nazis. He even earned the grudging admiration of Nazi propaganda chief Dr Goebbels who said, “…I can feel only respect for this man, for whom no humiliation is too base and no trouble too great when the victory of the Allies is at stake”.

Churchill conferred with both Roosevelt and Stalin to hammer out strategy and to discuss postwar arrangements. The three men congregated for the first time at Tehran in November 1943. There, and again at their last meeting at Yalta, Churchill was conscious of the fact that Britain, exhausted by her war effort, was now very much the junior partner of the two emerging superpowers.`

}

]; export default items;

App.tsx

import React from "react"

import { Chrono } from "react-chrono";

import items from "./data"; const App = () => {

return (

<div style={{ width: "500px", height: "950px" }}>

<Chrono

items={items}

slideShow

mode="VERTICAL_ALTERNATING"

/>

</div>

)

}

Then start the react application and access to localhost:3000

$ yarn start

You will see the vertical timeline.

As you can see, we can create a timeline to edit items which is data.ts.

We will use Contentful to keep adding items without editing data.ts.

There are will be 4 steps.

Step 1 Create a model on Contentful

The first step is creating a model for items that need to follow data.ts format.

{

title: "May 1940",

cardTitle: "Dunkirk",

media: {

name: "dunkirk beach",

source: {

url: "http://someurl/image.jpg"

},

type: "IMAGE"

},

cardSubtitle:

"Men of the British Expeditionary Force (BEF) wade out to a destroyer during the evacuation from Dunkirk."

} {

title: string;

cardTitle: string;

media: {

name: string;

source: {

url: string;

},

type: "IMAGE";

},

cardSubtitle: string;

cardDetailedText: string;

}

On Contentful the data types are the following.

title: short text

cardTitle: short text

media: media (image)

title: short text

url(Contentful returns url when call the API)

Description: "IMAGE" (If you upload an image, the input should be "IMAGE") cardSubtitle: long text(Maybe short text can be fine)

cardDetailedText: long text

You can see Contentful data model ↓

https://www.contentful.com/developers/docs/concepts/data-model/

Images API

https://www.contentful.com/developers/docs/references/images-api/

The model should be like this. Actually, you can put anything as an item. But, I used the same name to make everything easy.

Step 2 Add content to the model

This step is quite easy because we just need to add new content to the model we created in the first step.

You can add anything you want to display on your timeline.

In this post, I used react-chrono 's sample data.

Step 3 Add API Key

Click Settings > API keys on Contentful

Click Add API Key and create a new key.

We just need 2 items, Space ID and Content Delivery API - access token

This step is the same as this

Step 4 Update react app

In this step first, we need to create client.ts to access Contentful API via contentful npm package. If you want to deploy your app to somewhere like Netilify. You must use the environment variables instead of hard-coding.

client.ts

import * as ctf from 'contentful'; export const client = ctf.createClient({

// space: process.env.REACT_APP_SPACE_ID,

// accessToken: process.env.REACT_APP_ACCESS_TOKEN,

space: 'your space id',

accessToken: 'your access token',

});

To use the above, of course, you need to install the package

$ yarn add contentful

The last thing we need to do is to update App.tsx which is very simple.

First, get data from Contentful with useEffect.

Next we need to format the data we get from the API since the model we created on Contentful is a little bit different from react-chrono’s data format. Finally, we need to pass the data to react-chrono.

Currently, I put everything in App.tsx, but I should create a component for timeline part and format to simplify App.tsx .

The revised version will be pushed soon.

import React, { useState, useEffect } from 'react';

import { Chrono } from 'react-chrono';

import { client } from './utils/client';

import { timeLine } from './Types';

import { formatData } from './utils/util'; const App = () => {

const [items, setItems] = useState<timeLine[]>([]); useEffect(() => {

fetchData();

}, []); const fetchData = async() => {

try {

const resp = await client.getEntries ({content_type: 'timeLine'});

const formattedData = formatData(resp);

const reversedData = formattedData.reverse();

// console.log(reversedData);

setItems(reversedData);

} catch (error) {

console.log(error);

}

} return (

<div className="App">

<div style={{ width: "100%", height: "850px" }}>

{

items.length > 0 ?

<Chrono

items={items}

mode="VERTICAL_ALTERNATING"

slideShow

slideItemDuration={4000}

theme={{primary: "#000", secondary: "#fff", cardBgColor: "#000", cardForeColor: "#fff" }}

scrollable={{ scrollbar: false }}

/>

:

<h3>no data</h3>

}

</div>

</div>

);

} export default App;

If everything works well, you will see something like this.

My repo is here