Building agency website with headless BCMS and NextJs
Published: 2024-05-15 13:47:29

This guide will show you how to build an agency website with [NextJs and BCMS](https://thebcms.com/nextjs-cms). You’ll create a professional website for your clients in a few minutes with little coding experience.

## Elements of an Agency Website

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 that communicates your agency’s value and acts 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 the services you offer, 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.

## **Why you should use BCMS and NextJs for building agency website**

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, a headless CMS like BCMS 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](https://thebcms.com/blog/choosing-cms-for-nextjs)

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](https://github.com/bcms/starters/tree/master/next/agency).

## Introducing the BCMS Nextjs Agency WebsiteStarter




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](https://github.com/bcms/starters) repo a star.

## Getting started with BCMS and Nextjs

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

## Initializing BCMS

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 their [website](https://cloud.thebcms.com/).

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:

```bash
npm install @becomes/cms-cli -g
```

Verify your installation, by running this command:

```bash
bcms --help
```

If the command is recognized in your terminal, this means BCMS is successfully installed.

### Creating a project

To create a BCMS project, run the following command:

```bash
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](https://docs.docker.com/) 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:

```bash
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.

## Populating BCMS with Website Data

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](https://docs.thebcms.com/inside-bcms/templates): These are your content blueprints. You define the fields needed for each section within a template.
- [Entries](https://docs.thebcms.com/inside-bcms/entries): These are individual pieces of content that follow the structure you defined in the template.
- [Groups](https://docs.thebcms.com/inside-bcms/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. To learn more about BCMS Widgets, check out [this resource](https://docs.thebcms.com/inside-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:

1. Contact page
2. Footer
3. Header
4. Homepage
5. Legal page
6. Portfolio ( Item )
7. Portfolio page
8. Service
9. Service page
10. Team member
11. Team page

There are 11 sections, hence you’ll create 11 templates.

### **Building agency website:** Contact page

Starting with the contact page, the necessary properties are:

- Title
- Slug
- SEO
- Email
- Phone

Firstly, create a new group called SEO. This group will store essential information for search engines, in this case, the Title and Description:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u9muc6ms9m006fwt6ipk.png)

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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1e5gfbt6hfws7pdx7qik.png)

### **Building agency website:** Footer

The properties needed for the Footer are Title and Slug. These are created by default when you create the Footer template.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhgx3kv4ieccrvop3od1.png)

### Header

Along with the default properties, the Header template has a Nav, which is an array of navigation links.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n7egk4ipkgktfci6crwb.png)

### Homepage Template

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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/az3q6pk36otrs8g32ouz.png)

**Home about:** This group should contain a Title, Subtitle, Description, and  Cover image for the About Us section on the home page.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gydk59tggbl6nnjqou1m.png)

**Home services:** Likewise, the Home services group has the Title, Subtitle, Description, and Cover image properties.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c20srzlfx8nk16p85vxb.png)

**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:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kcj8xljypjq827rpt42g.png)

Then create the Home capabilities group:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yqzqmocsfvjw8n23w46l.png)

**Home team:** In addition to the Title, Subtitle, Description, and Cover, there are Members title, Members Description 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:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qf3gve93nltldgba63ev.png)

Then create the Home team group:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h2a0gcjqyxjkw6z0xprv.png)

**Contact block:** This group has just the Title and Description properties.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5k6f9fylrrhfz771w2gr.png)

Finally, you can create the Home page template since the necessary groups and templates have been built:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wemrtgl0djgrutuynjcc.png)

### Legal page

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:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8jnzdheo3dx405cd89l.png)

Then create the Legal page template:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hzny0vadu3po9brm8hl.png)

### Portfolio

This template should contain the default properties with Subtitle, Description, Cover, Project Cover, and Url properties.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lsm3kv50jc4p0oyyl58o.png)

### Portfolio page

The Portfolio page template has the SEO, Description, Items, and Contact block properties besides the default properties.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3j1ms1qwcwb09qzehqf.png)

### Service

The Service template contains a Description, Cover, and Theme properties as well as the defaults.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1j3el42kp3cwbsup6otd.png)

### Services page

The Services page template contains the SEO, Description, Services, and Contact block properties in addition to the defaults.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n15hmu89u0e8zhzw8qcz.png)

### Team member

The Team member template contains the member’s Role, Description, and Image in addition to the defaults.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/walokvnlcasadi8k2xi8.png)

### Team page

The Team page template should contain the SEO, Description, Team members, and contact block properties alongside the defaults.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hn7l8zhu0sih5yc9brl3.png)

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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/idyo13kh292zgrfnbrme.png)

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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8f451bqcxtx4lu1bmfva.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/35xl86kdo8jtqsu4rv4q.png)

## Configuring NextJS to use BCMS

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.

### Creating an API Key

1. Navigate to the **Key Manager** tab within the BCMS admin panel
2. Click Add New Key to create a new key.
3. Give your key a descriptive name
4. Permissions: Since this tutorial focuses on data fetching, you only need the **Can get resources** permission for all the templates. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2s5jqrivv9fzzd6qtfsg.png)

Now, navigate to the directory you want to create your Next.js project and use the following BCMS CLI command:

 

```bash
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:

```bash
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 `http://localhost:8080`.Remember to replace this with your production URL when deploying to your server.

```jsx
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;


```

<aside>
💡 It’s advisable to store sensitive data such as API keys in an environment file.

</aside>

You can now run the development server by `npm run dev` which will also fetch the data from the CMS.

## Fetching and Displaying Data on the Frontend

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 a `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 :

```jsx
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:

```jsx
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` :

```jsx
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. 

```jsx
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:

```jsx
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 we’ve fetched from the BCMS.

<aside>
💡 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](https://v2.tailwindcss.com/docs) to learn more.

</aside>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jq5mu7qhv356247kcy97.png)

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.

## Deployment

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. The BCMS documentation provides deployment steps for [DigitalOcean deployment](https://docs.thebcms.com/install/digitalocean).

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.

<aside>
💡 Again, remember to update **`BCSM_API_ORIGIN`** in your app with the new production URL of your BCMS instance on DigitalOcean.

</aside>

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](https://vercel.com/). See the official Next.js [guide](https://nextjs.org/learn-pages-router/basics/deploying-nextjs-app/deploy) to deploy your Next.js frontend to Vercel.