Tech viser
Building a Dynamic Blog with Contentful CMS and JavaScript

Karamjot Singh

September 13, 2024

Building a Dynamic Blog with Contentful CMS and JavaScript

Integrate Contentful with your website using JavaScript to dynamically fetch and render blog posts. Learn how to set up Contentful, create a client, handle rich text content, and display blog posts.

Introduction

In the modern web development landscape, integrating a Content Management System (CMS) with your website can greatly enhance content management and scalability. Contentful, a popular headless CMS, provides a flexible API that allows developers to manage content effortlessly. In this blog post, we’ll explore how to integrate Contentful with your website using JavaScript, enabling you to fetch and render blog posts dynamically.

We’ll walk through:

  1. Setting up Contentful.
  2. Fetching data using the Contentful JavaScript SDK.
  3. Handling rich text content.
  4. Displaying blog posts on your site.

Table of Contents

  1. Prerequisites
  2. Setting Up Contentful
  3. Creating the Contentful Client
  4. Fetching Blog Posts
  5. Fetching a Single Blog Post by Slug
  6. Handling Rich Text Content
  7. Rendering the Blog Posts
  8. Conclusion

Prerequisites

Before you start, ensure that you have the following:

Setting Up Contentful

1. Create a Contentful Account and Space

2. Define the Content Model

In your space, create a Content Type called Blog Post with the following fields:

Create an Author content type with a Name field.

3. Populate Content

4. Obtain API Credentials

Creating the Contentful Client

We’ll use the Contentful JavaScript SDK to interact with the Contentful API.

1. Install the Contentful SDK

npm install contentful 

2. Create a contentful.js Module

// contentful.js
import { createClient } from 'contentful';

const client = createClient({
  space: 'YOUR_SPACE_ID',
  accessToken: 'YOUR_ACCESS_TOKEN',
  environment: 'master', // or your specific environment ID
});

export default client;

Replace ‘YOUR_SPACE_ID’ and ‘YOUR_ACCESS_TOKEN’ with your actual credentials.

Fetching Blog Posts

1. Fetching Blog Posts

// contentful.js

// Fetch all blog posts
export async function getBlogPosts() {
  try {
    const entries = await client.getEntries({
      content_type: 'blogPost', // Use the Content Type ID
      order: '-fields.publishedDate', // Order posts by published date
      include: 10, // Include linked entries/assets up to 10 levels deep
    });
    return entries.items.map((item) => item.fields);
  } catch (error) {
    console.error('Error fetching blog posts:', error);
    return [];
  }
}

2. Usage Example

import { getBlogPosts } from './contentful.js';

(async () => {
  const blogPosts = await getBlogPosts();
  console.log(blogPosts);
})();

Fetching a Single Blog Post by Slug

1. Fetch Single Post Function

// contentful.js

// Fetch a single blog post by slug
export async function getSinglePost(slug) {
  try {
    const entries = await client.getEntries({
      content_type: 'blogPost',
      'fields.slug': slug,
      include: 10, // Include related entries/assets
    });

    if (entries.items.length > 0) {
      const entry = entries.items[0];
      return {
        entry: entry.fields,
        includes: entries.includes || {}, // Related assets/entries
      };
    }
    return null;
  } catch (error) {
    console.error(`Error fetching blog post with slug "${slug}":`, error);
    return null;
  }
}

2. Usage Example

import { getSinglePost } from './contentful.js';

(async () => {
  const slug = 'your-blog-post-slug';
  const post = await getSinglePost(slug);
  console.log(post);
})();

Handling Rich Text Content

1. Install the Rich Text Renderer

npm install @contentful/rich-text-html-renderer

2. Create a richTextOptions.js Module

import { BLOCKS, INLINES } from '@contentful/rich-text-types';
import { documentToHtmlString } from '@contentful/rich-text-html-renderer';

export default function richTextOptions(assets, entries) {
  const assetMap = new Map();
  assets?.forEach((asset) => {
    assetMap.set(asset.sys.id, asset);
  });

  const entryMap = new Map();
  entries?.forEach((entry) => {
    entryMap.set(entry.sys.id, entry);
  });

  return {
    renderNode: {
      [BLOCKS.EMBEDDED_ASSET]: (node) => {
        const asset = assetMap.get(node.data.target.sys.id);
        if (asset) {
          const { url, fileName } = asset.fields.file;
          const alt = asset.fields.description || asset.fields.title || fileName;
          return `<img src="https:${url}" alt="${alt}" />`;
        }
        return '';
      },
      [INLINES.EMBEDDED_ENTRY]: (node) => {
        const entry = entryMap.get(node.data.target.sys.id);
        if (entry && entry.sys.contentType.sys.id === 'blogPost') {
          return `<a href="/blog/${entry.fields.slug}">${entry.fields.title}</a>`;
        }
        return '';
      },
    },
  };
}

Rendering the Blog Posts

1. Rendering a Single Blog Post

import { getSinglePost } from './contentful.js';
import { renderRichTextContent } from './richTextOptions.js';

app.get('/blog/:slug', async (req, res) => {
  const { slug } = req.params;
  const data = await getSinglePost(slug);

  if (!data) {
    res.status(404).send('Blog post not found');
    return;
  }

  const { entry, includes } = data;
  const contentHtml = renderRichTextContent(entry.content, includes);

  res.render('blogPost', {
    title: entry.title,
    contentHtml,
    featuredImage: entry.featuredImage,
  });
});

Conclusion

Integrating Contentful with JavaScript enables dynamic content management, helping you build scalable and maintainable websites. Key steps include setting up the CMS, fetching content, and rendering it dynamically on your site.

← Back to Blog