
Managing Content on Small Websites: Guide for 2025 & Beyond
22 Nov 2022
Are you frustrated with slow, outdated CMSs that make development a headache? You’re not alone. Many developers and businesses waste hours wrestling with WordPress limitations, fixing security issues, and making compromises just to get things working.
But what if you could have speed, flexibility, and control without the hassle? That’s where NextJS and BCMS come in.
Next.js is a powerful React framework that helps developers build high-performance web applications. Its features include Server-Side Rendering (SSR), Static Site Generation (SSG), and automatic code splitting. These enable websites to load faster, rank better on search engines, and deliver dynamic content efficiently.
On the other hand, BCMS is a headless CMS, which means it separates content storage from the front end. Unlike traditional CMSs, a headless CMS provides content through APIs, making it accessible to multiple platforms (web, mobile, etc.). This gives developers the freedom to create custom user interfaces while allowing content managers to update content seamlessly.
A headless CMS decouples the backend from the front-end presentation. Instead of being tied to a specific website design or framework, it delivers content via APIs. This allows developers to build highly customized and flexible frontends using any technology, such as Next.js, React, Vue, or mobile frameworks.
By using a headless CMS for NextJS, businesses can:
Improve performance: A headless CMS delivers content via APIs, allowing developers to use SSG and CDNs for faster load times. Unlike traditional CMSs, which fetch data on every request, a headless CMS enables pre-cached content delivery, improving speed, user experience, and SEO.
Enhance security: Since the backend operates independently, the CMS isn’t exposed to public traffic, reducing risks like SQL injections, brute-force attacks, and plugin vulnerabilities. Content is accessed via secure APIs, making the system more resilient against cyber threats.
Increase flexibility for scalable and custom digital experiences: Developers can use Next.js, React, Vue, or mobile frameworks without being tied to a specific CMS front-end. This allows custom UI design, seamless third-party integrations, and effortless scaling, making it easier to expand and adapt.
In modern web development, speed, SEO, and scalability are important for delivering a great user experience. While React is a powerful front-end library, it lacks built-in features for server-side rendering, static generation, and backend API handling. This is where Next.js comes in.
Next.js enhances React by providing:
Server-Side Rendering (SSR): Generates pages on demand for dynamic content and better SEO.
Static Site Generation (SSG): Pre-builds pages for lightning-fast performance.
API Routes: Allows backend functionality without a separate server.
Before I dive into building a blog with BCMS, here’s what you’ll need:
A basic understanding of React, React components, hooks, and Typescript.
Node.js and npm installed.
A BCMS Account (Free Tier or Trial). Sign up at BCMS to manage content through its headless CMS API.
Getting started with NextJS and BCMS is a straightforward process. The NextJS project creation tool can be used to create a sample blog project to help you get started. To create a new BCMS and NextJS project, execute the following command from your terminal:
npx @thebcms/cli create next fruit-blog
Running this command will set up a Next.js project, create the blog starter, and link it to a BCMS instance.
You’ll need to log in through your browser. If you don’t have a BCMS account yet, you can create one by following the prompts.
Once you’re in, the command will ask for a project name and then complete the setup, as shown in the image below:
Next, I’d move into the
project folder, install dependencies, and start the development server:
cd fruit-blog
npm install
npm run dev
Let’s jump into BCMS, open the dashboard, and first check that the project I created has been selected.
Next, I’ll remove all the existing templates and entries from the blog starter to make room for our own.
To do this, go to Templates, which is located on the sidebar, and then select any existing templates.
On the top right corner of the page, click Edit Template, then delete the template. Repeat this step until all Templates are deleted.
Let’s create a new template now. Click on the Create new template and click the Create button.
The blog template is going to contain keys like:
title → String
slug → String
createdAt → Date
description → Rich Text
author → String
tags → String Array
fruitImage → Media
After creating a template, I can start adding actual blog content by creating a new blog entry in BCMS.
Since I created a template Fruit_blog
, it now appears in the Entries section, allowing me to add content based on that template.
To create a new entry, click on Create new entry at the top right of the screen, fill out the available form fields, and click the Create button.
Here’s the list of entries I’ve created so far:
Before fetching data from BCMS, I need to configure our environment variables. To do this, go to the Settings section on the sidebar and click on API keys.
I’d be creating a new API key. Click the Add new key button, enter any name of your choice, and click Add key to create. Once it’s created, your API key will include the following fields:
Also, check all the Template Permissions that allow you to perform CRUD operations using the API I just created.
Now that I’ve created and published entries in BCMS, the next step is to fetch this content into the Next.js application using BCMS APIs. This allows me to dynamically display our blog posts on the front end.
Let’s start by setting up the environmental variables in the NextJs project.
Head over to the .env file
located at /thebcms_projects/fruit-blog/.env
and match the API key into their respective variables:
BCMS_ORG_ID=***
BCMS_INSTANCE_ID=***
BCMS_API_KEY_ID=***
BCMS_API_KEY_SECRET=***
To retrieve BCMS data in the Next.js application, open the src/app/page.tsx
file and import the auto-generated types provided by BCMS.
At the top of the file, I’ll import the necessary modules:
import React from 'react'; import { bcms } from './bcms-client'; import { Metadata } from 'next'; import BlogCard from '@/components/blog/Card'; import { FruitBlogEntry } from '@bcms-types/types/ts'; import { notFound } from 'next/navigation';
Next, I’d define a pageTitle
and use Next.js metadata API to improve search engine visibility:
const pageTitle = 'Fruit Blog'; export const metadata: Metadata = { title: pageTitle, openGraph: { title: pageTitle, }, twitter: { title: pageTitle, }, };
In BCMS integration, I retrieve all "fruit-blog" entries using bcms.entry.getAll()
. It applies TypeScript type assertion (FruitBlogEntry[]
) to ensure the data aligns with the CMS content model.
const HomePage: React.FC = async () => { const fruit_blogs = (await bcms.entry.getAll( 'fruit-blog', )) as FruitBlogEntry[]; if (!fruit_blogs) return notFound();
Next, let’s style page.tsx
and improve the user interface.
import React from 'react'; import { bcms } from './bcms-client'; import { Metadata } from 'next'; import BlogCard from '@/components/blog/Card'; import { FruitBlogEntry } from '@bcms-types/types/ts'; import { notFound } from 'next/navigation'; const pageTitle = 'Fruit Blog'; export const metadata: Metadata = { title: pageTitle, openGraph: { title: pageTitle, }, twitter: { title: pageTitle, }, }; const HomePage: React.FC = async () => { const fruit_blogs = (await bcms.entry.getAll( 'fruit-blog', )) as FruitBlogEntry[]; if (!fruit_blogs) return notFound(); const items = fruit_blogs.map((blog) => { return blog.meta.en; }); return ( <div className="py-24 md:py-32"> <div className="container"> <div className="flex flex-col gap-6 items-center text-center mb-20 md:mb-[120px]"> <h2 className="text-5xl font-bold"> Let's talk about fruits </h2> </div> <div> <div className="grid grid-cols-4 gap-12 max-w-[1040px] mx-auto"> {items.map((item, index) => { return <BlogCard key={index} blog={item!} />; })} </div> </div> </div> </div> ); }; export default HomePage;
Here’s the BlogCard.tsx
component, which I’ve rendered:
import React from 'react'; import Link from 'next/link'; import { BCMSImage } from '@thebcms/components-react'; import { bcms } from '@/app/bcms-client'; import { FruitBlogEntryMetaItem } from '@bcms-types/types/ts/entry/fruit-blog'; interface Props { blog: FruitBlogEntryMetaItem; } const BlogCard = ({ blog }: Props) => { const fruit_tags = blog.tags?.map((tag, index) => { return ( <div className="bg-black text-white text-xs rounded-md py-2 px-4 flex justify-center" key={index} > {tag} </div> ); }); return ( <div className="border rounded-lg"> <Link className="w-full" href={`/blog/${blog.slug}`}> <div> <div className=" w-[200px] h-[200px]"> <BCMSImage clientConfig={bcms.getConfig()} media={blog.fruitimage} className="size-full object-cover object-center" /> </div> <div className="p-2"> <div className="flex items-center gap-2 flex-wrap"> {fruit_tags} </div> <div className="flex gap-2 mt-2"> <p>By</p> <h4 className="font-semibold">{blog.author}</h4> </div> <div className="mt-2"> <span className="line-clamp-3" dangerouslySetInnerHTML={{ __html: blog.description.nodes[0].value, }} /> </div> </div> </div> </Link> </div> ); }; export default BlogCard;
Here’s a preview of what our user interface looks like, Next js blog example:
I’ve finally created our homepage. Let’s now create a post for each individual blog post.
import React from 'react'; import Link from 'next/link'; import { bcms } from '@/app/bcms-client'; import { notFound } from 'next/navigation'; import { FruitBlogEntry } from '@bcms-types/types/ts'; import { BCMSImage } from '@thebcms/components-react/image'; import { ArrowLeft } from 'lucide-react'; import { toReadableDate } from '@/utils/date'; type Props = { params: { slug: string; }; }; export async function generateStaticParams() { const blogs = (await bcms.entry.getAll('fruit-blog')) as FruitBlogEntry[]; return blogs.map((blog) => ({ slug: blog.meta.en?.slug, })); } const BlogPage: React.FC<Props> = async ({ params }) => { const blogs = (await bcms.entry.getAll('fruit-blog')) as FruitBlogEntry[]; const blog = blogs.find((e) => e.meta.en?.slug === params.slug); if (!blog) { return notFound(); } return ( <div className="py-15 md:py-20"> <div className="container"> <Link href="/" className="border border-appGray-200 bg-appGray-100 flex w-fit leading-none px-3 py-2 text-xl font-medium rounded-lg transition-colors duration-300 hover:bg-appGray-200 focus-visible:bg-appGray-200 mb-10 md:mb-10 md:px-5 md:py-4 md:text-2xl" > <ArrowLeft /> </Link> <div className="flex justify-center"> <div className="aspect-[1.25] w-[400px] h-[400px]"> <BCMSImage clientConfig={bcms.getConfig()} media={blog.meta.en?.fruitimage!} className="size-full object-cover transition-transform duration-500 object-center group-hover:scale-105 group-focus-visible:scale-105" /> </div> </div> <div> <div className="mb-5 md:mb-5"> <h1 className="text-3xl font-semibold leading-none md:text-[40px]"> {blog.meta.en?.title} </h1> </div> </div> <div className="my-3 flex justify-between"> <p> Created by{' '} <span className="font-bold"> {blog.meta.en?.author}{' '} </span> </p> <p>{toReadableDate(blog.meta.en?.createdat!)}</p> </div> <div> <span dangerouslySetInnerHTML={{ __html: blog.meta.en!.description.nodes[0].value.toString(), }} /> </div> </div> </div> ); }; export default BlogPage;
The snippet generates static parameters for each “fruit-blog” entry, giving each post a unique route.
It then fetches the matching post by slug and neatly displays the image, title, author, and date.
For RTF, the content is inserted directly as HTML.
In this article, I explored how to integrate BCMS with a Next.js application, from setting up a project and managing content to fetching and displaying data dynamically. By leveraging BCMS’s API, I created a structured and scalable blog website while keeping content management flexible.
With this foundation, you can now customize and expand your project, whether by adding more content types, enhancing styling, or implementing advanced features.
The combination of Next.js and BCMS offers a powerful way to build modern, content-driven web applications with ease. To learn more about Next.js and BCMS, you can visit the Next js blog starter toolkit and view or contribute to our fruit-blog project.
Happy Coding! 🚀
Get all the latest BCMS updates, news and events.
By submitting this form you consent to us emailing you occasionally about our products and services. You can unsubscribe from emails at any time, and we will never pass your email to third parties.
There are many actionable insights in this blog post. Learn more: