Back to all posts

How to Build a CMS-powered Marketing Website while Avoiding WordPress

Posted by Nirmalya Ghosh on April 5, 2019

While WordPress powers 33% of the web, there are plenty of easier ways through which you can create your marketing site. WordPress can give you a basic outlook to your site. But, to make changes to it, you need to understand how WordPress works. Apart from that, WordPress tends to slow down as you need to add more plugins to modify your site.

You can create a marketing site with ease using React.js on the Front End and ButterCMS on the Back End. In this way, you can easily construct and manage the data that your Front End needs. You can use Butter as a headless CMS to build websites in any tech stack. Also, ButterCMS works with any tech stack and works with zero maintenance.

In this tutorial, you will be creating a marketing site using Next.js on the Front End and ButterCMS will power the API. Next.js is a React.js framework which will do Server Side Rendering for you. As a result, your site will have a much better SEO score all while avoiding Wordpress. 

Try it yourself

Getting Started 

Here's a look at the beautiful marketing website I created using ButterCMS:

cms-powered marketing website
Free HTML landing page template provided by Cruip

Follow along with me as I show you how to build your own dynamic CMS-powered marketing website, while avoiding Wordpress. Let's get started.

An online demo is hosted at ZEIT Now and the code is available on GitHub. We will be using the React API Client of ButterCMS. You can start with my initial commit on GitHub to get started with the code.

First, you need to install ButterCMS JavaScript SDK:

yarn add buttercms

This will add buttercms to your list of dependencies in your project. You will need to initialize ButterCMS with an API key which you can find in the settings page of ButterCMS. In this case, we will use React Context to create and pass props to the consumer. To create a React Context, you need to add the following in pages/index.js:

const ButterCMSContext = React.createContext();
const butter = Butter(process.env.BUTTER_CMS_API_KEY);

Deploying

  1. Create a ZEIT account at https://zeit.co/signup and download the CLI
  2. Add the API key as a secret now secrets add butter-cms-api-key "YOUR_API_KEY"
  3. Run now at the project root

Storing data in ButterCMS

You can create pages in ButterCMS to store your data. Go to the New Page tab and select the Media option from the sidebar:

CMS-powered marketing website

Then, you need to add a slug (logo) to the Media option and click on Save to save the page:

CMS-powered marketing website

Then, you can add the Page Title and API Slug for the page:

undefined

Click on Insert Media to add a new Logo and click on Publish to publish the page:

undefined

Fetching data from ButterCMS

We will fetch the data from ButterCMS required for that page using ButterCMS pages API in the getInitialProps hooks provided by Next.js. First, you can create a data object which will store the data fetched from ButterCMS:

 data = {
   generalData: {}
 };

Then, you use getInitialProps to fetch the data from ButterCMS using a async/await:

static async getInitialProps() {
  try {
    const response = await Promise.all([
      butter.page.retrieve("*", "general")
    ]);

    this.data = {
      generalData: response[0].data.data
    };
  } catch (error) {
    console.error(error);
  }

  return {
    data: this.data
  };
}

So, this data is now available as a prop which can be accessed as this.props.data in the render method. We can pass this prop to our components like Header, Footer, etc. using the Consumer Render Props:

<ButterCMSContext.Provider
value={{
  generalData: data.generalData
}}
>
<ButterCMSContext.Consumer>
  {({
    generalData
  }) => {
    return (
      <Fragment>
        <Header data={generalData} />
      </Fragment>
    );
  }}
</ButterCMSContext.Consumer>
</ButterCMSContext.Provider>

Now, this data is available as a prop in the Header component.

Using data in a component

You can use this data in the Header component which will look like the following:

function Header({ data }) {
return (
  <header className="site-header">
    <div className="container">
      <div className="site-header-inner">
        <div className="brand header-brand">
          <h1 className="m-0">
            <Link href="/">
              <a>
                <img src={data.fields.logo} alt={data.fields.company_name} />
              </a>
            </Link>
          </h1>
        </div>
      </div>
    </div>
  </header>
);
}

