Managing Content on Small Websites: Guide for 2025 & Beyond
22 Nov 2022
This guide will show you how to build an agency website with the best CMS for Next.js. You’ll create a professional website for your clients in a few minutes with little coding experience.
Before getting hands-on, these are some of the key elements that make up a stellar agency website:
Hero Section: This can be a picture or video communicating your agency’s value and acting as a first impression.
Header: This is your navigation bar, providing easy access to different sections of your website like "Services," "Portfolio," "About Us," and "Contact."
Services: This section outlines your services, highlighting your expertise and how it benefits clients.
Portfolio: This section showcases your best work through compelling visuals and client testimonials.
Blog Page: This section your agency as a thought leader by sharing valuable insights and industry trends.
Contact Form: This section makes it easy for potential clients to reach you with a user-friendly contact form.
Footer: This includes essential information like contact details, copyright information, and social media links.
About Us Section: This section introduces your team, shares your agency's story, and builds trust with potential clients.
Pricing Section: If applicable, this section provides a transparent overview of your pricing structure.
Next.js utilizes static site generation (SSG) to prerender your website content, resulting in faster load times. It is also SEO-friendly, ensuring your website ranks well in search results. Additionally, you can build reusable components that ensure consistent design across your website.
However, using a user-friendly interface, BCMS headless CMS enables you to manage website content easily without touching code while updating text, images, and other content.
Learn more: Choosing the right CMS for Next.js Project: 14 Key Questions
Now, combining these two, allows you to focus on creating compelling content with BCMS while Next.js handles the technical aspects. For this tutorial, you’ll create an agency website similar to the BCMS Agency Website Starter.
The BCMS Nextjs Agency Website starter is a prebuilt solution with all the essential components for an agency website. This starter gives you a solid foundation to build your website. It gives you an idea of BCMS and how it works with Nextjs.
In this tutorial, you will learn how to build an Agency website with Nextjs and BCMS modeling this starter. You won’t be coding from scratch so you don’t have to be a developer expert to build your website. Instead, you’ll explore the starter, see how it was built, and understand why it was built that way.
By following this NextJs tutorial, you’ll learn how to build NextJS sites integrated with BCMS to add your desired functionalities. Do well to give the BCMS starters repo a star.
Now, you are ready to use BCMS and Nextjs to bring your agency website to life. The article will have the following structure:
Initializing BCMS
Populating BCMS with website data
Configuring NextJs to use BCMS
Fetching and Displaying Data on the Frontend
Deployment
There are two options for using BCMS: Live and Local.
BCMS Live is a hosted solution where BCMS takes care of server management and infrastructure. You can sign up for a BCMS Cloud account on the website.
While BCMS Local enables you to self-host BCMS on your own server. The documentation provides instructions for setting this up.
For this tutorial, you’ll be setting it up locally. To begin, install the BCMS CLI globally:
npm install @becomes/cms-cli -g
Verify your installation, by running this command:
bcms --help
If the command is recognized in your terminal, this means BCMS is successfully installed.
To create a BCMS project, run the following command:
bcms --cms create
Answer the prompts as required and wait for the setup to complete. On completion, the project doesn’t start by default. BCMS requires Docker to deploy the application and run it. See the official Docker documentation to install Docker if you don’t have it on your machine.
Going forward, navigate to your created BCMS project in your terminal and run:
docker-compose up
If the docker installation were successful, the server would be up and running. To verify, visit localhost:8080
on your browser. You’ll see a welcome screen that says You are now logged in. The application you just launched is your BCMS Instance.
BCMS data creation follows the same basic principles as other systems you might be familiar with, like custom backends or other CMS. For starters, you define a data model that defines the information you want to capture like specific fields, each having a designated data type (text, number, etc).
Now, on your BCMS dashboard, you’ll find an admin panel with the key elements:
Templates: These are your content blueprints. You define the fields needed for each section within a template.
Entries: These are individual pieces of content that follow the structure you defined in the template.
Groups: These are reusable collections of properties that can be incorporated into any template, widget, or even another group.
In simpler terms: Templates are like pre-designed content boxes, entries are the actual content that fills those boxes, and groups are like pre-made sets of elements that you can insert anywhere.
Combining these elements allows you to create and manage your content with BCMS using BCMS Widgets.
Now, you can enter the information you want for an agency site. You’ll identify the kind of information that should be stored in BCMS (backend) instead of being directly hard coded into the website’s front end. The sections of the agency website that likely need BCMS to store their data are:
Contact page
Footer
Header
Homepage
Legal page
Portfolio ( Item )
Portfolio page
Service
Service page
Team member
Team page
There are 11 sections, hence you’ll create 11 templates.
Starting with the contact page, the necessary properties are:
Title
Slug
SEO
Phone
Firstly, create a new group called SEO. This group will store essential information for search engines, in this case, the Title and Description:
Now create the Contact page template with the properties mentioned earlier. On creating a template, the Title and Slug features are added by default, so the features you’ll add are SEO, Email, and Phone.
The properties needed for the Footer are Title and Slug. These are created by default when you create the Footer template.
Along with the default properties, the Header template has a Nav, which is an array of navigation links.
Looking at the Starter’s Hompage above, the identified properties are:
Home Hero
Home About
Home Services
Home Capabilities
Team
Contact block
Home Hero: This group should contain Title and Gallery properties.
Home about: This group should contain a Title, Subtitle, Description, and Cover image for the About Us section on the home page.
Home services: Likewise, the Home services group has the Title, Subtitle, Description, and Cover image properties.
Home capabilities: The capabilities group has a Title, Subtitle, Description, and Portfolio items properties.
However, Portfolio items are linked to the Portfolio template. So first create the Portfolio template with Title, Slug, Subtitle, Description, Cover, Project Cover, and Url properties:
Then create the Home capabilities group:
Home team: In addition to the Title, Subtitle, Description, and Cover, there are Members titles, Members Descriptions, and Members properties in this group. Members property is linked to the Team member template. So create it with Role, Description, and Image properties in addition to the template defaults:
Then create the Home team group:
Contact block: This group has just the Title and Description properties.
Finally, you can create the Home page template since the necessary groups and templates have been built:
This template contains the template defaults in addition to the SEO and Blocks properties. The Blocks property is linked to the Legal block group. Therefore, create the Legal block group with a Title and Description property:
Then create the Legal page template:
This template should contain the default properties with Subtitle, Description, Cover, Project Cover, and Url properties.
The Portfolio page template has the SEO, Description, Items, and Contact block properties besides the default properties.
The Service template contains a Description, Cover, and Theme properties as well as the defaults.
The Services page template contains the SEO, Description, Services, and Contact block properties in addition to the defaults.
The Team member template contains the member’s Role, Description, and Image in addition to the defaults.
The Team page template should contain the SEO, Description, Team members, and contact block properties alongside the defaults.
Now you've successfully developed templates for your project. However, templates define the structure for your content, similar to a data model. But to populate that structure you need entries.
You’ll find the Entries tab with the admin panel (scroll down if needed). Here you can create entries for each template.
Also, if the provided Starter content meets your needs you can download the entire BCMS instance from GitHub, run it locally, and use the existing content directly or you can download the media files and content from the starter instance and import them into your own BCMS instance.
For the scope of this tutorial, you’ll be creating your content from scratch for just the Contact page, Header, and Footer.
First, create an entry named Contact page and fill it with content based on the template.
The entry should contain the title, SEO, Email, and Phone fields as defined in the Contact Page properties.
Then create an entry for the Header and Footer and place them in a collection/folder called LAYOUT by dragging one above the other. This collection is there just for better organization - it does not affect the CMS in any other way.
Now, you can connect your Next.js frontend to your BCMS backend to retrieve data. You’ll use the BCMS CLI to streamline this process.
To ensure secure communication between your Next.js app and BCMS, you’ll need an API key. This key grants your app access to specific BCMS data.
Navigate to the Key Manager tab within the BCMS admin panel
Click Add New Key to create a new key.
Give your key a descriptive name
Permissions: Since this tutorial focuses on data fetching, you only need the Can get resources permission for all the templates.
Now, navigate to the directory you want to create your Next.js project and use the following BCMS CLI command:
bcms --website create
This will prompt you with details like the framework which in this case is Next.js, the project name, BMCS instance (live or local), and the API key. Once you provide this information the BCMS CLI will set up your Next.js project for you.
If you encounter a Not Logged in Message during setup, in your terminal run:
bcms --login true
A login URL will open in your browser. Follow the steps to log in successfully. Then delete the project that initially created the login issue. Finally, rerun the BCMS CLI with the correct details to set up a new project.
Now that we have set up the NextJs project, let’s proceed to add the API key to it.
Within your project directory, locate bcms.config.js
and add the new API key to it. Also, the origin field should point to localhost:8080
. Remember to replace this with your production URL when deploying to your server.
const { createBcmsMostConfig } = require('@becomes/cms-most'); module.exports = createBcmsMostConfig({ cms: { origin: process.env.BCMS_API_ORIGIN || 'http://localhost:8080', key: { id: process.env.BCMS_API_KEY || '663126a615be57915b79a1cb', secret: process.env.BCMS_API_SECRET || 'f1d4444fdbd7a481d3f5d2e77be3bc162c272d0481d7999d90bdf9cc71f69abd', }, }, media: { output: 'public/api', download: false, }, enableClientCache: true, });
Similarly, navigate to pages/_app.tsx
and the API keys.
import '../styles/globals.css'; import type { AppProps } from 'next/app'; import { BCMSImageConfig } from '@becomes/cms-most/frontend'; BCMSImageConfig.cmsOrigin = process.env.NEXT_PUBLIC_BCMS_API_ORIGIN || 'http://localhost:8080'; BCMSImageConfig.publicApiKeyId = process.env.NEXT_PUBLIC_BCMS_API_PUBLIC_KEY_ID || '663126a615be57915b79a1cb'; function MyApp({ Component, pageProps }: AppProps): JSX.Element { return <Component {...pageProps} />; } export default MyApp;
Notion: 💡 It’s advisable to store sensitive data such as API keys in an environment file.
You can now run the development server by npm run dev
which will also fetch the data from the CMS.
While setting up the NextJs project with BCMS, the BCMS CLI automatically generates type definitions for entries and templates, which can be imported from ./bcms/types
Now create an page-data.ts
under utils
in the project directory to fetch header and footer data from your Nextjs application using BCMS. It retrieves entries for the header and footer :
import { FooterEntry, FooterEntryMeta, HeaderEntry, HeaderEntryMeta, } from '@/bcms/types'; import { BCMSClient } from '@becomes/cms-client/types'; export interface HeaderAndFooter { header: HeaderEntryMeta; footer: FooterEntryMeta; } export async function getHeaderAndFooter( client: BCMSClient, ): Promise<HeaderAndFooter> { const header = (await client.entry.get({ // Template name or ID template: 'header', // Entry slug or ID entry: 'header', })) as HeaderEntry; const footer = (await client.entry.get({ // Template name or ID template: 'footer', // Entry slug or ID entry: 'footer', })) as FooterEntry; return { header: header.meta.en as HeaderEntryMeta, footer: footer.meta.en as FooterEntryMeta, }; }
Then, create PageWrapper.tsx
to define the layout component for your Next.js application and page data:
import Header from './layout/Header'; import Footer from './layout/Footer'; import React, { FC, PropsWithChildren, useMemo } from 'react'; import { PageProps } from '@/types'; import Head from 'next/head'; import { useRouter } from 'next/router'; export const PageWrapper: FC<PropsWithChildren<PageProps>> = ({ page, header, children, footer, }) => { const router = useRouter(); const routePath = useMemo(() => router.asPath, [router.asPath]); const title = page?.meta?.title ?? 'YourBrand'; const description = 'Jumpstart your Next project with this BCMS starter. Easily manage your content and scale your application without the backend hassle. Get started now!'; const image = '/thumbnail.jpg'; const domain = 'https://yourwebsite.com'; return ( <div className="flex flex-col min-h-screen flex-1 overflow-hidden"> <Head> <title>{title} - YourBrand</title> <meta name="description" content={description} /> <meta property="og:site_name" content={`${title} - YourBrand`} /> <meta property="og:type" content="website" /> <meta property="twitter:card" content="summary_large_image" /> <meta name="ogUrl" property="og:url" content={`${domain}${routePath}`} /> <meta property="og:title" content={`${title} - YourBrand`} /> <meta property="og:description" content={description} /> <meta property="og:image" content={image} /> <meta property="twitter:url" content={`${domain}${routePath}`} /> <meta property="twitter:title" content={`${title} - YourBrand`} /> <meta property="twitter:description" content={description} /> <meta property="twitter:image" content={image} /> <link rel="canonical" href={`${domain}${routePath}`} /> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" /> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:wght@400;500;600;700&display=swap" rel="stylesheet" /> </Head> <Header data={header} /> <main className="flex-1">{children}</main> <Footer data={footer} /> </div> ); };
Also, create a contact.ts
under the types/pages
folder and define the type interface for the contact page data. You’ll import ContactPageEntryMeta
type defined in @bcms/types
:
import { ContactPageEntryMeta } from '@/bcms/types'; export interface ContactPageData { meta: ContactPageEntryMeta; }
After that create an interface for page-props.ts
in your Next.js application under the types
folder.
import { HeaderEntryMeta, FooterEntryMeta } from '@/bcms/types'; import { ContactPageData, } from './pages'; export interface PageProps< Page = | ContactPageData > { page: Page; header: HeaderEntryMeta; footer: FooterEntryMeta; }
Finally, create the contact.tsx
page under the pages folder:
import React from 'react'; import { PageWrapper } from '@/components/PageWrapper'; import ContactHero from '@/components/contact/Hero'; import ContactForm from '@/components/contact/Form'; import { ContactPageData, PageProps } from '@/types'; import { GetStaticProps } from 'next'; import { getBcmsClient } from 'next-plugin-bcms'; import { getHeaderAndFooter } from '@/utils/page-data'; import { ContactPageEntry, ContactPageEntryMeta } from '@/bcms/types'; const ContactPage: React.FC<PageProps<ContactPageData>> = ({ page, header, footer, }) => { return ( <PageWrapper page={page} header={header} footer={footer}> <ContactHero title={page.meta.title} email={page.meta.email} phone={page.meta.phone} /> <ContactForm /> </PageWrapper> ); }; export const getStaticProps: GetStaticProps< PageProps<ContactPageData> > = async () => { const client = getBcmsClient(); const { header, footer } = await getHeaderAndFooter(client); // Get Contact Page entry const contactPage = (await client.entry.get({ template: 'contact_page', entry: 'contact', })) as ContactPageEntry; if (!contactPage) { throw new Error('Home page entry does not exist.'); } return { props: { header, footer, page: { meta: contactPage.meta.en as ContactPageEntryMeta, }, }, }; }; export default ContactPage;
After this has been added, you can now visit the contact page on localhost:3000/contact
to see the data fetched from the BCMS.
Notion: 💡The page will not be styled properly because it depends on Tailwind CSS which is out of the scope of this tutorial. See TailwindCSS documentation to learn more.
Hence, you’ve successfully integrated BCMS into the NextJs frontend. If you’d like to finish this project and build all the pages, then check out the BCMS Next.js Agency Website Starter.
To deploy your self-hosted BCMS instance, you’ll need to deploy it to a live server like DigitalOcean to make it accessible on the internet.
However, with the BCMS cloud, deployment is handled for you, eliminating the need for manual server configuration.
Once your BCMS instance is deployed (either self-hosted or cloud-based), you can proceed to deploy your Next.js frontend.
Notion: 💡 Again, remember to update BCSM_API_ORIGIN
in your app with the new production URL of your BCMS instance on DigitalOcean.
Then test your Next.js application locally to verify everything works by running npm run build
and if there are no errors, you can now deploy to Vercel. See the official Next.js guide to deploy your Next.js frontend to Vercel.
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: