Dynamic Webpage: Integrating Contentful CMS With Your Web App

Rufus GladnessRufus Gladness
7 min read

Introduction

Static web pages are becoming outdated. Users now expect personalized, up-to-date web content that respond to their needs and interests, as opposed to content that is frozen in time. Imagine a website where text, images, and data can be updated without touching a single line of code - this is the reality of modern dynamic web pages.

Power of Decoupling

One principle that is transforming how websites are built is separating content from code. This approach provides several major benefits:

  • Increased Agility: Content updates are faster as they no longer need developer intervention.

  • Enhanced Collaboration: Developers and content creators can work simultaneously, each focusing on their expertise.

  • Improved Scalability: As content grows, the website’s structure remains unaffected, allowing for easy expansion.

  • Greater Flexibility: Content can be effortlessly reused across different platforms and devices.

Headless CMS

Leading this revolution are headless Content Management Systems (CMS). These tools store and manage content separately from the presentation layer, delivering it via APIs. This setup allows developers to create dynamic web pages that fetch content in real-time, adapting to user preferences and behaviors.

Why This Matters

By separating content from code, we can create websites that are:

  • Responsive: Adapt not just to different screen sizes but to different user contexts.

  • Personalized: Tailor content based on user preferences and behavior.

  • Always Fresh: Update content in real-time without rebuilding the entire site.

  • Future-Proof: Ready to deliver content to new platforms and technologies as they emerge.

What You'll Learn

In this article, we'll explore how to leverage this powerful approach by integrating a headless CMS into a dynamic webpage. We'll use Contentful, a popular headless CMS, and React, a leading JavaScript library for building user interfaces. You'll learn how to:

  • Set up a Contentful project to manage your content.

  • Create a React application that connects to Contentful.

  • Fetch and display dynamic content in real-time.

Prerequisites

To follow along with this tutorial, you'll need:

  • Basic JavaScript skills

  • Familiarity with React (we'll use create-react-app)

What is Contentful CMS?

Contentful CMS is a cloud-based, headless content management system (CMS) that allows users to create content and integrate it into any application or platform through a robust API. Its flexibility makes it suitable for a vast range of businesses and projects.

1. Setting Up a Contentful Project

  1. Visit Contentful and Sign Up for an Account.

  2. Create a Space

    • A space in Contentful is a container for a project. For this tutorial, we will create a new space named "Dynamic Content" using the free plan.

  1. Create a Content Model

    • Once we've established our content space, the next crucial step is to define the structure of our content, referred to as the content model. We will navigate to the Content Model tab and create a new model named 'Featured Event'.

  1. Add Fields to the Content Model

    • Our content model will define elements for an event card, containing:

      • An image: name 'Event Image', type 'media asset'

      • A heading: name 'Event Heading', type 'text'

      • The event end date: name 'Event End Date', type 'Date'

  1. Add Content

    • After adding all our fields, we will navigate to the Content tab and click on "Add Entry". We then fill out the fields and publish the entry.

  1. Creating an API key

    • To fetch this data through an API and send it to our app, we need to create an API key. This API key provides us with a space ID and an access token. To create an API key, navigate to Settings > API Keys > Add API Key.

2. Creating a React App

  1. We will start by creating an empty folder named dynamic-card-with-contentful. Inside it, we will initialize a new React app using npx create-react-app

     npx create-react-app dynamic-card-with-contentful
    
  2. The React app comes with default files as a template. We will edit the App.js file to create our frontend structure. Before doing that, let's outline the essential elements of our event card:

  • Image: A visual representation of the event.

  • Heading: The event's title.

  • End Date: The date when the event concludes.

      import './App.css';
    
      function EventCard() {
        return (
          <div className="event-card">
            <h1 className='section-title'>Featured Event</h1>
            <img src='' alt=''  />
            <h2 className='event-heading'></h2>
            <p className='end-date'> Ends: </p>
          </div>
        );
      }
       default EventCard;
    

    Explanation:

    We define a functional component EventCard that renders our structure: a section title, image placeholder, event heading, and end date. We keep the import ./App.css at the top as we will need it shortly.

    When we run our app with npm start and open the URL localhost:3000 in our browser, we should see our basic event card structure.

  1. Next, let's add some styling to our UI by modifying the App.css file:

     .container {
       display: flex;
       justify-content: center;
       align-items: center;
       height: 100vh;
       width: 100%;
     }
     .event-card {
       display: flex;
       align-content: center;
       justify-content: center;
       height: 400px;
       width: 400px;
       border: 4px solid rgb(100, 9, 9);
       background-color: rgb(230, 229, 229);
       border-radius: 1rem;
       box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
       transition: box-shadow 300ms ease-in-out;
    
       &:hover {
         box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.2);
       }
     }
     h1  {
       font-size: 2.25rem;
       line-height: 2.5rem;
       font-weight: 800;
       color: transparent;
       background-image: linear-gradient(to right, #1e40af, #9a3412);
       background-clip: text;
       position: absolute;
       top: 4px;
     }
     .event-image {
       height: 230px;
       width: 100%;
       object-fit: cover;
     }
     .event-heading {
       font-size: 1.5rem; 
       line-height: 1.75rem;
       position: absolute;
       top: 346px;
       color: #1e40af; 
       font-weight: 700;
     }
     .event-end-date {
       position: absolute;
       top: 67%;
       padding: 8px;
       border-radius: 0.9rem;
       background: linear-gradient(to right, #f06595, #f9a825);
       color: transparent;
       background-clip: text;
       font-weight: 800;
       font-size: 1.25rem;
     }
    

Output:

Our event card is now styled but still empty. Let's fill it with our Contentful data.

3. Fetching and Displaying Contentful Data

To fetch our data from Contentful, we will add fetch logic inside our EventCard component.

First, we will install the Contentful library:

npm install contentful

Next, we will create a .env file in the root of our project and add our Contentful space ID and access token:

REACT_APP_CONTENTFUL_SPACE_ID=your_space_id
REACT_APP_CONTENTFUL_ACCESS_TOKEN=your_access_token

We will update the App.js file as follows:

import './App.css';
import React, { useState, useEffect } from 'react';
import { createClient } from 'contentful';

const client = createClient({
  space: process.env.REACT_APP_CONTENTUL_SPACE_ID,
  accessToken: process.env.REACT_APP_CONTENTFUL_ACCESS_TOKEN,
});

function EventCard() {
  const [event, setEvent] = useState(null);

  useEffect(() => {
    const fetchContentfulData = async () => {
      try {
        const response = await client.getEntries({
          content_type: 'featuredEvent'
        });
        setEvent(response.items[0]?.fields);
      } catch (error) {
        console.error('Error fetching events:', error);
      }
    };
    fetchContentfulData();
  }, []);

  if (!event) return null;

  const { eventImage, eventHeading, eventEndDate } = event;
  const formattedDate = formatDate(eventEndDate);

  return (
    <div className='container'>
      <h1 className='section-title'>Featured Event</h1>
      <div className="event-card">
        <img className='event-image' src={eventImage?.fields?.file?.url} alt={eventHeading} />
        <h2 className='event-heading'>{eventHeading}</h2>
        <p className='event-end-date'> Ends: {formattedDate}</p>
      </div>
    </div>
  );
}

  // Helper function to format date
  function formatDate(dateString) {
    const [datePart] = dateString.split('T');
    const [year, month, day] = datePart.split('-');
    const date = new Date(year, month - 1, day);
    return new Intl.DateTimeFormat('en-US', {
      year: 'numeric',
      month: 'long',
      day: 'numeric'
    }).format(date);
  }

export default EventCard;

Code Explanation:

  1. We begin by importing necessary modules: React, useState, useEffect, and createClient from the contentful library.

     import React, { useState, useEffect } from 'react';
     import { createClient } from 'contentful';
    
  2. We establish a connection to Contentful using createClient with our space ID and access token.

     const client = createClient({
       space: process.env.REACT_APP_CONTENTUL_SPACE_ID,
       accessToken: process.env.REACT_APP_CONTENTFUL_ACCESS_TOKEN,
     });
    
  3. We create a state variable event and a setter function setEvent, and employ the useState hook to manage the event data.

     const [event, setEvent] = useState(null);
    
  4. We use the useEffect hook, containing an asynchronous function fetchContentfulData, to request data from Contentful and set event state with the fetched data if the data is not null.

      useEffect(() => {
         const fetchContentfulData = async () => {
           try {
             const response = await client.getEntries({
               content_type: 'featuredEvent'
             });
             setEvent(response.items[0]?.fields);
           } catch (error) {
             console.error('Error fetching events:', error);
           }
         };
         fetchContentfulData();
       }, []);
    
     if (!event) return null;
    
  5. We destructure the required fields from the event object and format the end date for better readability with a helper function formatDate.

     const { eventImage, eventHeading, eventEndDate } = event;
     const formattedDate = formatDate(eventEndDate);
    
  6. We update our JSX structure with the required fields.

     return (
         <div className='container'>
           <h1 className='section-title'>Featured Event</h1>
           <div className="event-card">
             <img className='event-image' src={eventImage?.fields?.file?.url} alt={eventHeading} />
             <h2 className='event-heading'>{eventHeading}</h2>
             <p className='event-end-date'> Ends: {formattedDate}</p>
           </div>
         </div>
       );
    
  7. Helper function to format date

       // Helper function to format date
       function formatDate(dateString) {
         const [datePart] = dateString.split('T');
         const [year, month, day] = datePart.split('-');
         const date = new Date(year, month - 1, day);
         return new Intl.DateTimeFormat('en-US', {
           year: 'numeric',
           month: 'long',
           day: 'numeric'
         }).format(date);
       }
    

Final Output:

Conclusion

Dynamic content has become a necessity in today's digital landscape. With Contentful CMS, you can create flexible content and seamlessly integrate it into your application. Contentful provides a user-friendly interface and the ability to manage diverse content types.

By embracing the power of dynamic content with Contentful CMS, you can keep your website fresh, personalized, and responsive to user needs, unlocking new possibilities for engagement and growth.

0
Subscribe to my newsletter

Read articles from Rufus Gladness directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rufus Gladness
Rufus Gladness

As a backend engineer and blockchain developer, I constantly crave new coding adventures. Exploring everything from HTML to web3 magic, I've delved into the realm of web development, developing a fervor for decentralized technologies. Come along on this exciting journey as we transform the digital landscape together!"