cms-powered marketing website

The logo will appear in the Header section.

Read more great CMS articles by subscribing to our monthly newsletter.
    

So far you've learned how you can save and fetch data from ButterCMS while building the Header section of your site. You can use a similar approach to fill in all the sections for your CMS-powered marketing site. At the end of the tutorial, you will be able to create a full site using the API of ButterCMS

Building the Hero section

For the Hero section, you can have four fields: title (Short Text), sub_title (Long Text), button_text (Short Text) and button_link (Short Text).

cms-powered marketing website

You can save this page and fill in with appropriate details:

CMS-powered marketing website

You can use this data in the Hero component which will look like the following: 

function Hero({ data }) {
return (
  <section className="hero">
    <div className="hero-left-decoration " />
    <div className="hero-right-decoration " />
    <div className="container">
      <div className="hero-inner">
        <div className="hero-copy">
          <h1 className="hero-title mt-0 ">{data.fields.title}</h1>
          <p className="hero-paragraph ">{data.fields.sub_title}</p>
          <p className="hero-cta mb-0 ">
            <a
              className="button button-primary button-shadow"
              href={data.fields.button_link}
            >
              {data.fields.button_text}
            </a>
          </p>
        </div>
        <div className="hero-illustration" />
      </div>
    </div>
  </section>
);
}

Once you Save and Publish this page, you can see the changes reflecting on your page:

cms-powered marketing website

Building the Clients section

For the Clients section, you can have one repeater field which will have an image (Image) and a name (Short Text) fields inside it:

undefined

You can save this page and fill in with appropriate details:

cms-powered marketing website

You can use this data in the Clients component which will look like the following:

function Clients({ data }) {
return (
  <section className="clients section">
    <div className="container">
      <div className="clients-inner section-inner has-top-divider">
        <div className="container-sm">
          <ul className="list-reset mb-0">
            {data.fields.clients.map((client, index) => {
              return (
                <li key={index} className="">
                  <img src={client.image} alt={client.name} />
                </li>
              );
            })}
          </ul>
        </div>
      </div>
    </div>
  </section>
);
}

Once you Save and Publish this page, you can see the changes reflecting on your page: 

cms-powered marketing website

Building the Features section 

For the Features section, you can have one title (Short Text), sub_title (Long Text) field and one repeater field which will have a title (Short Text) and a sub_title (Long Text) fields inside it:

cms-powered website

You can use this data in the Features component which will look like the following:

