Create Timeline with Reactjs and Contentful
Tags: react, typescript, contentful, cms
Published: 2020-12-21 14:56:21

I saw @mikenikles's article which was very impressive to me.

{% post mikenikles/why-i-turned-my-homepage-into-a-timeline-h9 %}


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

{% github prabhuignoto/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
```zsh
$ 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](https://codesandbox.io/s/keen-shannon-gtjwn?file=/src/data.js:0-15693)

```ts
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
```ts
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`
```zsh
$ 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.


```json
{
  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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zcvavi6yjijm9q42cl2f.png)


## 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

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/r1b889owqmmr5ey0wb90.png)

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

{% post kojikanao/build-a-simple-blog-with-contentful-and-react-5ce0 %}


## 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
```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
```zsh
$ 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.

```ts
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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1hykcuecd718q6vjkn3d.png)


My repo is here
{% github koji/MyTimeline-with-reactjs %}



<a href="https://www.buymeacoffee.com/koji"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kA3wM6t6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buymeacoffee.com/buttons/default-red.png" alt="Buy Me A Coffee" height="41" width="174" loading="lazy"></a>