A Step-by-Step Guide to Creating Your First Blog with ElmapiCMS and Next.js
In this tutorial, you'll learn how to build a simple blog using ElmapiCMS and Next.js. We'll walk through setting up your ElmapiCMS instance, creating a blog collection, connecting it to a Next.js application, and displaying your content. By the end, you'll have a fully functional blog that you can customize and extend.
Table of Contents
- What You'll Build
- Prerequisites
- Step 1: Set Up ElmapiCMS
- Step 2: Create a Blog Collection
- Step 3: Add Sample Content
- Step 4: Create a Next.js Project
- Step 5: Install the ElmapiCMS SDK
- Step 6: Configure Environment Variables
- Step 7: Create the API Client
- Step 8: Build the Blog List Page
- Step 9: Create the Blog Post Page
- Step 10: Add Styling
- Testing Your Blog
- Next Steps
- Troubleshooting
- Conclusion
What You'll Build
By the end of this tutorial, you'll have:
- A Next.js blog application with a list page showing all posts
- Individual blog post pages with full content
- Integration with ElmapiCMS for content management
- A clean, responsive design
- A foundation you can extend with features like pagination, categories, or search
Prerequisites
Before you begin, make sure you have:
- Node.js 18 or higher installed
- An ElmapiCMS instance running (local or remote)
- Basic knowledge of React and Next.js
- A code editor like VS Code or Cursor
If you don't have ElmapiCMS installed yet, check out our installation guide to get started.
Step 1: Set Up ElmapiCMS
First, you need to have ElmapiCMS running. If you haven't installed it yet, follow the installation docs for your environment (CodeCanyon or GitHub, then your OS).
Once ElmapiCMS is running:
- Login to your ElmapiCMS instance.
- Create a new project by clicking "Create Project"
- Give it a name like "My Blog" and click "Create Project"
You'll be taken to your project dashboard where you can start creating collections.
Step 2: Create a Blog Collection
Now let's create a collection to store your blog posts. ElmapiCMS has predefined collection templates. We will use the "Blog Post" template.
- In your ElmapiCMS project, click "+ Add New" button in the "Collections" sidebar.
- Name it "Posts" (this will be the collection identifier)
- Chose "Blog Post" as the template
- Click "Create Collection"
This will create a new collection with 3 fields: title, slug and content. You can see the fields list in the collection settings by clicking the setting icon next to the collection name. You can add, edit or delete fields here.
Step 3: Add Sample Content
Let's add a few sample blog posts to test with:
- Click colleciton name in the collection list.
- Click "+ Create New"
- Fill in the fields:
- Title: "Welcome to My Blog"
- Slug: "welcome-to-my-blog" (auto-generated from title)
- Content: "This is my first blog post using ElmapiCMS and Next.js!"
- Click "Save & Publish"
Repeat this process to add 2-3 more sample posts. This will give us content to display in our Next.js application.
Tip: You can use AI to generate content for your blog posts. See our tutorial on how to generate content with AI using ElmapiCMS Import/Export feature for more details.
Step 4: Create a Next.js Project
Now let's create a new Next.js project. Open your terminal and run:
npx create-next-app@latest my-blog
When prompted, "Would you like to use the recommended Next.js defaults?" "Yes, use recommended defaults." will be selected by default. Press Enter to continue.
This will create a new Next.js project with the latest features.
Go to the project directory:
cd my-blog
Step 5: Install the ElmapiCMS SDK
Next, we need to install the ElmapiCMS JavaScript SDK to interact with our CMS:
npm install @elmapicms/js-sdk
The SDK provides a simple interface to fetch content from your ElmapiCMS instance.
Step 6: Configure Environment Variables
Create a .env.local file in your Next.js project root:
ELMAPI_PROJECT_ID=your-project-uuid-here
ELMAPI_API_KEY=your-api-key-here
ELMAPI_API_URL=http://localhost:8000/api
ELMAPI_IMAGE_HOST=localhost:8000
To find your Project ID:
- Go to your ElmapiCMS project
- Navigate to API Access
- Copy the Project ID
- Paste it into your
.env.localfile
To create your API key:
- Go to your ElmapiCMS project
- Navigate to API Access
- Click "Create Token"
- Copy the API key
- Paste it into your
.env.localfile
Tip: You can also enable "Public GET Access" in the same page. You don't need to create a token if you enable this.
If your ElmapiCMS instance is hosted remotely, update the ELMAPI_API_URL and ELMAPI_IMAGE_HOST accordingly.
Step 7: Create the API Client
Create a new file lib/elmapicms.ts to set up the ElmapiCMS client:
import { createClient } from '@elmapicms/js-sdk';
const apiUrl = process.env.ELMAPI_API_URL || 'http://localhost:8000/api';
const projectId = process.env.ELMAPI_PROJECT_ID || '';
const apiKey = process.env.ELMAPI_API_KEY || '';
if (!projectId) {
throw new Error('ELMAPI_PROJECT_ID environment variable is required');
}
export const client = createClient(apiUrl, apiKey, projectId);
export interface BlogPost {
uuid: string;
locale: string;
published_at: string | null;
fields: {
title: string;
slug: string;
content: string;
excerpt?: string;
published_date?: string;
};
}
export async function getAllPosts(): Promise<BlogPost[]> {
try {
const entries = await client.getEntries('posts');
return Array.isArray(entries) ? entries : [];
} catch (error) {
console.error('Error fetching posts:', error);
return [];
}
}
export async function getPostBySlug(slug: string): Promise<BlogPost | null> {
try {
const posts = await getAllPosts();
return posts.find((post) => post.fields.slug === slug) || null;
} catch (error) {
console.error('Error fetching post:', error);
return null;
}
}
This file exports:
- A configured ElmapiCMS client
- TypeScript interfaces for type safety
- Helper functions to fetch all posts and individual posts by slug
Step 8: Build the Blog List Page
Now let's create the main blog page that lists all posts. Update app/page.tsx:
import Link from 'next/link';
import { getAllPosts } from '@/lib/elmapicms';
export default async function Home() {
const posts = await getAllPosts();
return (
<div className="container mx-auto px-4 py-8">
<header className="mb-12">
<h1 className="text-4xl font-bold mb-4">My Blog</h1>
<p className="text-gray-600">Welcome to my blog powered by ElmapiCMS and Next.js</p>
</header>
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{posts.length === 0 ? (
<p className="text-gray-500">No posts found. Add some posts in your ElmapiCMS admin panel.</p>
) : (
posts.map((post) => (
<article key={post.uuid} className="border rounded-lg p-6 hover:shadow-lg transition-shadow">
<h2 className="text-2xl font-semibold mb-2">
<Link
href={`/blog/${post.fields.slug}`}
className="hover:text-blue-600"
>
{post.fields.title}
</Link>
</h2>
{post.fields.published_date && (
<time className="text-sm text-gray-500 block mb-3">
{new Date(post.fields.published_date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</time>
)}
{post.fields.excerpt && (
<p className="text-gray-700 mb-4">{post.fields.excerpt}</p>
)}
<Link
href={`/blog/${post.fields.slug}`}
className="text-blue-600 hover:underline"
>
Read more →
</Link>
</article>
))
)}
</div>
</div>
);
}
This page:
- Fetches all posts from ElmapiCMS
- Displays them in a responsive grid
- Shows the title, date, and excerpt for each post
- Links to individual post pages
Step 9: Create the Blog Post Page
Create a new directory and file app/blog/[slug]/page.tsx for individual blog posts:
import { notFound } from 'next/navigation';
import Link from 'next/link';
import { getPostBySlug, getAllPosts } from '@/lib/elmapicms';
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map((post) => ({
slug: post.fields.slug,
}));
}
export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await getPostBySlug(slug);
if (!post) {
notFound();
}
return (
<div className="container mx-auto px-4 py-8 max-w-4xl">
<Link
href="/"
className="text-blue-600 hover:underline mb-8 inline-block"
>
← Back to Blog
</Link>
<article className="prose prose-lg max-w-none">
<h1 className="text-4xl font-bold mb-4">{post.fields.title}</h1>
{post.fields.published_date && (
<time className="text-gray-500 block mb-8">
{new Date(post.fields.published_date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</time>
)}
<div
className="blog-content"
dangerouslySetInnerHTML={{ __html: post.fields.content }}
/>
</article>
</div>
);
}
This page:
- Fetches a single post by slug
- Uses
generateStaticParamsfor static generation - Displays the full post content
- Includes a back link to the blog list
Step 10: Add Styling
If you're using Tailwind CSS (which we set up earlier), the styling is already included in the code above. If you want to add custom styles for the blog content, you can add this to your globals.css:
.blog-content {
line-height: 1.8;
}
.blog-content h2 {
font-size: 2rem;
font-weight: bold;
margin-top: 2rem;
margin-bottom: 1rem;
}
.blog-content p {
margin-bottom: 1.5rem;
}
.blog-content ul,
.blog-content ol {
margin-left: 2rem;
margin-bottom: 1.5rem;
}
.blog-content code {
background-color: #f4f4f4;
padding: 0.2rem 0.4rem;
border-radius: 0.25rem;
font-size: 0.9em;
}
Testing Your Blog
Now let's test your blog:
- Make sure your ElmapiCMS instance is running
- Start your Next.js development server:
npm run dev - Open
http://localhost:3000in your browser - You should see your blog posts listed
- Click on a post to view the full content
If your ElmapiCMS instance is hosted locally you may encounter this error:
Error [NetworkError]: unable to verify the first certificate
To fix this you can run the following command:
NODE_TLS_REJECT_UNAUTHORIZED=0 npm run dev
This will allow you to access your blog locally without encountering the certificate error. This is a temporary solution and you should not use it in production.
Troubleshooting
If you encounter issues:
- Posts not showing: Check that your
ELMAPI_PROJECT_IDis correct and your ElmapiCMS instance is running - API errors: Verify your
ELMAPI_API_URLmatches your ElmapiCMS instance URL - Build errors: Make sure all environment variables are set in
.env.local - Type errors: Ensure you've installed the ElmapiCMS SDK and TypeScript is properly configured
For more help, visit the ElmapiCMS documentation or check out our getting started guide.
Conclusion
Congratulations! You've successfully built a simple blog using ElmapiCMS and Next.js. You now have:
- A headless CMS setup with ElmapiCMS
- A Next.js frontend that fetches and displays content
- A foundation you can extend with additional features
ElmapiCMS makes it easy to manage your content while Next.js provides a fast, modern frontend. This combination gives you the flexibility to scale your blog as it grows, add new features, and maintain full control over your content and code.