function Features({ data }) {
return (
  <section className="features section text-center">
    <div className="container">
      <div className="features-inner section-inner has-top-divider">
        <div className="features-header text-center">
          <div className="container-sm">
            <h2 className="section-title mt-0">{data.fields.title}</h2>
            <p className="section-paragraph mb-0">{data.fields.sub_title}</p>
          </div>
        </div>
        <div className="features-wrap">
          {data.fields.features.map((feature, index) => {
            return (
              <div className="feature " key={index}>
                <div className="feature-inner">
                  <div className="feature-icon">
                    <img src={FeatureIcon1} alt="Feature 01" />
                  </div>
                  <h4 className="feature-title">{feature.title}</h4>
                  <p className="text-sm">{feature.sub_title}</p>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  </section>
);
}

You can save this page, fill in with appropriate details and Save and Publish this page, you can see the changes reflecting on your page:

cms-powered website

Building the Testimonials section

For the Testimonials section, you can have one title (Short Text), sub_title (Long Text) field and one repeater field which will have a body (Short Text) and an author (Long Text) fields inside it:

cms-powered marketing website

You can use this data in the Testimonials component which will look like the following:

function Testimonials({ data }) {
return (
  <section className="testimonials section">
    <div className="container">
      <div className="testimonials-inner section-inner">
        <div className="testimonials-header text-center text-light">
          <h2 className="section-title mt-0">{data.fields.title}</h2>
        </div>
        <div className="testimonials-wrap">
          {data.fields.testimonials.map((testimonial, index) => {
            return (
              <div className="testimonial text-sm " key={index}>
                <div className="testimonial-inner">
                  <div className="testimonial-main">
                    <div className="testimonial-body">
                      <p>{testimonial.body}</p>
                    </div>
                  </div>
                  <div className="testimonial-footer">
                    <div className="testimonial-name">
                      <a href="#">@{testimonial.author}</a>
                    </div>
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  </section>
);
}

You can save this page, fill in with appropriate details and Save and Publish this page, you can see the changes reflecting on your page:

cms-powered marketing website

Building the Pricing section

For the Pricing section, you can have one title (Short Text) and one repeater field which will have a title (Short Text), price (Number), features (Long Text), button_link (Short Text) and a button_text (Short Text) fields inside it:

undefined

You can use this data in the Pricing component which will look like the following:

function Pricing({ data }) {
return (
  <section className="pricing section">
    <div className="container">
      <div className="pricing-inner section-inner">
        <h2 className="section-title mt-0 text-center">
          {data.fields.title}
        </h2>
        <div>
          <div className="pricing-tables-wrap">
            {data.fields.pricing.map((preset, index) => {
              return (
                <div className="pricing-table " key={index}>
                  <div className="pricing-table-inner">
                    <div className="pricing-table-main">
                      <div className="pricing-table-header mb-24 pb-24">
                        <div className="pricing-table-title h4 mt-0 mb-16">
                          {preset.title}
                        </div>
                        <div className="pricing-table-price">
                          <span className="pricing-table-price-currency">
                            $
                          </span>
                          <span className="pricing-table-price-amount h1">
                            {preset.price}
                          </span>
                          /m
                        </div>
                      </div>
                      <ul className="pricing-table-features list-reset text-xs">
                        {preset.features}
                      </ul>
                    </div>
                    <div className="pricing-table-cta">
                      <a
                        className="button button-secondary button-shadow button-block"
                        href={preset.button_link}
                      >
                        {preset.button_text}
                      </a>
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  </section>
);
}

You can save this page, fill in with appropriate details and Save and Publish this page, you can see the changes reflecting on your page:

cms-powered marketing website

Building the FAQ section

For the FAQ section, you can have one title (Short Text) and one repeater field which will have a question (Long Text)  and answer (Long Text) fields inside it:

cms-powered marketing website

You can use this data in the FAQ component which will look like the following:

function FAQ({ data }) {
return (
  <section className="pricing section has-animations">
    <div className="container">
      <div className="pricing-inner section-inner">
        <div className="pricing-faqs container-sm ">
          <h4 className="mt-40 mb-32">{data.fields.title}</h4>
          <ul className="accordion">
            {data.fields.faqs.map((faq, index) => {
              return (
                <li key={index}>
                  <div className="accordion-title">
                    <span>{faq.question}</span>
                    <div className="accordion-icon" />
                  </div>
                  <div className="accordion-body">
                    <p>{faq.answer}</p>
                  </div>
                </li>
              );
            })}
          </ul>
        </div>
      </div>
    </div>
  </section>
);
}

You can save this page, fill in with appropriate details and Save and Publish this page, you can see the changes reflecting on your page:

faqs

Building the Contact section

For the Contact section, you can have one button_link (Short Text), button_text (Short Text) and a description (Short Text) fields inside it:

undefined

You can use this data in the Contact component which will look like the following:

function Contact({ data }) {
return (
  <section className="cta section">
    <div className="container">
      <div className="cta-inner section-inner ">
        <h3 className="section-title mt-0">{data.fields.description}</h3>
        <div className="cta-cta">
          <a
            className="button button-primary button-shadow"
            href={data.fields.button_link}
          >
            {data.fields.button_text}
          </a>
        </div>
      </div>
    </div>
  </section>
);
}

You can save this page, fill in with appropriate details and Save and Publish this page, you can see the changes reflecting on your page:


cms-powered marketing website

Building a Blogs page

You can also create a separate page which will list out all the blogs. You can create pages easily using Next.js. (We have a guide on setting up CMS-powered pages for any tech stack here)

For generating the API from ButterCMS, you can make use of their Blog Engine feature. You need to click on the Blog Posts menu on the sidebar:

cms-powered marketing website

By default, there will be one sample post present. You can add more by clicking on the Write New Post button or by visiting the New Post url.

You can write your own article there and click on the Publish button to publish your blog post:

undefined
Once that is done, you need to create a new page in your code to show all the blogs. To create a new page in Next.js, you just need to create a new file called blogs.js inside the pages directory:

undefined

Similar to our pages/index.js, you can create a data object to store the data from API:

data = {
loading: true,
generalData: {},
blogsData: {},
contactData: {}
};

Then, you use getInitialProps to fetch the data from ButterCMS using a async/await:

static async getInitialProps() {
try {
  const response = await Promise.all([
    butter.page.retrieve("*", "general"),
    butter.post.list(),
    butter.page.retrieve("*", "contact")
  ]);

  this.data = {
    generalData: response[0].data.data,
    blogsData: response[1].data.data,
    contactData: response[2].data.data,
    loading: false
  };
} catch (error) {
  console.error(error);
}

return {
  data: this.data
};
}

Then, you can create a Provider and a Consumer like we did earlier in our pages/index.js to display the data:

<ButterCMSContext.Provider
value={{
  loading: data.loading,
  generalData: data.generalData,
  blogsData: data.blogsData,
  contactData: data.contactData
}}
>
<ButterCMSContext.Consumer>
  {({ loading, generalData, blogsData, contactData }) => {
    if (loading)
      return (
        <div className="loading">
          <img src={Loader} />
        </div>
      );

    return (
      <Fragment>
        <Header data={generalData} />
        <Blogs data={blogsData} />
        <Contact data={contactData} />
        <Footer data={generalData} />
      </Fragment>
    );
  }}
</ButterCMSContext.Consumer>
</ButterCMSContext.Provider>


You can create a new
Blogs component which will look like the following: 

function createMarkup(html) {
return { __html: html };
}

function Blogs({ data }) {
return (
  <section className="testimonials section">
    <div className="container">
      <div className="testimonials-inner section-inner">
        <div className="testimonials-header text-center text-light">
          <h2 className="section-title mt-0">Blogs</h2>
        </div>
        <div className="testimonials-wrap">
          {data.map((blog, index) => {
            return (
              <div className="testimonial text-sm " key={index}>
                <div className="testimonial-inner">
                  <div className="testimonial-main">
                    <div className="testimonial-body">
                      <div
                        dangerouslySetInnerHTML={createMarkup(blog.body)}
                      />
                    </div>
                  </div>
                  <div className="testimonial-footer">
                    <div className="testimonial-name">
                      <a href="#">
                        {blog.author.first_name} {blog.author.last_name}
                      </a>
                    </div>
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  </section>
);
}

 Now, if you add some data on ButterCMS, you can see the blogs appearing on your page: 

undefined


You can also create individual pages for each blog as well as show a paginated view for the blogs section. You can also show Categories, Tags and Authors.

Conclusion

At this point, you should be able to understand how to create a beautiful site using ButterCMS and Next.js. Your own CMS-powered marketing website without using Wordpress. ButterCMS play really well with React.js and Next.js and would be very useful to create a visually beautiful website using its APIs.

I hope this tutorial helps you in your future projects. Please feel free to share your feedback in the comments section below.

Interested in JAMStack? We have a guide that covers the foundations, architecture and a step-by-step tutorial on how to build a site using Jamstack. Check it out!

cms-powered marketing website

Make sure you receive the freshest Butter product updates.
    

Related Articles