Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/jasonuc/nextjs-contentful-sample-blog

How to use NextJS + TypeScript + Contentful for your next blog
https://github.com/jasonuc/nextjs-contentful-sample-blog

blog education nextjs readme

Last synced: 5 days ago
JSON representation

How to use NextJS + TypeScript + Contentful for your next blog

Awesome Lists containing this project

README

        

# Next.js & Contentful Sample Blog

*A guide to building a blog using Contentful CMS with Next.js*

### Preview of the sample blog

Preview of Home Page
Preview of Blog Post


### First of all make a Next.JS, Typescript project

```zsh
npx create-next-app@latest nextjs-contentful-sample-blog --ts
```

Then install Contentful

```zsh
npm i contentful
```

Now that Contentful library is installed, make a new file `lib/create-client.ts`, now we set up the connection with **Contentful**, in order to retrieve data from our blog.

```ts
import { ContentfulClientApi, createClient } from "contentful";

export const client: ContentfulClientApi = createClient({
space: process.env.CONTENTFUL_SPACE_ID!,
accessToken: process.env.CONTENTFUL_CONTENT_DELIVERY_ACCESS_TOKEN!,
});
```

Remember to paste the API Keys in your `.env.local` file. To retreive them open **Contentful** and open `settings > API keys > Add API key`. Here you can copy the **SPACE ID** and the **Content Delivery API - access token**. Paste these in your `.env.local` file.
```
CONTENTFUL_SPACE_ID=********
CONTENTFUL_CONTENT_DELIVERY_ACCESS_TOKEN=***************
```
Now the **API keys** can be accessed when prefixed with `process.env`

Now open the `/app/page.tsx` and add this (you can design it any how you want this is just my version)
```tsx
export default async function Home() {

return (

Next.JS + Contentful Sample Blog


Blog posts go here




);
}
```

![Step 1 Image](public/Step-1.jpeg)

Now before we can access the blog posts you have to fetch the blog entries from **Contentful**.

Create a new file `/lib/get-blog-entries.ts`
```ts
import { client } from "@/lib/create-client";
import { EntryFieldTypes } from "contentful";

export interface BlogPostInterface {
contentTypeId: "blogPost";
fields: {
title: EntryFieldTypes.Text
subtitle: EntryFieldTypes.Text
slug: EntryFieldTypes.Text
publishDate: EntryFieldTypes.Text
readTime: EntryFieldTypes.Number
postContent: EntryFieldTypes.RichText
}
}

export const getBlogEntries = async () => {
const entries = await client.getEntries({ content_type: "blogPost" });
return entries;
}
```

1. **Import Statements**:
```ts
import { client } from "@/lib/create-client";
import { EntryFieldTypes } from "contentful";
```

- The first import statement imports the `client` object from the `create-client.ts` file located in the `lib` directory. This `client` object is responsible for interacting with the **Contentful API**.
- The second import statement imports `EntryFieldTypes` from the `contentful` package. This is used to define the types of fields in Contentful entries.

2. **Interface Definition**:
```ts
interface BlogPostInterface {
contentTypeId: "blogPost";
fields: {
title: EntryFieldTypes.Text
subtitle: EntryFieldTypes.Text
slug: EntryFieldTypes.Text
publishDate: EntryFieldTypes.Text
readTime: EntryFieldTypes.Number
postContent: EntryFieldTypes.RichText
}
}
```

- This defines an interface named `BlogPostInterface` to represent the structure of a blog post entry fetched from Contentful.
- It includes the following fields:
- `contentTypeId`: Represents the type ID of the Contentful entry, which should be `"blogPost"`.
- `fields`: An object containing the fields of the blog post entry, where each field is typed according to its Contentful field type.
- We make use of `EntryFieldTypes` from **Contentful** so that we are able to access the types provided my **Contentful** like `EntryFieldTypes.RichText` which would be the type for the Blog Post content (written in markdown). The other types are self-explanatory :)

3. **Function Definition**:
```ts
export const getBlogEntries = async () => {
const entries = await client.getEntries({ content_type: "blogPost" });
return entries;
}
```

- This exports a function named `getBlogEntries` responsible for fetching blog post entries from Contentful.
- It is an asynchronous function (using `async` keyword) because fetching data from Contentful is an asynchronous operation.
- Inside the function:
- It uses the `client` object to call `getEntries`, which fetches entries from Contentful.
- `` specifies the type of entries that should be returned, ensuring type safety.
- `{ content_type: "blogPost" }` specifies the content type of the entries to be fetched, which should be `"blogPost"`.
- The function returns the fetched entries.

Overall, this code sets up a function to fetch blog post entries from Contentful and ensures type safety by defining an interface to represent the structure of these entries.

Now the blog posts can be fetched from **Contentful**. Open `app/page.tsx`
```tsx
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; // These components are from Shadcn-UI
import { BlogPostInterface, getBlogEntries } from "@/lib/get-blog-entries";
import { Entry } from "contentful";
import Link from "next/link";

export default async function Home() {

const blogEntries = await getBlogEntries();
const posts: Entry[] = blogEntries.items

return (

Next.JS + Contentful Sample Blog

{posts.map((post) => (



{post.fields.title}
{post.fields.subtitle}


Read time: {post.fields.readTime}


Publish date: {post.fields.publishDate}





))}



);
}
```
Now you should have something that looks like this:
![Preview of Home Page](public/Preview-Home.jpeg)
I am making use of Shadcn-UI for the *blog* to look mordern and i can spend less time on *styling*