#nextjs
Integrating Contentful (TypeScript)
This documentation provides a step-by-step guide on integrating Contentful with a Next.js 14 project using TypeScript and the App Router.
Install Dependencies
1npm install contentful @contentful/rich-text-react-renderer
Create Contentful Client
Create a contentful.ts file in the src/lib directory to configure the Contentful client:
1// src/lib/contentful.ts2import { createClient } from "contentful";34const client = createClient({5space: process.env.CONTENTFUL_SPACE_ID || "",6accessToken: process.env.CONTENTFUL_ACCESS_TOKEN || "",7});89export const getEntries = async () => {10const entries = await client.getEntries();11return entries.items;12};1314export const getEntryBySlug = async (slug: string) => {15const entries = await client.getEntries({16content_type: "posts",17"fields.slug": slug,18});19return entries.items[0];20};
Fetch Contentful Entries in a Blog Page
Create a blog.tsx file to fetch and display blog entries:
1import React from "react";2import Link from "next/link";3import { getEntries } from "@/lib/contentful";45type Post = {6sys: { id: string };7fields: {8title: string;9slug: string;10cover?: {11fields: {12file: {13url: string;14};15};16};17date: string;18};19};2021async function fetchPosts(): Promise<Post[]> {22const entries = await getEntries();23return entries24.map((entry: any) => ({25sys: entry.sys,26fields: entry.fields,27}))28.filter(29(entry: Post) =>30entry.fields &&31entry.fields.slug &&32entry.fields.title &&33entry.fields.date34);35}3637const Blog = async () => {38const posts = await fetchPosts();3940return (41<div>42<h1>Blog</h1>43<div>44{posts.map((post) => (45<Link href={`/blog/${post.fields.slug}`} key={post.sys.id}>46key={post.sys.id}47>48<div49style={{50backgroundImage: post.fields.cover51? `url(${post.fields.cover.fields.file.url})`52: "none",53backgroundSize: "cover",54}}55></div>56<div>57{post.fields.title}58<p>59{post.fields.date}60</p>61</div>62</Link>63))}64</div>65</div>66);67};6869export const revalidate = 10;7071export default Blog;
Create a Dynamic Route for Blog Posts
Create a [slug].tsx file in the src/app/blog directory to handle dynamic routing for individual blog posts:
1import { getEntryBySlug } from "@/lib/contentful";2import { documentToReactComponents } from "@contentful/rich-text-react-renderer";3import Links from "@/components/Nav/Links";4import Link from "next/link";5import React from "react";6import { IoArrowBackCircleOutline } from "react-icons/io5";7import { Metadata, ResolvingMetadata } from "next";89// Define the structure of the Contentful entry10type PostFields = {11title: string;12body: any;13cover: any;1415type Post = {16fields: PostFields;17};1819// Define the props for the component20type PostProps = {21post: Post;22};2324export async function generateMetadata(25{ params }: { params: { slug: string } },26parent: ResolvingMetadata27): Promise<Metadata> {28const entry = await getEntryBySlug(params.slug);29if (!entry || !entry.fields) {30return {31title: "Post not found",32};33}3435const previousImages = (await parent).openGraph?.images || [];3637return {38title: (entry.fields.title ?? "Default Title") as string,39};40}4142// Server component to fetch the data43const BlogPost: React.FC<{ params: { slug: string } }> = async ({ params }) => {44let post: Post | null = null;4546try {47// Fetch the data48const entry = await getEntryBySlug(params.slug);4950// Check if the data is valid and has the expected structure51if (entry && entry.fields) {52// If it does, assign it to the post variable53post = { fields: entry.fields as PostFields };54}55} catch (error) {56console.error("Error fetching blog post:", error);57// Handle error appropriately58// For now, let's log the error and continue rendering with post set to null59}6061// Check if post is null before rendering62if (!post) {63// Render loading state or error message64return <div>Loading...</div>;65}6667return (68<div69<h1>{post.fields.title}</h1>70<div71className=" h-52 w-full bg-slate-500 bg-center"72style={{73backgroundImage: `url(${post.fields.cover.fields.file.url})`,74backgroundSize: "cover",75}}76></div>77{documentToReactComponents(post.fields.body)}78</div>79);80};8182export default BlogPost;
Configure Environment Variables
Ensure you have the necessary environment variables in your .env.local file:
1CONTENTFUL_SPACE_ID=your_space_id2CONTENTFUL_ACCESS_TOKEN=your_access_token
©2024 Codeblockz
Privacy Policy