ButterCMS Logo

How to Create a Blog with React and ButterCMS

Published on
Updated on
21 min read
Featured Image: How to Create a Blog with React and ButterCMS
GSD

If you are a software developer, the odds that you have used React.js to build a web application are pretty high. This is because React is unarguably one of the most popular JavaScript frameworks. According to Statistica, it is the second most used web framework among developers worldwide after Node.js, with a 40.58% usage rating from respondents. React has been used to build popular web apps such as Instagram, Dropbox, Netflix, etc, and can be used for different kinds of websites, including blogs, which is the focus of this tutorial.

There are different ways to create a blog with React. Still, if you want an organized and flexible way of managing your content while saving the time and effort it will take to develop a solution yourself, then, using a headless content management system (CMS) like ButterCMS is one of the best ways to do that.

In this tutorial, we’ll guide you through setting up a blog using React and ButterCMS. If you'd like to follow along, you can start a 14-day free trial of ButterCMS to get hands-on experience as we build. Before we dive into implementation, let’s look at a few reasons why React is great for building a blog:

Why is React great for building blogs

React offers features and capabilities that could make it exactly what you need for your blog. Let’s explore a handful below:

React is SEO-friendy

Out of the box, React is client-side rendered (CSR-ered). A few years ago, this would have made it a bad option for blogs due to SEO (search engine optimization) concerns, but with the support of Javascript by popular search engines like Google and Bing, React CSR-ered websites can now be crawled and indexed, making it a viable option for creating blogs.

For better SEO and to avoid the challenges associated with CSR, we can enable server-side rendering (SSR) using server React DOM APIs like renderToString and renderToPipeableStream or generate static HTML using renderToStaticMarkup. With this, search engines will no longer need to execute JavaScript to crawl our site, resulting in improved SEO performance.

Enhanced UI capabilities

React’s rich ecosystem offers a large pool of tools and packages that add enhanced UI capabilities to your blog. From WYSIWYG editor and code syntax highlighter to slick animations, you name it; there is probably a library out there you can integrate to provide the desired experience for your users. Additionally, with React’s virtual DOM – an out-of-the-box mechanism that efficiently handles DOM updates, the process of adding UI capabilities, is much easier and faster, giving you more time to create the desired user experience.

Below are a few tools that can be used to enhance the UI capabilities of your blog:

  • Tiptap Editor: A customizable WYSIWYG rich text editor. It can be added to the comment section to facilitate communication.

  • React Syntax Highlighter: A React component for code syntax highlighting. It can be used to highlight code snippets in a blog post.

  • Framer motion: A popular React animation library. It can be used to add page transitions, scroll animations, hover effects, etc.

Reusable components

One of the key concepts of React is components. Components enable us to break down a user interface (UI) into independent pieces that can be used in different parts of an application. Utilizing components for a blog, the blog postcard, header, footer, custom button, etc., can be created separately and used through the blog application. This can improve productivity by enabling the reuse of said components. Additionally, several component libraries like Material UI, Chakra UI, Ant Design, etc., offer ready-to-use components that could save you time. 

React is flexible

React can be used on different platforms to build high-quality user interfaces. It can be used for web apps, mobile apps, and desktop apps. In the context of creating blogs, it allows the setting of unique meta descriptions and titles that contribute to SEO. It can be used for server-side rendering and static-site generation, which can be enabled with a few tweaks to your code. Furthermore, its large community of developers continuously develops tools to accelerate the incorporation of desired features into your blog app.

Tutorial goals and prerequisites

In this tutorial, we will learn how to create a blog with React and manage its content using ButterCMS. In the blog, we will fetch the data from ButterCMS, display it in our app, and add a search feature to sort content based on their category.

To follow along with this tutorial, you should have the following:

Tutorial: Building a blog with React

There are different approaches to building a blog with React. We can choose to hard-code a blog (i.e., manually enter all data to be displayed on the blog). While this could work, it can quickly become cumbersome to update the blog’s content. Moreover, it is not suitable for a developer-to-client project, as it would require the client to possess the technical know-how of the used programming language or the developer’s assistance to update the blog.

One alternative to this is to create a back-end along with an authentication-protected interface to manage the blog’s content. This provides more flexibility and makes carrying out CRUD operations feasible for the client. 

However, a better approach is to use a content management system (CMS), namely a headless CMS. This approach provides a ready-to-use dashboard where we can view and manage all our content. We will be using this approach in this article. 

We will start by building the front-end of the blog with React, then set up our content structure using ButterCMS, and finally, link the CMS to our app to display content.

Building the blog’s front-end

The first step to setting up the front-end with React is to create a new React application. This can be done via the CLI in your chosen directory with the following command:

npm create vite@latest blogreact -- --template react
cd blogreact
npm install

If you’re prompted to install create-vite, enter y. The above command creates a project folder blogreact with all necessary dependencies for React installed. 

To handle API requests and routing in our blog application and to navigate from the landing page to specific blog content, we will be using Axios and React-routers (a React module for creating dynamic routes in a React application). We can install these dependencies via the CLI with the following commands:

npm install react-router-dom axios

Once installation is complete, we can open this in a code editor and proceed to create the front-end of our application.

Defining the application’s routes

Firstly, we will define the routes for our blog application. These routes are the landing page, a route to a specific blog post, and a redirect route back to the home page.

We will do this using the React-router-dom package we installed. In main.jsx, make the following changes to allow for routing in the application:

import { BrowserRouter } from "react-router-dom";

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode&gt;,
)

Then, we can create specific routes in App.jsx:

import "./App.css";
import { Route, Routes } from "react-router-dom";
import HomePage from "./pages/HomePage";
import BlogPost from "./pages/BlogPost";
import { React, useState } from "react";

function App() {
const [getBlogContent, setGetBlogContent] = useState([]);
  const getData = (blog) => {
    setGetBlogContent(blog);
  }
  return (
    <div>
      <div className="container">
        <Routes>
          <Route path="/" element={<HomePage data={getData}/>} />
          <Route path="/blog/:id" element={<BlogPost content={getBlogContent}/>} />
        </Routes>
      </div>
    </div>
  );
}
export default App;

Here, we set up the route to our HomePage and BlogPost. The home page has an empty path for the route, while the blog route has the pathname “blog,” along with the ID of the specific React blog we will be viewing. In this case, it will be the name of the content.

Creating the landing and blog pages

We will create two folders in the src directory of the project folder: pages and components. In the former, we will have the HomePage and BlogPost pages, while the latter will contain the components that make up our application's two main pages. In HomePage.jsx, we have the following:

import {React,  useState, useEffect } from 'react';
import EmptyList from '../components/EmptyList';
import BlogList from '../components/BlogList';
import Header from '../components/Header';
import SearchBar from '../components/SearchBar';
import { blogList } from '../config/Api';

const HomePage = ({data}) => {
  const [blogs, setBlogs] = useState([]);
  const [filteredBlogs, setFilteredBlogs] = useState([])
  const [searchKey, setSearchKey] = useState('');
  // Search submit
  const handleSearchBar = (e) => {
    e.preventDefault();
    handleSearchResults();
  };
  // Search for blog by category
  const handleSearchResults = () => {
   //handle search inputs
  };
  // Clear search and show all blogs
  const handleClearSearch = () => {
    blogList().then((res) => {
      setBlogs(res);
    })
    setSearchKey("");
  };

  // function to get selected blog content
 const BlogContent = (id) => {
  data(id);
}
  return (
    <div>
      {/* Page Header */}
      <Header />
      {/* Search Bar */}
      <SearchBar
        value={searchKey}
        clearSearch={handleClearSearch}
        formSubmit={handleSearchBar}
        handleSearchKey={(e) => setSearchKey(e.target.value)}
      />
      {/* Blog List & Empty View */}
      {!filteredBlogs.length ? <EmptyList /> : <BlogList blogs={filteredBlogs} content = {BlogContent}/>}
    </div>
  );
};
export default HomePage;

This will be the page structure of our landing page. It will contain a Header component and a SearchBar component, and it will render the content of our React blog. 

In BlogPost.jsx we have the following:

import {React} from 'react';
import Chip from '../components/Chip';
import EmptyList from '../components/EmptyList';
import '../index.css';
import { Link } from 'react-router-dom';

const Blog = () =&gt; {
 
  return (
    &lt;&gt;
      &lt;Link className='blog-goBack' to='/'&gt;
        &lt;span&gt; &#8592;</span> <span>Go Back</span>
      &lt;/Link>
    </&gt;
  );
};
export default Blog;

This page describes the structure of the React blog view page. When the user selects content from the landing page, they can view more information on that content. This will be made possible using the BlogPost page.

Creating our components

Next, we will define all components we will be using in our application. In the components directory, create the following files: Chip.jsx, EmptyList.jsx, BlogList.jsx, BlogItem.jsx, Header.jsx, and SearchBar.jsx.

The first component, Chip.jsx, will be responsible for containing the tags of specific blog content. We will use these tags to classify content and return content whose tags match the search field input when we wish to search through our React blog. The listed components will contain the following blocks of code:

//Chip.js

import React from 'react';
import '../index.css';
const Chip = ({ label }) => <p className='chip'>{label}</p>;
export default Chip;

//EmptyList.js

import React from 'react';
import '../index.css';
const EmptyList = () => (
  <div className='emptyList-wrap'>
    <img src='/assets/13525-empty.gif' alt='empty' />
  </div>
);
export default EmptyList;

This component defines an image that will be displayed when there is no content to be displayed on the page, or when the search query does not match the tags of any article. 

//BlogList.js

import React from 'react';
import BlogItem from './BlogItem';
import '../index.css';
const BlogList = ({ blogs, content }) => {
  return (
    <div className='blogList-wrap'>
      {blogs.map((blog) => (
        <BlogItem blog={blog} content={content}/>
      ))}
    </div>
  );
};
export default BlogList;

And in BlogItem.jsx, we have:

import React from 'react';
import { Link } from 'react-router-dom';
import Chip from './Chip';
import '../index.css';
const BlogItem = ({blog, content}) => {
  return (
    <div className='blogItem-wrap'>
    </div>
  );
};
export default BlogItem;

BlogItem.jsx and BlogList.jsx will handle rendering the content received from our CMS in our React application. In our Header.jsx, we have the following:

import React from 'react';
import '../index.css';
const Header = () => (
  <header className='home-header'>
    <h2>My Personal Blog</h2>
    <h1>
      <span>"</span> Take a look at my Content <span>"</span>
    </h1>
    <p>
      Read, enjoy <br /> and contribute.
    </p>
  </header>
);
export default Header;

For the SearchBar.js component, we have:

import React from 'react';
import '../index.css';
const SearchBar = ({ formSubmit, value, handleSearchKey, clearSearch }) => (
  <div className='searchBar-wrap'>
    <form onSubmit={formSubmit}>
      <input
        type='text'
        placeholder='Search By Category'
        value={value}
        onChange={handleSearchKey}
      />
      {value && <span onClick={clearSearch}>X</span>}
      <button>Go</button>
    </form>
  </div>
);
export default SearchBar;

And finally we will create a file called Api.js in a new directory, config, within the src folder. This file will handle all API requests for our CRUD operations:

import React from 'react'
export const blogList = async ()=>{
}

Styling our blog application

To style our application, add the following styles in index.css:

@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
*,
*::after,
*::before {
  box-sizing: border-box;
  margin: 0;
}
body {
  font-family: 'Montserrat', sans-serif;
}
button,
input {
  font-family: 'Montserrat', sans-serif;
}
.container {
  max-width: 1140px;
  width: 95%;
  margin: 0 auto;
  padding: 1rem 0;
}
/* BlogPost */
.blog-wrap {
  max-width: 700px;
  margin: 0 auto;
}
.blog-goBack {
  text-decoration: none;
  font-size: 0.8rem;
  color: #a9a9a9;
  font-weight: 500;
  margin-bottom: 2rem;
  display: block;
}
.blog-wrap header {
  text-align: center;
}
.blog-date {
  font-size: 0.8rem;
  color: #a9a9a9;
  font-weight: 500;
}
.blog-wrap img {
  width: 100%;
}
.blog-subCategory {
  display: flex;
  justify-content: center;
}
.blog-subCategory > div {
  margin: 1rem;
}
.blog-content p{
  margin-top: 1.5rem;
}
.blog-content h1, .blog-content h2, .blog-content h3, .blog-content h4, .blog-content h5, .blog-content h6 {
  margin-top: 1.5rem;
}

.blog-content img {
  margin: 10px 0;
}
/* Search Bar */
.searchBar-wrap, .create-form input {
  background-color: #f0f0f0;
  width: fit-content;
  margin: 2.5rem auto 4rem auto;
  padding: 0.5rem;
  border-radius: 5px;
}
.searchBar-wrap form, .create-form input {
  display: flex;
  align-items: center;
}
.searchBar-wrap input {
  background-color: #f0f0f0;
  outline: none;
  border: none;
}
.searchBar-wrap span {
  padding-right: 0.5rem;
  cursor: pointer;
}
.searchBar-wrap button, .btn {
  outline: none;
  border: none;
  padding: 0.3rem 1rem;
  border-radius: 5px;
  background-color: #0f52ba;
  color: #fff;
}

.btn{
  z-index: 5;
}
/* Header */
.home-header {
  text-align: center;
}
.home-header h2 {
  color: #0080ff;
  font-size: 2rem;
}
.home-header h1 {
  font-size: 3rem;
  color: #0f52ba;
  margin-bottom: 1rem;
}
.home-header h1 span {
  color: #b0c4de;
}
.home-header p {
  color: #a9a9a9;
  font-weight: 500;
}
/* Blog List */
.blogList-wrap {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 3rem;
}
@media (max-width: 768px) {
  .blogList-wrap {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 600px) {
  .blogList-wrap {
    grid-template-columns: repeat(1, 1fr);
  }
}
/* Blog Item */
.blogItem-wrap {
  display: flex;
  flex-direction: column;
}
.blogItem-cover {
  width: 100%;
  height: 250px;
  object-fit: cover;
  border-radius: 20px;
  margin-bottom: 0.5rem;
}
.blogItem-wrap h3 {
  margin: 0.5rem 0 1rem 0;
  flex: 1;
}
.blogItem-desc {
  position: relative;
  max-height: 50px;
  overflow: hidden;
  padding-right: 0.6rem;
  font-size: 0.8rem;
  color: #a9a9a9;
}
.blogItem-desc::before {
  position: absolute;
  content: '...';
  bottom: 0;
  right: 0;
}
.blogItem-wrap footer {
  display: flex;
  align-items: center;
  margin-top: 1rem;
  justify-content: space-between;
}
.blogItem-link {
  text-decoration: none;
  color: inherit;
}
.blogItem-author {
  display: flex;
  align-items: center;
}
.blogItem-author img {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  margin-right: 0.5rem;
}
.blogItem-author p {
  font-size: 0.6rem;
  color: #a9a9a9;
  font-weight: 600;
}
/* Empty List */
.emptyList-wrap {
  display: flex;
  justify-content: center;
}
.emptyList-wrap img {
  max-width: 250px;
  width: 100%;
}
/* Chip */
.chip {
  font-size: 0.7rem;
  background: linear-gradient(to right, #6190e8, #a7bfe8);
  color: #fff;
  padding: 0.3rem 0.5rem;
  border-radius: 5px;
  width: fit-content;
  text-transform: capitalize;
}

/* Create content form */
.create-form{
  position: absolute;
  height: 90vh;
  top: 0;
  z-index: 3;
  backdrop-filter: blur(5px);
  width: 80%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.formcont{
  background: #fff;
  width: 80%;
  display: flex;
  padding: 10px 10px;
  flex-direction: column;
  height: 80%;
  justify-content: center;
}
.formcont textarea{
  border: 1px solid #ccc;
  outline: none;
  resize: none;
  padding: 10px;
  font-size: 0.7rem;
  border-radius: 5px;
  margin-bottom: 1rem;
  width: 100%;
  height: 12em;
}
.formcont span{
  display: flex;
}
.file{
  margin: 7px!important;
  padding: 0 !important;
}

Now, if we run our application using the npm run dev command in the CLI, we get a result similar to the below image:

Personal blog result after running npm start in the CLI.

Setting up ButterCMS

To get started with ButterCMS, you will first need to create a user account and log in to your dashboard on the ButterCMS website. ButterCMS has a content structure set up designed for creating React blog posts. With this, we do not need to create our own setup. To make use of React blog posts, select Blog posts from the left sidebar:

Where the

On the new page that opens up, we have an example blog post already created for us. If you wish to create a new blog post, click on the New Post button at the top-right and fill out the form on the page that opens. The WYSIWYG editor provided by the Blog Post type makes it easy to create content for a React blog site, including inserting media and videos into it.

Creating a new post in ButterCMS interface (specifically the blog engine).

Connecting our application to ButterCMS

To connect our application to ButterCMS, we will need the read token to fetch content from the CMS. This can be found in the settings tab on the dashboard. Copy and store this key in a .env file in your project directory. In Api.js, we will set up our fetch operation:

import React from "react";
import axios from "axios";

const read_token = import.meta.env.VITE_READ_TOKEN;

export const blogList = async () => {
  const url = `
  https://api.buttercms.com/v2/posts?auth_token=${read_token}`;
  return axios.get(url).then((res) => {
    return res.data.data;
  });
};

In HomePage.jsx, we can  get the data from this API request and store it in our blog state:

// get content from buttercms
useEffect(() => {
    blogList().then((res) => {
        setBlogs(res);
    })
} , []);

Then, we will render the content in BlogItem.jsx:

const BlogItem = ({blog, content}) => {
  return (
   <div className='blogItem-wrap' key={blog.title}>
      <img className='blogItem-cover' src={blog.featured_image} alt='cover' />
      <Chip label={blog.tags[0].name} />
      <h3>{blog.title}</h3>
      <p className='blogItem-desc'>{blog.summary}</p>
      <footer>
        <div className='blogItem-author'>
          <img src={blog.author.profile_image} alt='avatar' />
          <div>
            <h6>{blog.author.first_name+" "+blog.author.last_name}</h6>
            <p>{blog.created_at}</p>
          </div>
        </div>
        <Link className='blogItem-link' to={`/blog/${blog.title}`} onClick={()=>{content(blog)}}></Link>
      </footer>
    </div>
  );
};

This will render the content stored on our CMS. Below is an image showing some sample content I created in my blogreact collection:

New blog post displaying on the deployed website.

To be able to view our content via the arrow at the bottom right of the BlogItem component, we just need to make the following changes to BlogPost.jsx:

const Blog = ({content}) => {
  return (
    <>
      <Link className='blog-goBack' to='/'>
        <span> &#8592;</span> <span>Go Back</span>
      </Link>
      {content ? (
        <div className='blog-wrap'>
          <header>
            <p className='blog-date'>Published {content.created}</p>
            <h1>{content.title}</h1>
            <div className='blog-subCategory'>
              
                <div>
                  <Chip label={content.tags[0].name} />
                </div>
              
            </div>
          </header>
          <img src={content.featured_image} alt='cover' />
          <div className='blog-content' dangerouslySetInnerHTML={{__html: content.body}}></div>
        </div>
      ) : (
        <EmptyList />
      )}
    </>
  );
};

Here, we have the structure for the React blog content that will be displayed in the BlogPost.jsx file. The WYSIWYG editor returns its content in the form of HTML. We can then render this to produce the required results.

With this, we get a preview of the selected React blog content:

Selected blog post preview.

With our search field in the HomePage component, we will be able to search through our content and display results corresponding to a search query. To do this, edit the handleSearchResults function in HomePage.jsx as shown below:

 // Search for blog by category
  const handleSearchResults = () => {
    const filtered = blogs.filter((blog) => {
      return blog.tags[0].name.toLowerCase().includes(searchKey.toLowerCase().trim());
    });
    setFilteredBlogs(filtered);
  };

What is the future of React blog development?

The React team is consistently introducing innovative improvements to build better React apps. Over the years, features like Streaming and Server Components have been introduced offering better performance along with Concurrent Rendering, which improves responsiveness, all of which are great for blogs. In the upcoming version of React: React 19, several new features have also been introduced, and one of the notable ones essential for blog applications is the support for Document metadata, enabling the use of tags like and <meta> directly in components. This was previously done using effects or libraries like <a href="https://www.npmjs.com/package/react-helmet" rel="nofollow noopener" target="_blank">React Helmet</a>. With all these improvements, we can tell how enthusiastic the React team is about making React more performant, which is very important for blog applications. </p> <h2 id="questions-about-building-a-blog-with-react"><span style="font-weight: 400;">Questions about building a blog with React</span></h2> <div itemscope="" itemtype="https://schema.org/Question"> <h3 itemprop="name"><span style="font-weight: 400;">How do you use React components in a blog?</span></h3> <div itemprop="acceptedAnswer"> <p><span style="font-weight: 400;">React components are independent segments of code created for reuse to prevent a repetition of code blocks. In a blog use case, portions of the code such as the card display of articles on the blog can be created as a component to avoid having to repeat the same code for different posts. This component can then be imported to handle the rendering of as many posts that are to be displayed on a page. Also, sections such as the header and footer sections, which are the same for each article page, can be created as components and rendered with changes to their content to reflect the current article’s name and details on the article page.</span><span style="font-weight: 400;"></span></p> </div> </div> <div itemscope="" itemtype="https://schema.org/Question"> <h3 itemprop="name"><span style="font-weight: 400;">How do you test your React blog?</span></h3> <div itemprop="acceptedAnswer"> <p><span style="font-weight: 400;">React applications usually have different levels of sophistication. The first step to application testing is to outline sections of the application to be tested. There is not really any general method of classifying aspects of applications to be tested. However, if you are to carry out a test on a React blog application, these are the following sections you should consider:</span><span style="font-weight: 400;"></span></p> <ul> <li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;"><b>Core features of the blog</b>: Fetching blog content from whatever data source it is linked to and rendering individual content in a manner that boosts user experience</span></li> <li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;"><b>User interactions</b>: Include testing scope for user interactions, such as actions to be handled by </span><span style="font-weight: 400;">onClick</span><span style="font-weight: 400;"> events on the cards and the resulting action of opening and displaying the article content. Testing the search events using the on-site search field.</span></li> <li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;"><b>Components and state changes</b>: Test the most used components that make up the blog and consider state changes and updates caused by prop changes in the blog. An example of this is the prop passed from the “all contents” display component of a React blog to the page where an individual content has its entire data displayed due to the user selecting a particular article from the list of items.</span></li> </ul> </div> </div> <div itemscope="" itemtype="https://schema.org/Question"> <h3 itemprop="name"><span style="font-weight: 400;">What should I learn before starting with React.JS?</span></h3> <div itemprop="acceptedAnswer"> <p><span style="font-weight: 400;">By now, you are fully aware that React is one of the most popular JavaScript frameworks, and its popularity increases on a day-by-day basis as more people dive into software development. </span><span style="font-weight: 400;"></span></p> <p><span style="font-weight: 400;">Starting out as a “newbie” developer can be very overwhelming, as there is tons of information out there at your disposal. So what should you learn before React.js? Here’s a list of the top 5 things:</span><span style="font-weight: 400;"></span></p> <ul> <li style="font-weight: 400;" aria-level="1"><a href="https://www.w3schools.com/html/html_intro.asp" rel="nofollow noopener" target="_blank"><span style="font-weight: 400; color: #3d2dd3;"><b>HTML </b></span></a></li> <li style="font-weight: 400;" aria-level="1"><a href="https://www.w3schools.com/css/" rel="nofollow noopener" target="_blank"><span style="font-weight: 400; color: #3d2dd3;"><b>CSS</b></span></a></li> <li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Fundamentals of </span><a href="https://www.w3schools.com/js/" rel="nofollow noopener" target="_blank"><span style="font-weight: 400; color: #3d2dd3;"><b>JavaScript</b></span></a><span style="font-weight: 400;"> (ES6 and above)</span></li> <li style="font-weight: 400;" aria-level="1"><a href="https://reactjs.org/docs/introducing-jsx.html" rel="nofollow noopener" target="_blank"><span style="font-weight: 400; color: #3d2dd3;"><b>JSX</b></span></a></li> <li style="font-weight: 400;" aria-level="1"><a href="https://git-scm.com/" rel="nofollow noopener" target="_blank"><span style="font-weight: 400; color: #3d2dd3;"><b>Git</b></span></a><span style="font-weight: 400;"> and </span><a href="https://github.com/" rel="nofollow noopener" target="_blank"><span style="font-weight: 400; color: #3d2dd3;"><b>GitHub</b></span></a></li> </ul> </div> </div> <div itemscope="" itemtype="https://schema.org/Question"> <h3 itemprop="name"><span style="font-weight: 400;">Why should I learn React?</span></h3> <div itemprop="acceptedAnswer"> <p>Learning React is like holding a master key to different frameworks and libraries. Its flexibility makes it possible to be used for not just web applications but mobile, desktop, and AR/VR apps as well. It is the backbone or fundamental part of powerful tools like Next.js – a full stack framework for building CSR, SSR, and SSG apps; React Native – a framework for building apps for Android, IOS, Desktop, and other platforms; ViroReact – a library for building AR/VR experience, etc. So by learning React, you are also equipping yourself with the knowledge to quickly pick up other useful tools as well which might be required as you progress in your development journey. </p> <p>Additionally, React is popular, so it has an active community of developers who create helpful tools to solve problems and ease development. This means that many companies use React, making knowledge of it a valuable skill in the job market.</p> </div> </div> <div itemscope="" itemtype="https://schema.org/Question"> <h3 itemprop="name"><span style="font-weight: 400;">Can a beginner learn React?</span></h3> <div itemprop="acceptedAnswer"> <p><span style="font-weight: 400;">React is easier to learn and more beginner-friendly compared to <a href="/angular-cms/" rel="follow">Angular </a>and <a href="/vuejs-cms/" rel="follow">Vue</a>. React uses JSX which has a syntax similar to HTML and JavaScript, making it beginner-friendly if you already have basic knowledge of HTML, CSS, and JavaScript. One of the best ways to learn React is by building projects and practicing regularly.</span><span style="font-weight: 400;"></span></p> <h3><span style="font-weight: 400;">How to create a blog with React and Firebase?</span></h3> <p>To do this, you can start by creating your blog’s front-end along with an interface protected by authentication to manage your blog content. The content management interface could be part of the front-end or a separate domain. Alternatively, rather than creating a content management interface from scratch, ButterCMS can be used, which also means it can be used as your database. Continuing from where we left off, head over to your <a href="https://console.firebase.google.com/" rel="nofollow noopener" target="_blank">Firebase Console</a>, add a new project, create a Firestore database, and set up authentication by enabling a sign-in method. Next, configure your newly created Firebase project for the web and copy the configurations you are given to your clipboard. Finally, head over to your project, install the Firebase SDK, initialize Firebase using the copied configurations, and then implement the needed functionalities. </p> <h3><span style="font-weight: 400;">How do I create a full-stack website using React?</span></h3> <p>To build a full-stack website with React, it is recommended to use a React-powered framework like <a href="https://nextjs.org/" rel="nofollow noopener" target="_blank">Next.js</a> or <a href="/blog/what-is-react-remix/" rel="follow">Remix</a>. These frameworks allow you to write both your front-end and back-end in a single codebase, saving you the time and effort of deploying and maintaining a separate backend server.</p> </div> </div> <h2 id="closing-thoughts"><span style="font-weight: 400;">Closing thoughts</span></h2> <p><span style="font-weight: 400;">In this article, we learned how to build a blog with React. We also learned how to set up a blog web application with React, and manage the React blog’s content using </span><span style="color: #000000;"><a rel="follow" style="color: #000000;"><span style="font-weight: 400;">ButterCMS</span></a></span><span style="font-weight: 400;">. We also fetched data from the CMS to be displayed in the application. An on-site search feature to easily sort content based on its category was also built into our React blog application. </span><span style="font-weight: 400;"></span></p> <p><span style="font-weight: 400;">I will love to know what else you would want to Build with React and ButterCMS. You can access the code for this tutorial in this <a href="https://github.com/ButterCMS/build-a-blog-with-react-and-butter" rel="nofollow noopener" target="_blank" span="" style="font-weight: 400;">Github repo.</a></span><span style="font-weight: 400;"></span></p> <p>For more articles on using React, read:</p> <ul> <li><span style="color: #3d2dd3;"><a href="/blog/dynamic-react-ecommerce/">Building a React Ecommerce Application</a></span></li> <li><span style="color: #3d2dd3;"><a href="/blog/web-analytics-tools-react-websites/">Web Analytics Tools for React Websites</a></span></li> <li><span style="color: #3d2dd3;"><a href="/blog/best-practices-for-building-a-large-scale-react-application/">Best Practices for Building Large-Scale Applications</a></span></li> <li><span style="color: #3d2dd3;"><a href="/blog/react-best-practices-maintaining-large-scale-projects/">React Best Practices: Maintaining Large Scale Projects</a></span></li> <li><span style="color: #3d2dd3;"><a href="/blog/react-firebase-google-analytics-set-up-log-events/">Firebase and Google Analytics: Set Up and Log Events</a></span></li> <li><a href="/blog/create-react-food-ordering-app/" rel="follow">Building a Food Ordering App with React</a></li> </ul> <p>Or check out our React starter projects, our react headless CMS and blog engine.</p> <ul> <li><span style="color: #3d2dd3;"><a href="/starters/react-starter-project/">React Starters</a></span></li> <li><span style="color: #3d2dd3;"><a class="anchor_text_cta" href="/react-cms/">React CMS</a></span></li> <li><span style="color: #3d2dd3;"><a href="/react-blog-engine/">React Blog Engine</a></span></li> </ul> <p>Post updated May 2024 by <a href="/blog/author/taminoturoko-briggs/" rel="follow">Taminoturoko Briggs</a>.</p> </div> </div> <div class="heading--margin-top heading--margin-bottom blog-post-page__author-heading palette-bg-main-tl-primary-dark" style="--heading-font-size:var(--heading-4-font-size);" data-astro-cid-g5xfp254="true" data-v-6b9b1130><!--[--><!----><!--]--><div class="title title--4" data-v-6b9b1130><!--[--><h4 class="title--internal" data-v-6b9b1130>Author</h4><!--]--><!----></div><!--[--><!----><!--]--></div> <astro-island uid="Z1wrHu7" prefix="s5" component-url="/_astro/TAuthorInfo.B0ckT7VZ.js" component-export="default" renderer-url="/_astro/client.BvFgaP12.js" props="{"palette":[0,"palette-accordion-main"],"author":[0,{"name":[0,"Chisom Uma"],"title":[0,"Software Developer"],"avatarSrc":[0,"https://cdn.buttercms.com/rHJQ8qeTSDSD5VsymjAi"],"bio":[0,"Chisom is a software developer and technical writer passionate about writing, and building front-end projects."],"slug":[0,"chisom-uma"],"linkedinUrl":[0,""],"twitterHandle":[0,""],"facebookUrl":[0,""],"href":[0,"/blog/author/chisom-uma/"]}],"text":[0,"Chisom is a software developer and technical writer passionate about writing, and building front-end projects."],"socialLinks":[1,[]],"expanded":[0,true],"border":[0,true],"class":[0,"blog-post-page__author-info"],"data-astro-cid-g5xfp254":[0,true]}" ssr client="visible" opts="{"name":"TAuthorInfo","value":{"rootMargin":"100px"}}" await-children><div class="accordion palette-accordion-main accordion--expanded accordion--border blog-post-page__author-info" data-astro-cid-g5xfp254="true" data-v-de13b56d data-v-35e99895><button class="accordion__header" data-v-35e99895><!--[--><div style="--max-name-title-width:none;" data-v-de13b56d data-v-ab490e41><a href="/blog/author/chisom-uma/" class="author__profile" data-v-ab490e41><div data-v-ab490e41><div class="avatar avatar--size-lg avatar--shape-circle" data-v-ab490e41 data-v-ce2c847c><img src="https://cdn.buttercms.com/rHJQ8qeTSDSD5VsymjAi" alt="Chisom Uma" class="avatar-image" data-v-ce2c847c></div></div><div data-v-ab490e41><div class="text text--size-md text--weight-medium text--no-margin author__name" data-v-ab490e41 data-v-b47df0cd><!--[-->Chisom Uma<!--]--></div><div class="text text--size-md text--weight-regular text--no-margin author__title author__title--separator" data-v-ab490e41 data-v-b47df0cd><!--[-->Software Developer<!--]--></div></div></a><!----></div><!--]--><span class="accordion__icon accordion__icon--expanded" data-v-35e99895><i class="pi pi-chevron-down"></i></span></button><div class="accordion__content-wrapper" id="accordion-content-default" style="--content-max-height:0px;" data-v-35e99895><div class="accordion__content" data-v-35e99895><div class="rich-text accordion__rich-text" data-v-35e99895>Chisom is a software developer and technical writer passionate about writing, and building front-end projects.</div><!--[--><div class="author-info__social" data-v-de13b56d><!--[--><!--]--></div><!--]--></div></div></div><!--astro:end--></astro-island> <astro-island uid="ZpwGX4" prefix="s6" component-url="/_astro/BlogPostFooter.D8TMgp1z.js" component-export="default" renderer-url="/_astro/client.BvFgaP12.js" props="{"blogPosts":[1,[[0,{"slug":[0,"astro-tutorial-knowledge-base"],"author":[0,{"name":[0,"Miracle Onyenma"],"title":[0,""],"avatarSrc":[0,"https://cdn.buttercms.com/FntrvXTJ6DoL0L6Dm0Lg"],"bio":[0,"Miracle Onyenma is a designer and front-end developer obsessed with crafting and sharing beautiful experiences. ✨"],"slug":[0,"miracle-onyenma"],"linkedinUrl":[0,""],"twitterHandle":[0,""],"facebookUrl":[0,""],"href":[0,"/blog/author/miracle-onyenma/"]}],"categories":[1,[[0,{"name":[0,"GSD"],"slug":[0,"gsd"],"href":[0,"/blog/category/gsd/"]}]]],"tags":[1,[[0,{"name":[0,"astro"],"slug":[0,"astro"]}],[0,{"name":[0,"buttercms"],"slug":[0,"buttercms"]}],[0,{"name":[0,"knowledge base"],"slug":[0,"knowledge-base"]}]]],"published":[3,"2025-10-22T15:41:00.000Z"],"updated":[3,"2025-10-24T14:47:37.282Z"],"featuredImageSrc":[0,"https://cdn.buttercms.com/qhP0NLPoT2iwAzTlpq76"],"featuredImageAlt":[0,"Featured Image: Astro Tutorial - How to Build a Knowledge Base"],"title":[0,"How to build a knowledge base with Astro and ButterCMS"],"body":[0,"<p>It’s important to have a <a href=\"https://buttercms.com/blog/tag/knowledge-base/\" rel=\"follow noopener\" target=\"_blank\"><strong>knowledge base</strong></a> that allows users to instantly access answers, guides, help articles, or product docs whenever they need them. It saves time for your support team and gives users a smooth self-service experience.</p>\n<p dir=\"ltr\"><br>In this piece, we’ll show you how to build a fast and flexible knowledge base using ButterCMS and <a href=\"https://buttercms.com/blog/build-astro-blog-using-buttercms/\" rel=\"follow noopener\" target=\"_blank\"><strong>Astro</strong></a>. Let’s get started!</p>\n<div class=\"section__content\">\n<div class=\"section__content-aside\">\n<h4>Table of contents</h4>\n</div>\n<div class=\"section__content-main\">\n<ul>\n<li><a href=\"#understanding-astro\" rel=\"follow\">Understanding Astro</a></li>\n<li><a href=\"#how-we-will-be-using-buttercms-to-power-our-knowledge-base\" rel=\"follow\">How to use ButterCMS to power your knowledge base</a></li>\n<li><a href=\"#tutorial-prerequisites\" rel=\"follow\">Tutorial prerequisites</a></li>\n<li><a href=\"#configuring-knowledge-base-content-types-in-buttercms\" rel=\"follow\">Configuring knowledge base content types in ButterCMS</a></li>\n<li><a href=\"#adding-our-knowledge-base-content-to-buttercms\" rel=\"follow\">Adding your knowledge base content to ButterCMS</a></li>\n<li><a href=\"#obtaining-the-read-api-token\" rel=\"follow\">Obtaining the Read API Token</a></li>\n<li><a href=\"#setting-up-astro\" rel=\"follow\">Setting up Astro</a></li>\n<li><a href=\"#building-out-the-knowledge-base\" rel=\"follow\">Building out the knowledge base</a></li>\n<li><a href=\"#creating-pages\" rel=\"follow\">Creating pages</a></li>\n<li><a href=\"#deploying-to-netlify\" rel=\"follow\">Deploying to Netlify</a></li>\n<li><a href=\"#final-results\" rel=\"follow\">Final results</a></li>\n<li><a href=\"#closing-thoughts\" rel=\"follow\">Closing thoughts</a></li>\n</ul>\n</div>\n</div>\n<h2 dir=\"ltr\" id=\"understanding-astro\"><span style=\"font-weight: 400;\">Understanding Astro</span></h2>\n<p><a href=\"https://buttercms.com/astro-cms/\"><strong>Astro</strong></a> is a <a href=\"https://buttercms.com/blog/what-is-a-static-site/\"><strong>static site</strong></a> builder that helps developers create fast, modern websites. It supports popular frameworks like <a href=\"https://buttercms.com/react-cms/\"><strong>React</strong></a>, <a href=\"https://buttercms.com/vuejs-cms/\"><strong>Vue</strong></a>, Svelte, and more, and also lets you mix standard <a href=\"https://buttercms.com/blog/how-to-add-a-cms-to-an-html-website/\"><strong>HTML </strong></a>and JavaScript with these frameworks as needed.</p>\n<p>There are various advantages of using Astro to create a knowledge base, including:</p>\n<ul>\n<li>\n<p><strong>Quick loading time:</strong> Astro creates pre-rendered HTML pages that can be served right away, which helps pages load faster and gives users a smoother experience.</p>\n</li>\n<li>\n<p><strong>Flexibility: </strong>Developers can create webpages with Astro using their preferred frameworks and tools, such as Markdown, React, and Vue. It is simple to incorporate different content management systems and APIs because of this versatility.</p>\n</li>\n<li>\n<p><strong>Simple maintenance: </strong>Because the Astro-generated site is pre-rendered, there is no need to handle server-side rendering or maintain databases. This makes maintenance easier and lowers the possibility of mistakes.</p>\n</li>\n<li>\n<p><strong>SEO optimization:</strong> Astro creates optimized HTML that has the necessary metadata and other information for search engines to understand the content. This increases the effectiveness of SEO and increases website traffic.</p>\n</li>\n<li>\n<p><strong>Community support:</strong> Astro is backed by a vibrant developer community that is always working to advance the framework. For programmers using Astro in their work, this community provides support and resources.</p>\n</li>\n</ul>\n<p>Overall, using Astro to build a knowledge base provides a powerful toolset that can be leveraged to create high-performance, flexible, and easy-to-maintain websites.</p>\n<p dir=\"ltr\"><a href=\"/astro-cms/\" rel=\"follow\"><span><img src=\"https://cdn.buttercms.com/3PQv93S8iiVIeUlLtUvA\" alt=\"See how Butter's simple content API works with your Astro app.\" style=\"display: block; margin-left: auto; margin-right: auto;\"></span></a></p>\n<h2 dir=\"ltr\" id=\"how-we-will-be-using-buttercms-to-power-our-knowledge-base\"><span style=\"font-weight: 400;\">How to use ButterCMS to power your knowledge base</span></h2>\n<p dir=\"ltr\"><span>The ButterCMS <a href=\"/blog/what-is-headless-cms/\" rel=\"follow noopener\" target=\"_blank\">headless content management system</a> provides a simple and user-friendly interface for creating and managing content. ButterCMS will be used to develop and manage the material for your knowledge base for this tutorial, and its <a href=\"/blog/graphql-vs-rest-api/\" rel=\"nofollow\">RESTful API</a> will be used to integrate that content into the Astro static site.</span></p>\n<p dir=\"ltr\"><span>Here are the core features of ButterCMS you'll use in this tutorial and why:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><strong>Content types:</strong><span style=\"font-weight: 400;\"><strong> </strong>ButterCMS allows you to create custom content types that can be used to structure your knowledge base content. This means you can create different types of content, such as articles, tutorials, and FAQs, and add fields that are specific to each type.</span></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><strong>Rich text editor</strong><span style=\"font-weight: 400;\"><strong>: </strong>ButterCMS provides a rich text editor that allows you to create and format content easily. You can add images, videos, and other media to your content and format it using styles, lists, and other formatting options.</span></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><strong>API access</strong><span style=\"font-weight: 400;\"><strong>:</strong> ButterCMS's RESTful API allows you to fetch content from your ButterCMS account and integrate it into your Astro static site. This means you can build dynamic pages that display the content from ButterCMS in real-time.</span></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><strong>Revisions and versioning:</strong><span style=\"font-weight: 400;\"> ButterCMS provides a revision history and version control system, which allows you to track changes to your content over time. This feature makes it easy to revert to previous versions of your content if needed.</span></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><strong>Search</strong><span style=\"font-weight: 400;\"><strong>:</strong> ButterCMS offers an integrated search feature accessible via API that enables users to look for information in your knowledge base. Users may quickly and easily access the information they need thanks to this functionality.</span></p>\n</li>\n</ul>\n<p dir=\"ltr\"><span>Overall, ButterCMS provides a powerful set of features that allows you to create and manage your knowledge base content efficiently and effectively. The API integration also enables you to deliver a dynamic and responsive experience for your users, making it an excellent choice for powering your knowledge base.</span></p>\n<h2 dir=\"ltr\" id=\"tutorial-prerequisites\"><span style=\"font-weight: 400;\">Tutorial prerequisites</span></h2>\n<p dir=\"ltr\"><span>To follow along, you should have the following:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A basic understanding of HTML, CSS, and JS</span></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><a href=\"/nodejs-cms/\" rel=\"follow noopener\" target=\"_blank\"><span>Node.js</span></a><span> </span><strong><a href=\"https://nodejs.org/en/download/\" rel=\"nofollow noopener\" target=\"_blank\">(latest LTS version)</a></strong></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><a href=\"https://code.visualstudio.com/\" rel=\"nofollow noopener\" target=\"_blank\"><span style=\"font-weight: 400;\"><strong>Visual Studio Code</strong></span></a></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A ButterCMS account—</span><strong><a href=\"/join/\" rel=\"follow noopener\" target=\"_blank\">get started here</a></strong></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A terminal—I recommend using VS Code’s integrated terminal</span></p>\n</li>\n</ul>\n<h2 dir=\"ltr\" id=\"configuring-knowledge-base-content-types-in-buttercms\"><span style=\"font-weight: 400;\">Configuring knowledge base content types in ButterCMS</span></h2>\n<p dir=\"ltr\"><span>In order to build out your knowledge base content in Butter, you’ll need to create a few page types and collections.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Set up a knowledge base article page type</span></h3>\n<p dir=\"ltr\"><span>This will contain the structure for a knowledge base article page. Go to the </span><strong>New Page Type</strong><span> page by clicking on the </span><span>“<strong>+</strong>”</span><span> icon in the </span><strong>Page Types</strong><span> option on the </span><strong>Content Types</strong><span> drop-down from the side menu.</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/CfhKu8E4Q4G22J4uLkZk\" alt=\"Select Page Types from the Content Types menu\" width=\"837\" height=\"438\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Now, you can create your page type structure, which includes:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>short text</strong> field with the name “Title” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>long text</strong> field with the name “Description” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>WYSIWYG</strong> field with the name “Content” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<p dir=\"ltr\"><span>With that, you should have something like this:</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/yQlLgPsmTpG4G3U3aA2Q\" alt=\"KB Article page type configuration\" width=\"836\" height=\"414\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Now, in order to save this page type to be used in your project, click the </span><strong>Create Page Type</strong><span> button at the top right of the page. This shows a modal where you can enter the page type name. Name it “KB Article” and click </span><strong>Save as Page Type</strong><span>.</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/NGinEIVnQqiZJ6d38GOL\" alt=\"Name Page KB Article \" width=\"826\" height=\"409\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Add Categories using Collections</span></h3>\n<p dir=\"ltr\"><span>The collection content type in ButterCMS is pretty handy and </span><a href=\"/kb/use-cases-for-buttercms-collections/\" rel=\"nofollow\"><span>has several use cases</span></a><span>. One popular use case that applies to your project is using it to create page facets to group/filter content. With this, you’ll be able to add categories to your article pages.</span></p>\n<p dir=\"ltr\"><span>To create your collection, first, navigate to the </span><strong>New Collection Configuration</strong><span> page by clicking the </span><span>“<strong>+</strong>”</span><span> icon in the </span><span><strong>Collection</strong>s</span><span> option on the </span><strong>Content Types</strong><span> drop-down from the side menu.</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/pJ6LEkqbSsg2uoFDmztP\" alt=\"Select collections from Content Types menu\" width=\"826\" height=\"409\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Now you can create the collection structure which includes:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>short text</strong> field with the name “Name” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>short tex</strong>t field with the name “Slug” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>long text</strong> field with the name “Description”</span></p>\n</li>\n</ul>\n<p dir=\"ltr\"><span>With that, you should have something like this:</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/dN2S8eMnTsm6CTrpesrr\" alt=\"KB Category collection configuration\" width=\"827\" height=\"433\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>To save, click on the </span><strong>Create Collection</strong><span> button at the top of the page and enter “KB Category” as the </span><span><strong>Collection Name</strong>.</span></p>\n<p dir=\"ltr\"><span>Now that you've created your category collection, you can add it as a reference in your </span><strong>KB Article</strong><span> page type. This will allow you to filter the pages using the category field in the API.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Add a category reference to the KB Article page type</span></h3>\n<p dir=\"ltr\"><span>Navigate to the </span><strong>Page Types Content Types</strong><span> page by clicking on the </span><strong>Page Types</strong><span> option in the </span><strong>Content Types</strong><span> drop-down from the side menu:</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/CfhKu8E4Q4G22J4uLkZk\" alt=\"Select Page Types from the Content Types menu\" width=\"837\" height=\"438\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Now, open the </span><strong>KB Article</strong><span> page type configuration by clicking on it. In the </span><strong>KB Article</strong><span> page, add a new </span><strong>reference</strong><span> field with the name “Category” and the following attributes:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><strong>What will this reference? - “KB Category”</strong></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\"><strong>Reference type - One-to-Many</strong>: This is so that one article can have more than one category. If your use case requires an article to be under only one category, you can select the One-to-One relationship type.</span></p>\n</li>\n</ul>\n<p dir=\"ltr\"><span>Your page type structure should look like this now:</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/RETWSIyMROLABSjVcJXw\" alt=\"Add reference for KB Category collection to KB Article Page Type\" width=\"836\" height=\"414\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>With that, click on the </span><strong>Save</strong><span> button to save the changes. In the next section, you’ll create another collection type for FAQs.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create a FAQ collection type</span></h3>\n<p dir=\"ltr\"><span>Navigate to the <strong>C</strong></span><strong>ollection Type</strong><span> page by clicking on the </span><span>“<strong>+</strong>”</span><span> icon in the </span><strong>Collections</strong><span> option on the </span><strong>Content Types</strong><span> drop-down from the side menu.</span></p>\n<p dir=\"ltr\"><span>Now, you can create your collection structure which includes:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>short text</strong> field with the name “Question” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>WYSIWYG</strong> field with the name “Answer” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>reference</strong> field with the name “Category” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><strong>What will this reference? - “KB Category”</strong></p>\n</li>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><strong>Reference type - One-to-one</strong></p>\n</li>\n</ul>\n</li>\n</ul>\n<p dir=\"ltr\"><span>With that, you should have something like this:</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/OEGXDHW2QNqS4Rw0JBNh\" alt=\"KB FAQ collection configuration\" width=\"838\" height=\"415\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>To save, click the </span><strong>Create Collection</strong><span> button at the top of the page and enter “KB FAQ” as the </span><span><strong>Collection Nam</strong>e.</span><span> Next, you’ll create the page structure for your knowledge base landing page.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create a dynamic landing page with components</span></h3>\n<p dir=\"ltr\"><span>Now, you’ll create the structure of your knowledge base landing page. First, create a new page. Go to the </span><strong>New Page Type</strong><span> page by clicking on the </span><span>“<strong>+</strong>”</span><span> icon in the </span><strong>Page Types</strong><span> option on the </span><strong>Content Types</strong><span> drop-down from the side menu.</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/CfhKu8E4Q4G22J4uLkZk\" alt=\"Select Page Types from the Content Types menu\" width=\"837\" height=\"438\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Now, you’ll add a few components. First, add the </span><strong>SEO</strong><span> component by clicking on the “</span><span><strong>Book</strong>”</span><span> icon for the </span><strong>Component Library</strong><span> option on the left menu:</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/KdswLETnQl61FFOKvJwq\" alt=\"Add SEO component with component library\" width=\"843\" height=\"474\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Great. Next, you’ll create another component—</span><strong>KB Landing Hero</strong><span>—by clicking on the </span><strong>Component</strong><span><strong> </strong>option and clicking on </span><strong>Create Component.</strong><span><br></span><span> Enter the name and description of the component as:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Name: “KB Landing Hero”</span></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Description: “Hero component for knowledge base landing page”</span></p>\n</li>\n</ul>\n<p dir=\"ltr\"><span>You can create the following fields for your component:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>short text</strong> field with the name “Caption” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A<strong> long text</strong> field with the name “Text” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>short text </strong>field with the name “Search field placeholder”</span></p>\n</li>\n</ul>\n<p dir=\"ltr\"><span>With that, it should look something like this:</span></p>\n<p><img src=\"https://cdn.buttercms.com/MhImmBSoQk67y0vP36qS\" alt=\"KB Landing Hero component configuration\" width=\"829\" height=\"434\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Click </span><span>Done.</span></p>\n<p dir=\"ltr\"><span>Next, you’ll add a </span><strong>Components Picker</strong><span> field where you can add multiple components. Click on the </span><strong>Component Picker</strong><span> option on the left menu to create a new field and name it </span><span><strong>Sections</strong>:</span></p>\n<p><img src=\"https://cdn.buttercms.com/y560Xyv2QCSdqdtKHiqJ\" alt=\"Add component picker\" width=\"839\" height=\"439\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Now, you can start creating components that will be used.</span></p>\n<p dir=\"ltr\"><span>Click on </span><strong>Create Component</strong><span> to create a new component and enter the name and description as:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Name: “KB Featured Categories”</span></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Description: “List of categories to be featured on the landing page”</span></p>\n</li>\n</ul>\n<p dir=\"ltr\"><span>Next, add the fields for your component:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>short text</strong> field with the name “Caption” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>reference</strong> field with the name “Categories” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><strong>What will this reference? - “KB Category”</strong></p>\n</li>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><strong>Reference type - One-to-Many</strong></p>\n</li>\n</ul>\n</li>\n</ul>\n<p dir=\"ltr\"><span>It should look something like this:</span></p>\n<p><img src=\"https://cdn.buttercms.com/YhUmtb2SIa47THdwZdKq\" alt=\"KB Featured Categories component configuration\" width=\"848\" height=\"444\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Click </span><strong>Done</strong><span> to save the component.</span></p>\n<p dir=\"ltr\"><span>Next, create another component for featured articles.</span></p>\n<p dir=\"ltr\"><span>Click on </span><strong>Create Component</strong><span> to create a new component and enter the name and description as:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Name: “KB Featured Articles”</span></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Description: “List of articles to be featured on the landing page”</span></p>\n</li>\n</ul>\n<p dir=\"ltr\"><span>Next, add the fields for your component:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>short text</strong> field with the name “Caption” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>reference</strong> field with the name “Categories” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><strong>What will this reference? - “KB Article”</strong></p>\n</li>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><strong>Reference type - One-to-Many</strong></p>\n</li>\n</ul>\n</li>\n</ul>\n<p dir=\"ltr\"><span>It should look like this:</span></p>\n<p><img src=\"https://cdn.buttercms.com/g307l1AISjmoQPGGdsN3\" alt=\"KB Featured Articles component configuration\" width=\"825\" height=\"432\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Click </span><strong>Done</strong><span> to save the component.</span></p>\n<p dir=\"ltr\"><span>Lastly, you’ll create the </span><strong>FAQs</strong><span> component that will display your knowledge base FAQs.</span></p>\n<p dir=\"ltr\"><span>Click on </span><strong>Create Component</strong><span> to create a new component and enter the name and description as:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Name: “KB FAQs”</span></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Description: “Knowledge base FAQs”</span></p>\n</li>\n</ul>\n<p dir=\"ltr\"><span>Next, add the fields for your component:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>short text </strong>field with the name “Caption” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">Required - ✅ True</span></p>\n</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><span style=\"font-weight: 400;\">A <strong>reference</strong> field with the name “Categories” and the following attributes:</span></p>\n</li>\n</ul>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><strong>What will this reference? - “KB FAQ”</strong></p>\n</li>\n<li dir=\"ltr\" aria-level=\"2\">\n<p dir=\"ltr\" role=\"presentation\"><strong>Reference type - One-to-Many</strong></p>\n</li>\n</ul>\n</li>\n</ul>\n<p dir=\"ltr\"><span>It should look like this:</span></p>\n<p><img src=\"https://cdn.buttercms.com/vwQgK8teR3V53H16lLCn\" alt=\"KB FAQ component configuration\" width=\"839\" height=\"439\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Click </span><strong>Done</strong><span> to save the component.</span></p>\n<p>To save the page type, click on <strong>Create Page Type</strong>. Then enter the page type name as “KB Landing Page”. With that, here’s what your landing page type should look like:</p>\n<p><img src=\"https://cdn.buttercms.com/ptOCUewPQouRPSB0TR19\" alt=\"KB Landing Page configuration\" width=\"834\" height=\"824\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Awesome.</span></p>\n<h2 dir=\"ltr\" id=\"adding-our-knowledge-base-content-to-buttercms\"><span style=\"font-weight: 400;\">Adding your knowledge base content to ButterCMS</span></h2>\n<p dir=\"ltr\"><span>Now that you’ve defined the structure of your knowledge base, create some content. Start with the categories as they will be referenced in our articles and FAQs.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create categories</span></h3>\n<p dir=\"ltr\"><span>To create a new collection, you can click on the </span><span>“<strong>+</strong>”</span><span> icon on the </span><strong>KB Category</strong><span> option in the </span><strong>Collections</strong><span> menu item on the sidebar. Or, navigate to the </span><code><span>/content/collections</span></code><span> page and click on the </span><strong>New Item</strong><span> button at the top right of the page and select the </span><strong>KB Category</strong><span> option:</span></p>\n<p><img src=\"https://cdn.buttercms.com/Db8lxLKHT6WdFIXP3JUZ\" alt=\"Create KB Category collection item\" width=\"833\" height=\"436\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Now fill in the fields with the category name and slug and click on </span><strong>Publish</strong><span> to save changes. Repeat these steps and create multiple categories. Here are a few I created:</span></p>\n<p><img src=\"https://cdn.buttercms.com/MUWOJwHiS1qRz8QLS73G\" alt=\"Created KB Category collection items in ButterCMS dashboard\" width=\"824\" height=\"408\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create knowledge base articles</span></h3>\n<p dir=\"ltr\"><span>To create a new KB article, you can click on the </span><span>“<strong>+</strong>”</span><span> icon on the </span><strong>KB Article</strong><span> option in the </span><strong>Pages</strong><span> menu item on the sidebar. Or, navigate to the </span><span>/pages</span><span> page and click on the </span><strong>New Page</strong><span> button at the top right of the page and select the </span><strong>KB Article</strong><span> option.</span></p>\n<p><img src=\"https://cdn.buttercms.com/136AuXRGerQ945Si4l1A\" alt=\"Create a page using KB Article page type\" width=\"830\" height=\"411\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Next, you’ll enter the page metadata, including the page title and the API slug (automatically populated) of your new KB article.</span></p>\n<p><img src=\"https://cdn.buttercms.com/AeLEuWoCQ4i7ZydNPEL5\" alt=\"Name individual kb article page\" width=\"848\" height=\"420\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Next, you’ll enter the name, description, and content of your article and then select a category reference.</span></p>\n<p><img src=\"https://cdn.buttercms.com/eT2JpjzaSZaGOficin3v\" alt=\"Add category collection items to article via references\" width=\"863\" height=\"426\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Once you’ve added the content for your article, click on </span><strong>Publish</strong><span> to save and publish the article. Repeat these steps and create multiple articles. Here are a few I created:</span></p>\n<p><img src=\"https://cdn.buttercms.com/CYdjKM0Q0uvOjCzJQov0\" alt=\"Created KB articles shown in ButterCMS\" width=\"836\" height=\"414\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Next, you’ll create a few FAQs.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create FAQs</span></h3>\n<p dir=\"ltr\"><span>To create a new collection, you can click on the </span><span>“<strong>+</strong>”</span><span> icon in the </span><strong>KB FAQs</strong><span> option in the </span><strong>Collections</strong><span> menu item on the sidebar. Or, navigate to the /collections page and click on the </span><strong>New Collection</strong><span> button at the top right of the page and select the </span><strong>KB FAQs</strong><span> option:</span></p>\n<p><img src=\"https://cdn.buttercms.com/jmK8xJyxRMqRWbFHh1Vk\" alt=\"Select KB FAQ collection to start adding items\" width=\"830\" height=\"411\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>In the </span><strong>Add Item to KB FAQs</strong><span> page, enter the question and answer for the FAQ and category references to previously created KB categories:</span></p>\n<p><img src=\"https://cdn.buttercms.com/308vgH5bTea5i9mOSyhu\" alt=\"Add content to KB FAQ items\" width=\"842\" height=\"417\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>As usual, click on </span><strong>Publish</strong><span> to save and publish the FAQ. Repeat these steps to create multiple FAQs. Here are a few of mine:</span></p>\n<p><img src=\"https://cdn.buttercms.com/X5Hoz0RbRXiNr9sMUAkA\" alt=\"KB FAQ collection items in ButterCMS \" width=\"860\" height=\"450\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Next, you’ll create your knowledge base landing page.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the knowledge base landing page</span></h3>\n<p dir=\"ltr\"><span>To create the KB landing page, click on the </span><span>“<strong>+</strong>”</span><span> icon on the<strong> </strong></span><strong>KB Landing Page</strong><span> option in the </span><strong>Pages</strong><span> menu item on the sidebar. Or, navigate to the </span><code><span>/pages</span></code><span> page and click on the </span><strong>New Page</strong><span> button at the top right of the page and select the </span><strong>KB Landing Page</strong><span> option.</span></p>\n<p dir=\"ltr\"><span>Now, enter the </span><strong>Page Title</strong><span> as “KB Home Page” and click on </span><span><strong>Save Page Metadata</strong>.</span><span> Next, enter content for </span><strong>SEO, </strong><span><strong>KB Landing Hero</strong>,</span><span> and </span><span><strong>Sections</strong>.</span><span> Here’s a quick look at what mine looks like:</span></p>\n<p><img src=\"https://cdn.buttercms.com/mn0hEvnrSQWC5nyjGEdF\" alt=\"Add content to SEO, Hero, and Sections components\" width=\"841\" height=\"440\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>For </span><strong>Sections</strong><span>, you have the </span><span><strong>KB Featured Categories</strong>:</span></p>\n<p><img src=\"https://cdn.buttercms.com/e9M1U6NRfmYJXg6Ip1rw\" alt=\"Add content to KB Featured Categories section\" width=\"833\" height=\"436\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span><strong>KB Featured Articles</strong>:</span></p>\n<p><img src=\"https://cdn.buttercms.com/ks8VLxAXTGmhTSwrY8oN\" alt=\"Add content to KB Featured Articles\" width=\"833\" height=\"436\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>And, </span><span><strong>KB FAQs</strong>:</span></p>\n<p><img src=\"https://cdn.buttercms.com/XZspkSTMTaahaRFYvwgZ\" alt=\"Add content to KB FAQs section\" width=\"831\" height=\"435\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Good. Now click on </span><strong>Publish</strong><span> to save and publish the page.</span></p>\n<p dir=\"ltr\"><span>Now that that’s all done and your content is set up, you can create and integrate your Astro front-end. You’ll need to obtain your token first.</span></p>\n<h2 dir=\"ltr\" id=\"obtaining-the-read-api-token\"><span style=\"font-weight: 400;\">Obtaining the Read API Token</span></h2>\n<p dir=\"ltr\"><span>First, navigate to the </span><strong>Butter dashboard</strong><span> and then to </span><strong>Settings</strong><span> in order to obtain the Read API Token for API integration on the Astro site.</span></p>\n<p><img src=\"https://cdn.buttercms.com/K5uhgCCYT1SvrkGbZchP\" alt=\"Access API token in account settings\" width=\"830\" height=\"451\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Great.</span></p>\n<h2 dir=\"ltr\" id=\"setting-up-astro\"><span style=\"font-weight: 400;\">Setting up Astro</span></h2>\n<p dir=\"ltr\"><span>Create the front-end for your project. As mentioned earlier, you’ll use the Astro framework. To </span><a href=\"https://docs.astro.build/en/install/auto/\" rel=\"nofollow noopener\" target=\"_blank\"><span>create a new project</span></a><span>, run:</span></p>\n<pre><code class=\"hljs language-sql\">npm <span class=\"hljs-keyword\">create</span> astro<span class=\"hljs-variable\">@latest</span>\n</code></pre>\n<p dir=\"ltr\"><span>This will take you through a few steps:</span></p>\n<pre><code class=\"hljs language-sql\">Need <span class=\"hljs-keyword\">to</span> install the following packages:\n <span class=\"hljs-keyword\">create</span><span class=\"hljs-operator\">-</span>astro<span class=\"hljs-variable\">@latest</span>\nOk <span class=\"hljs-keyword\">to</span> proceed? (y) y\n\n astro v2<span class=\"hljs-number\">.1</span><span class=\"hljs-number\">.3</span> Launch sequence initiated.\n\n dir <span class=\"hljs-keyword\">Where</span> should we <span class=\"hljs-keyword\">create</span> your <span class=\"hljs-keyword\">new</span> project?\n .<span class=\"hljs-operator\">/</span>astro<span class=\"hljs-operator\">-</span>butter<span class=\"hljs-operator\">-</span>kb\n\n tmpl How would you <span class=\"hljs-keyword\">like</span> <span class=\"hljs-keyword\">to</span> <span class=\"hljs-keyword\">start</span> your <span class=\"hljs-keyword\">new</span> project?\n Include sample files\n ✔ Template copied</code></pre>\n<p dir=\"ltr\"><span>After this step, you’ll be asked if you want to install dependencies, enter </span><code><span>y</span></code><span> to install, or </span><span><code>cancel</code> </span><span>to install later.</span></p>\n<p dir=\"ltr\"><span>Once the project has been created, navigate to the folder and install the dependencies:</span></p>\n<pre><code class=\"hljs language-bash\"><span class=\"hljs-built_in\">cd</span> astro-butter-kb/\nnpm install</code></pre>\n<p dir=\"ltr\"><span>Once the installation is complete, you can proceed to install the </span><a href=\"https://docs.astro.build/en/guides/integrations-guide/tailwind/\" rel=\"nofollow noopener\" target=\"_blank\"><span>Tailwind integration for Astro</span></a><span> so you can build with Tailwind:</span></p>\n<pre><code class=\"hljs language-csharp\">npx astro <span class=\"hljs-keyword\">add</span> tailwind</code></pre>\n<p dir=\"ltr\"><span>This will take you through a few steps; just confirm each one. Once Tailwind has been added, you can add the </span><a href=\"https://github.com/tailwindlabs/tailwindcss-forms\" rel=\"nofollow noopener\" target=\"_blank\"><span>forms plugin for Tailwind</span></a><span>. Install the plugin from npm:</span></p>\n<pre><code class=\"hljs language-bash\">npm install -D @tailwindcss/forms @tailwindcss/typography</code></pre>\n<p dir=\"ltr\"><span>Then add the plugin to your </span><code><span>tailwind.config.cjs</span></code><span> file:</span></p>\n<pre><code class=\"hljs language-lua\">// ./tailwind.<span class=\"hljs-built_in\">config</span>.cjs\n/** @<span class=\"hljs-built_in\">type</span> {import(<span class=\"hljs-string\">'tailwindcss'</span>).Config} */\n<span class=\"hljs-built_in\">module</span>.exports = {\n content: [<span class=\"hljs-string\">\"./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}\"</span>],\n theme: {\n extend: {},\n },\n plugins: [<span class=\"hljs-built_in\">require</span>(<span class=\"hljs-string\">\"@tailwindcss/forms\"</span>), <span class=\"hljs-built_in\">require</span>(<span class=\"hljs-string\">\"@tailwindcss/typography\"</span>)],\n};</code></pre>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Integrating ButterCMS</span></h3>\n<p dir=\"ltr\"><span>Now, you’ll create a </span><code><span>.env</span></code><span> file in the root of your project and add your API token as an environment variable:</span></p>\n<pre><code class=\"hljs language-bash\">// .<span class=\"hljs-built_in\">env</span>\nBUTTER_TOKEN=YOUR_API_TOKEN_HERE</code></pre>\n<p dir=\"ltr\"><span>Next, you’ll install the ButterCMS SDK as a dependency:</span></p>\n<pre><code class=\"hljs language-undefined\">npm install buttercms</code></pre>\n<p dir=\"ltr\"><span>Once installed, you’ll create a </span><code><span>buttercms.js</span></code><span> file in a new </span><code><span>src/lib/</span></code><span><code> </code>directory in your project:</span></p>\n<pre><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">// ./src/lib/buttercms.js</span>\n\n<span class=\"hljs-keyword\">import</span> <span class=\"hljs-title class_\">Butter</span> <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">\"buttercms\"</span>;\n\n<span class=\"hljs-keyword\">export</span> <span class=\"hljs-keyword\">const</span> butterClient = <span class=\"hljs-title class_\">Butter</span>(<span class=\"hljs-keyword\">import</span>.<span class=\"hljs-property\">meta</span>.<span class=\"hljs-property\">env</span>.<span class=\"hljs-property\">BUTTER_TOKEN</span>);\n\n</code></pre>\n<p dir=\"ltr\"><span>The exported </span><code><span>butterClient</span></code><span> initializes </span><span>Butter</span><span> using the </span><code><span>BUTTER_TOKEN</span></code><span> from your environment variable which authenticates your requests to the ButterCMS API.</span></p>\n<p dir=\"ltr\"><span>Great. Now, create a few components for your site.</span></p>\n<p dir=\"ltr\"><span>📝 Just a quick heads up! I’ll be omitting the styles for this project in order to reduce the lines of code in the article so it doesn’t seem overwhelming. You can always find it in the GitHub repo for this project.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the SiteHeader component</span></h3>\n<p dir=\"ltr\"><span>Create a new file called </span><code><span>./src/components/SiteHeader.astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-perl\">&<span class=\"hljs-keyword\">lt</span>;!-- ./src/components/SiteHeader.astro --&<span class=\"hljs-keyword\">gt</span>;\n&<span class=\"hljs-keyword\">lt</span>;header <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">header</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;a href=<span class=\"hljs-string\">\"/\"</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">logo</span> <span class=\"hljs-title\">text</span>-<span class=\"hljs-title\">purple</span>-<span class=\"hljs-number\">400</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;span <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">logo</span>-<span class=\"hljs-title\">text</span>\"&<span class=\"hljs-title\">gt</span></span>;Andromeda &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/span&gt;\n | Knowledge Base\n &lt;/di</span>v&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/a&gt;\n &lt;/di</span>v&<span class=\"hljs-keyword\">gt</span>;\n&<span class=\"hljs-keyword\">lt</span>;/header&<span class=\"hljs-keyword\">gt</span>;</code></pre>\n<p dir=\"ltr\"><span>Now, you’ll add this component to your already-created layout file in </span><code><span>./src/layouts/Layout.astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-xml\">---\n// ./src/layouts/Layout.astro\n\nimport SiteHeader from \"../components/SiteHeader.astro\";\n\nexport interface Props {\n title: string;\n description?: string;\n}\nconst { title, description } = Astro.props;\n---\n<span class=\"hljs-symbol\">&lt;</span>!DOCTYPE html<span class=\"hljs-symbol\">&gt;</span>\n<span class=\"hljs-symbol\">&lt;</span>html lang=\"en\"<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>head<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>meta charset=\"UTF-8\" /<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>meta name=\"viewport\" content=\"width=device-width\" /<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.svg\" /<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>meta name=\"generator\" content={Astro.generator} /<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>title<span class=\"hljs-symbol\">&gt;</span>{title}<span class=\"hljs-symbol\">&lt;</span>/title<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>meta name=\"description\" content={description} /<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>/head<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>body<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>SiteHeader /<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>slot /<span class=\"hljs-symbol\">&gt;</span>\n <span class=\"hljs-symbol\">&lt;</span>/body<span class=\"hljs-symbol\">&gt;</span>\n<span class=\"hljs-symbol\">&lt;</span>/html<span class=\"hljs-symbol\">&gt;</span></code></pre>\n<p dir=\"ltr\"><span>Here, the layout component is responsible for defining the structure of the application, including the meta, header, and content.</span></p>\n<p dir=\"ltr\"><span>The Props interface defines two props: </span><code><span>title</span></code><span> and </span><code><span>description</span></code><span>. These props are passed to the layout component and are used to set the <code>title</code> and <code>description</code> of the HTML document. The </span><span>title</span><span> and </span><span>description</span><span> props are destructured from the </span><span>Astro.props</span><span> object which contains all of the props passed to the component.</span></p>\n<p dir=\"ltr\"><span>In the </span><code><span><body></span></code>, <span>render the </span><code><span><SiteHeader></span></code><span> component and the </span><code><span><slot /></span></code><span> is where the page will be rendered.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Fetching data</span></h3>\n<p dir=\"ltr\"><span>To fetch page content, you’ll import the Butter client and use its </span><span>page.retrieve</span><span> function. In </span><code><span>./src/pages/index.astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-yaml\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-string\">//</span> <span class=\"hljs-string\">.src/pages/index.astro</span>\n<span class=\"hljs-string\">import</span> <span class=\"hljs-string\">Layout</span> <span class=\"hljs-string\">from</span> <span class=\"hljs-string\">\"../layouts/Layout.astro\"</span><span class=\"hljs-string\">;</span>\n<span class=\"hljs-string\">import</span> { <span class=\"hljs-string\">butterClient</span> } <span class=\"hljs-string\">from</span> <span class=\"hljs-string\">\"../lib/butter\"</span><span class=\"hljs-string\">;</span>\n<span class=\"hljs-string\">//</span> <span class=\"hljs-string\">retrieve</span> <span class=\"hljs-string\">the</span> <span class=\"hljs-string\">page</span> <span class=\"hljs-string\">data</span> <span class=\"hljs-string\">from</span> <span class=\"hljs-string\">ButterCMS</span> <span class=\"hljs-string\">by</span> <span class=\"hljs-string\">type</span> <span class=\"hljs-string\">and</span> <span class=\"hljs-string\">slug</span>\n<span class=\"hljs-string\">const</span> <span class=\"hljs-string\">response</span> <span class=\"hljs-string\">=</span> <span class=\"hljs-string\">await</span> <span class=\"hljs-string\">butterClient.page.retrieve(</span>\n <span class=\"hljs-string\">\"kb_landing_page\"</span><span class=\"hljs-string\">,</span>\n <span class=\"hljs-string\">\"kb-home-page\"</span>\n<span class=\"hljs-string\">);</span>\n<span class=\"hljs-string\">const</span> <span class=\"hljs-string\">pageData</span> <span class=\"hljs-string\">=</span> <span class=\"hljs-string\">response.data.data;</span>\n<span class=\"hljs-string\">console.log({pageData})</span>\n<span class=\"hljs-meta\">---</span>\n<span class=\"hljs-string\">&lt;Layout</span> <span class=\"hljs-string\">title={pageData.fields.seo.title}</span> <span class=\"hljs-string\">description={pageData.fields.seo.description}&gt;&lt;/Layout&gt;</span></code></pre>\n<p dir=\"ltr\"><span>Use the ButterCMS API to retrieve page data for your knowledge base landing page. First, import the </span><code><span>Layout</span></code><span> component and </span><code><span>butterClient</span></code><span> function.</span></p>\n<p dir=\"ltr\"><span>Then, make an asynchronous API request to ButterCMS to retrieve data for a specific page with the type </span><code><span>kb_landing_page</span></code><span> and the slug </span><code><span>kb-home-page</span></code><span>.</span></p>\n<p dir=\"ltr\"><span>You can see this in ButterCMS’s API Explorer:</span></p>\n<p><img src=\"https://cdn.buttercms.com/lLwgkDzSRLiPk9GoID6h\" alt=\"Review API Explorer\" width=\"827\" height=\"433\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>The response from the API is then stored in the </span><span>response</span><span> variable. The </span><code><span>console.log({pageData})</span></code><span> statement logs the retrieved page data to the console.</span></p>\n<p dir=\"ltr\"><span>Finally, the </span><span>Layout</span><span> component is rendered with </span><code><span>title</span></code><span> and </span><code><span>description</span></code><span> props passed in. These props are set to values retrieved from the </span><code><span>pageData</span></code><span> object, specifically the </span><code><span>fields.seo.title</span></code><span> and </span><code><span>fields.seo.description</span></code><span> properties. This allows the </span><code><span>Layout</span></code><span> component to dynamically render metadata for the page based on the data retrieved from the API.</span></p>\n<p dir=\"ltr\"><span>Now, when you run:</span></p>\n<pre><code class=\"hljs language-undefined\">npm run dev</code></pre>\n<p dir=\"ltr\"><span>You should see the </span><code><span>pageData</span></code><span> in your console:</span></p>\n<p><img src=\"https://cdn.buttercms.com/Xe1XSCehT12qnDvHyklf\" alt=\"PageData in console\" width=\"835\" height=\"472\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>And on </span><code><span>http://127.0.0.1:3000/</span></code><span>:</span></p>\n<p><img src=\"https://cdn.buttercms.com/FOogM2EDTwyYYDDwF7vu\" alt=\"First Page\" width=\"847\" height=\"488\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Great start!</span></p>\n<h2 dir=\"ltr\" id=\"building-out-the-knowledge-base\"><span style=\"font-weight: 400;\">Building out the knowledge base</span></h2>\n<p dir=\"ltr\"><span>Now, you’ll create the components and pages you need and build out your knowledge base. First, create components for your landing page and a couple of types to emulate the data ButterCMS provides.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create type definitions</span></h3>\n<p>In your <code>./src/lib/butter.ts</code> file, add the following interfaces:</p>\n<pre><code class=\"hljs language-yaml\"><span class=\"hljs-string\">//</span> <span class=\"hljs-string\">./src/lib/buttercms.ts</span>\n<span class=\"hljs-string\">import</span> <span class=\"hljs-string\">Butter</span> <span class=\"hljs-string\">from</span> <span class=\"hljs-string\">\"buttercms\"</span><span class=\"hljs-string\">;</span>\n<span class=\"hljs-string\">export</span> <span class=\"hljs-string\">const</span> <span class=\"hljs-string\">butterClient</span> <span class=\"hljs-string\">=</span> <span class=\"hljs-string\">Butter(import.meta.env.BUTTER_TOKEN);</span>\n<span class=\"hljs-string\">export</span> <span class=\"hljs-string\">interface</span> <span class=\"hljs-string\">kb_article</span> {\n <span class=\"hljs-attr\">slug:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">published:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">updated:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">page_type:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">fields:</span> {\n <span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">description:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">content:</span> <span class=\"hljs-string\">string;</span>\n }<span class=\"hljs-string\">;</span>\n}\n<span class=\"hljs-string\">export</span> <span class=\"hljs-string\">interface</span> <span class=\"hljs-string\">kb_category</span> {\n <span class=\"hljs-attr\">meta:</span> <span class=\"hljs-string\">object;</span>\n <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">description:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">slug:</span> <span class=\"hljs-string\">string;</span>\n}\n<span class=\"hljs-string\">export</span> <span class=\"hljs-string\">interface</span> <span class=\"hljs-string\">kb_featured_categories</span> {\n <span class=\"hljs-attr\">type:</span> <span class=\"hljs-string\">\"kb_featured_categories\"</span><span class=\"hljs-string\">;</span>\n <span class=\"hljs-attr\">fields:</span> {\n <span class=\"hljs-attr\">caption:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">categories:</span> [<span class=\"hljs-string\">kb_category</span>]<span class=\"hljs-string\">;</span>\n }<span class=\"hljs-string\">;</span>\n}\n<span class=\"hljs-string\">export</span> <span class=\"hljs-string\">interface</span> <span class=\"hljs-string\">kb_featured_articles</span> {\n <span class=\"hljs-attr\">type:</span> <span class=\"hljs-string\">\"kb_featured_articles\"</span><span class=\"hljs-string\">;</span>\n <span class=\"hljs-attr\">fields:</span> {\n <span class=\"hljs-attr\">caption:</span> <span class=\"hljs-string\">object;</span>\n <span class=\"hljs-attr\">articles:</span> [<span class=\"hljs-string\">kb_article</span>]<span class=\"hljs-string\">;</span>\n }<span class=\"hljs-string\">;</span>\n}\n<span class=\"hljs-string\">export</span> <span class=\"hljs-string\">interface</span> <span class=\"hljs-string\">kb_faqs</span> {\n <span class=\"hljs-attr\">type:</span> <span class=\"hljs-string\">\"kb_faqs\"</span><span class=\"hljs-string\">;</span>\n <span class=\"hljs-attr\">fields:</span> {\n <span class=\"hljs-attr\">caption:</span> <span class=\"hljs-string\">object;</span>\n <span class=\"hljs-attr\">faqs:</span> [\n {\n <span class=\"hljs-attr\">question:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">answer:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">category:</span> [<span class=\"hljs-string\">string</span>]<span class=\"hljs-string\">;</span>\n }\n ]<span class=\"hljs-string\">;</span>\n }<span class=\"hljs-string\">;</span>\n}\n<span class=\"hljs-string\">export</span> <span class=\"hljs-string\">interface</span> <span class=\"hljs-string\">kb_landing_hero</span> {\n <span class=\"hljs-attr\">caption:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">text:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">search_field_placeholder:</span> <span class=\"hljs-string\">string;</span>\n}\n<span class=\"hljs-string\">export</span> <span class=\"hljs-string\">interface</span> <span class=\"hljs-string\">kb_landing_page</span> {\n <span class=\"hljs-attr\">slug:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">published:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">updated:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">page_type:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">fields:</span> {\n <span class=\"hljs-attr\">seo:</span> {\n <span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">string;</span>\n <span class=\"hljs-attr\">description:</span> <span class=\"hljs-string\">string;</span>\n }<span class=\"hljs-string\">;</span>\n <span class=\"hljs-attr\">kb_landing_hero:</span> <span class=\"hljs-string\">kb_landing_hero;</span>\n <span class=\"hljs-attr\">sections:</span> [<span class=\"hljs-string\">kb_featured_articles</span> <span class=\"hljs-string\">|</span> <span class=\"hljs-string\">kb_featured_categories</span> <span class=\"hljs-string\">|</span> <span class=\"hljs-string\">kb_faqs</span>]<span class=\"hljs-string\">;</span>\n }<span class=\"hljs-string\">;</span>\n}</code></pre>\n<p dir=\"ltr\"><span>This emulates the data returned by Butter’s API.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the LandingHero component</span></h3>\n<p dir=\"ltr\"><span>Create a new file - </span><code><span>.</span><span>/src/components/LandingHero.astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-perl\">---\n<span class=\"hljs-regexp\">//</span> ./src/components/LandingHero.astro\nimport type { kb_landing_hero } from <span class=\"hljs-string\">\"../lib/butter\"</span>;\nexport type Props = {\n fields: kb_landing_hero;\n};\nconst { fields } = Astro.props;\n---\n&<span class=\"hljs-keyword\">lt</span>;header <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">hero</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;h1 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">hero</span>-<span class=\"hljs-title\">caption</span>\"&<span class=\"hljs-title\">gt</span></span>;\n {fields.caption}\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/h1&gt;\n &lt;p class=\"site-hero-text\"&gt;\n {fields.text}\n &lt;/p</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">hero</span>-<span class=\"hljs-title\">search</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;input\n type=<span class=\"hljs-string\">\"text\"</span>\n <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">hero</span>-<span class=\"hljs-title\">search</span>-<span class=\"hljs-title\">input</span> <span class=\"hljs-title\">form</span>-<span class=\"hljs-title\">input</span>\"\n <span class=\"hljs-title\">placeholder</span>=</span>{fields.search_field_placeholder}\n /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;button <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">cta</span> <span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">hero__search</span>-<span class=\"hljs-title\">button</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;svg\n xmlns=<span class=\"hljs-string\">\"http://www.w3.org/2000/svg\"</span>\n width=<span class=\"hljs-string\">\"24\"</span>\n height=<span class=\"hljs-string\">\"24\"</span>\n viewBox=<span class=\"hljs-string\">\"0 0 24 24\"</span>\n fill=<span class=\"hljs-string\">\"none\"</span>\n stroke=<span class=\"hljs-string\">\"currentColor\"</span>\n stroke-width=<span class=\"hljs-string\">\"2\"</span>\n stroke-linecap=<span class=\"hljs-string\">\"round\"</span>\n stroke-linejoin=<span class=\"hljs-string\">\"round\"</span>\n <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">icon</span> <span class=\"hljs-title\">feather</span> <span class=\"hljs-title\">feather</span>-<span class=\"hljs-title\">search</span>\"\n &<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;circle cx=<span class=\"hljs-string\">\"11\"</span> cy=<span class=\"hljs-string\">\"11\"</span> r=<span class=\"hljs-string\">\"8\"</span>&<span class=\"hljs-keyword\">gt</span>;&<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/circle&gt;\n &lt;line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"&gt;&lt;/lin</span>e&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/svg&gt;\n &lt;/</span>button&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/div&gt;\n &lt;/di</span>v&<span class=\"hljs-keyword\">gt</span>;\n&<span class=\"hljs-keyword\">lt</span>;/header&<span class=\"hljs-keyword\">gt</span>;</code></pre>\n<p dir=\"ltr\"><span>Here, you have a component used to render the hero section of your landing page.</span></p>\n<p dir=\"ltr\"><span>Import the </span><code><span>kb_landing_hero</span></code><span> type from a file named in the </span><code><span>./lib/butter.ts</span></code><span>. This type is used to define the </span><code><span>Props</span></code><span> type, which is an object that contains a single field named </span><code><span>fields</span></code><span> of type </span><code><span>kb_landing_hero</span></code><span>.</span></p>\n<p dir=\"ltr\"><span>Next, extract the </span><code><span>fields</span></code><span> object from the </span><code><span>Astro.props</span></code><span> object which will be passed to the </span><code><span>fields</span></code><span> prop to the component.</span></p>\n<p dir=\"ltr\"><span>The rest of the code is the markup for the hero section. Use the </span><code><span>fields</span></code><span> prop to dynamically render the content of the hero section, including the heading, subheading, search field placeholder text, and search button.</span></p>\n<p dir=\"ltr\"><span>Next, create the components for your sections.</span></p>\n<p dir=\"ltr\"><span><a href=\"/astro-cms/\" rel=\"follow\"><img src=\"https://cdn.buttercms.com/3PQv93S8iiVIeUlLtUvA\" alt=\"See how Butter's simple content API works with your Astro app.\" style=\"display: block; margin-left: auto; margin-right: auto;\"></a></span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the ArticleCard component</span></h3>\n<p dir=\"ltr\"><span>Create a new file called </span><code><span>./components/ArticleItem.astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-perl\">---\n<span class=\"hljs-regexp\">//</span> ./src/components/ArticleItem.astro\n\nimport type { kb_article } from <span class=\"hljs-string\">\"../lib/butter\"</span>;\nexport type Props = {\n article: kb_article;\n};\nconst { article } = Astro.props;\n---\n&<span class=\"hljs-keyword\">lt</span>;a href={<span class=\"hljs-string\">`article/${article.slug}`</span>}&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;article <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">article</span>-<span class=\"hljs-title\">item</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;h3 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">title</span>\"&<span class=\"hljs-title\">gt</span></span>;{article.fields.title}&<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/h3&gt;\n &lt;p&gt;{article.fields.description}&lt;/p</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/article&gt;\n&lt;/a</span>&<span class=\"hljs-keyword\">gt</span>;\n</code></pre>\n<p dir=\"ltr\"><span>Here, use the<code> </code></span><code><span>kb_article</span></code><span> type for your </span><code><span>article</span></code><span> prop. The markup is simply an anchor element (</span><code><span><a></span></code><span>) with an </span><span>href</span><span> attribute that dynamically creates a link to the article based on its </span><span>slug</span><span> property. Then, we render the title and description.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the CategoryItem component</span></h3>\n<p>Create a new file called <code>./src/components/CategoryItem.astro</code>:</p>\n<pre><code class=\"hljs language-perl\">// ./src/components/CategoryItem.astro\nimport type { kb_category } from <span class=\"hljs-string\">\"../lib/butter\"</span>;\nexport type Props = {\n category: kb_category;\n};\nconst { category } = Astro.props;\n---\n&<span class=\"hljs-keyword\">lt</span>;a href={<span class=\"hljs-string\">`category/${category.slug}`</span>}&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;article <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">category</span>-<span class=\"hljs-title\">item</span> <span class=\"hljs-title\">group</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;h3 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">name</span>\"&<span class=\"hljs-title\">gt</span></span>;{category.name}&<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/h3&gt;\n &lt;p&gt;{category.description}&lt;/p</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/article&gt;\n&lt;/a</span>&<span class=\"hljs-keyword\">gt</span>;</code></pre>\n<p dir=\"ltr\"><span>Here, use the </span><code><span>kb_category</span></code><span> type for your </span><span>category</span><span> prop. The markup is simply an anchor element (</span><code><span><a></span></code><span>) with an </span><span>href</span><span> attribute that dynamically creates a link to the category page based on its </span><span>slug</span><span> property. Then we render the name and description.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the FeaturedCategories component</span></h3>\n<p dir=\"ltr\"><span>Create a new file called </span><code><span>./src/components/Sections/FeaturedCategories.astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-perl\">---\n<span class=\"hljs-regexp\">//</span> ./src/components/Sections/FeaturedCategories.astro\nimport { kb_featured_categories } from <span class=\"hljs-string\">\"../../lib/butter\"</span>;\nimport CategoryItem from <span class=\"hljs-string\">\"../CategoryItem.astro\"</span>;\nexport type Props = {\n section: kb_featured_categories;\n};\nconst { section } = Astro.props;\n---\n&<span class=\"hljs-keyword\">lt</span>;section <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">section</span>\" <span class=\"hljs-title\">data</span>-<span class=\"hljs-title\">section</span>=</span>{section.type}&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;header <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">section</span>-<span class=\"hljs-title\">header</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;h2 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">caption</span>\"&<span class=\"hljs-title\">gt</span></span>;{section.fields.caption}&<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/h2&gt;\n &lt;/</span>header&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;ul <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">categories</span>-<span class=\"hljs-title\">list</span>\"&<span class=\"hljs-title\">gt</span></span>;\n {\n section.fields.categories.map((category) =&<span class=\"hljs-keyword\">gt</span>; (\n &<span class=\"hljs-keyword\">lt</span>;li&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;CategoryItem category=<span class=\"hljs-string\">{category}</span> /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/li&gt;\n ))\n }\n &lt;/ul</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/div&gt;\n&lt;/s</span>ection&<span class=\"hljs-keyword\">gt</span>;</code></pre>\n<p dir=\"ltr\"><span>Here, import the </span><code><span>kb_featured_categories</span></code><span> type to define the </span><code><span>Props</span></code><span> type, which is an object that contains a single field named </span><code><span>section</span></code><span> which is for the </span><code><span>section</span></code><span><code> </code>prop extracted from </span><code><span>Astro.props</span></code><span>.</span></p>\n<p dir=\"ltr\"><span>The remainder of the code is the markup for the featured categories section. It uses the </span><code><span>section</span></code><span> prop to dynamically render the content of the section, including the section header and a list of categories.</span></p>\n<p dir=\"ltr\"><span>The list of categories is rendered using the </span><code><span>map()</span></code><span> method to iterate over the </span><span>categories</span><span> array in </span><code><span>section.fields.categories</span></code><span>. For each category, the <code>`</code></span><code><span>CategoryItem</span></code><span><code>`</code> component is rendered.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the FeaturedArticles component</span></h3>\n<p dir=\"ltr\"><span>Create a new file called </span><code><span>./src/components/Sections/FeaturedArticles.astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-perl\">---\n<span class=\"hljs-regexp\">//</span> ./src/components/Sections/FeaturedArticles.astro\nimport { kb_featured_articles } from <span class=\"hljs-string\">\"../../lib/butter\"</span>;\nimport ArticleCard from <span class=\"hljs-string\">\"../ArticleCard.astro\"</span>;\nexport type Props = {\n section: kb_featured_articles;\n};\nconst { section } = Astro.props;\n---\n&<span class=\"hljs-keyword\">lt</span>;section <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">section</span>\" <span class=\"hljs-title\">data</span>-<span class=\"hljs-title\">section</span>=</span>{section.type}&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;header <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">section</span>-<span class=\"hljs-title\">header</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;h2 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">caption</span>\"&<span class=\"hljs-title\">gt</span></span>;{section.fields.caption}&<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/h2&gt;\n &lt;/</span>header&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;ul <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">articles</span>-<span class=\"hljs-title\">list</span>\"&<span class=\"hljs-title\">gt</span></span>;\n {\n section.fields.articles.map((article) =&<span class=\"hljs-keyword\">gt</span>; (\n &<span class=\"hljs-keyword\">lt</span>;li&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;ArticleItem article=<span class=\"hljs-string\">{article}</span> /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/li&gt;\n ))\n }\n &lt;/ul</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/div&gt;\n&lt;/s</span>ection&<span class=\"hljs-keyword\">gt</span>;\n</code></pre>\n<p dir=\"ltr\"><span>As you would expect, import the </span><code><span>kb_featured_articles</span></code><span> type for this component. Similar to your previous components, destructure the </span><span>section</span><span> prop from </span><code><span>Astro.props</span></code><span> and assign it the </span><code><span>kb_featured_articles</span></code><span> type.</span></p>\n<p dir=\"ltr\"><span>In the markup, render a list of articles with the </span><code><span><ArticleItem/></span></code><span><code> </code>components.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the KBFAQs component</span></h3>\n<p dir=\"ltr\"><span>Create a new file called </span><code><span>./src/components/KBFAQs.astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-perl\">---\n<span class=\"hljs-regexp\">//</span> ./src/components/KBFAQs.astro\nimport type { kb_faqs } from <span class=\"hljs-string\">\"../../lib/butter\"</span>;\nexport interface Props {\n section: kb_faqs;\n}\nconst { section } = Astro.props;\n---\n&<span class=\"hljs-keyword\">lt</span>;section <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=</span>{<span class=\"hljs-string\">\"site-section\"</span>}&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;header <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">section</span>-<span class=\"hljs-title\">header</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;h2 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">caption</span>\"&<span class=\"hljs-title\">gt</span></span>;{section.fields.caption}&<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/h2&gt;\n &lt;/</span>header&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;ul <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">faqs</span>-<span class=\"hljs-title\">list</span>\"&<span class=\"hljs-title\">gt</span></span>;\n {\n section.fields.faqs.map((faq) =&<span class=\"hljs-keyword\">gt</span>; (\n &<span class=\"hljs-keyword\">lt</span>;details <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">faq</span>-<span class=\"hljs-title\">item</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;summary <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">summary</span>\"&<span class=\"hljs-title\">gt</span></span>;{faq.question}&<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/summary&gt;\n &lt;div class=\"content\" set:html={faq.answer} /</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/details&gt;\n ))\n }\n &lt;/ul</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/div&gt;\n&lt;/s</span>ection&<span class=\"hljs-keyword\">gt</span>;\n\n</code></pre>\n<p dir=\"ltr\"><span>Here, use the </span><code><span>kb_faqs</span></code><span> type for the </span><span>section</span><span> prop. Then, in the markup, render a </span><code><span><details /></span></code><span> element with the </span><code><span>faq.question</span></code><span> and </span><code><span>faq.answer</span></code><span>.</span></p>\n<p dir=\"ltr\"><span>Notice that you’re using the </span><code><span>set:HTML</span></code><span> attribute to render the string as HTML.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the Search component</span></h3>\n<p dir=\"ltr\"><span>Since you’ll be adding search functionality to your knowledge base, create a Search component to handle that. Create a new file called </span><code><span>./src/components/Search.astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-perl\">---\n<span class=\"hljs-regexp\">//</span> ./src/components/Search.astro\ninterface Props {\n placeholder?: string;\n}\nconst { placeholder } = Astro.props;\n---\n&<span class=\"hljs-keyword\">lt</span>;form id=<span class=\"hljs-string\">\"search\"</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">search</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;input\n id=<span class=\"hljs-string\">\"search-input\"</span>\n type=<span class=\"hljs-string\">\"text\"</span>\n <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">search</span>-<span class=\"hljs-title\">input</span> <span class=\"hljs-title\">form</span>-<span class=\"hljs-title\">input</span>\"\n <span class=\"hljs-title\">placeholder</span>=</span>{placeholder}\n /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;button <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">cta</span> <span class=\"hljs-title\">search</span>-<span class=\"hljs-title\">button</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;svg\n xmlns=<span class=\"hljs-string\">\"http://www.w3.org/2000/svg\"</span>\n width=<span class=\"hljs-string\">\"24\"</span>\n height=<span class=\"hljs-string\">\"24\"</span>\n viewBox=<span class=\"hljs-string\">\"0 0 24 24\"</span>\n fill=<span class=\"hljs-string\">\"none\"</span>\n stroke=<span class=\"hljs-string\">\"currentColor\"</span>\n stroke-width=<span class=\"hljs-string\">\"2\"</span>\n stroke-linecap=<span class=\"hljs-string\">\"round\"</span>\n stroke-linejoin=<span class=\"hljs-string\">\"round\"</span>\n <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">icon</span> <span class=\"hljs-title\">feather</span> <span class=\"hljs-title\">feather</span>-<span class=\"hljs-title\">search</span>\"\n &<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;circle cx=<span class=\"hljs-string\">\"11\"</span> cy=<span class=\"hljs-string\">\"11\"</span> r=<span class=\"hljs-string\">\"8\"</span>&<span class=\"hljs-keyword\">gt</span>;&<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/circle&gt;\n &lt;line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"&gt;&lt;/lin</span>e&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/svg&gt;\n &lt;/</span>button&<span class=\"hljs-keyword\">gt</span>;\n&<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/form&gt;\n&lt;script&gt;\n const form = document.getElementById(\"search\") as HTMLFormElement;\n const searchInput = document.getElementById(\n \"search-input\"\n ) as HTMLInputElement;\n const handleSearch = (e: FormDataEvent) =&gt; {\n e.preventDefault();\n const search = searchInput.value;\n console.log(e.target);\n if (search) {\n window.location.href = `/s</span>earch?query=<span class=\"hljs-variable\">${search}</span><span class=\"hljs-string\">`;\n }\n };\n form.addEventListener(\"submit\", handleSearch);\n&lt;/script&gt;</span></code></pre>\n<p dir=\"ltr\"><span>Here, include a </span><code><span><script></span></code><span> block that adds an event listener to the form's submit event. When the form is submitted, the </span><code><span>handleSearch</span></code><span> function is called. This function prevents the default form submission behavior, gets the search query from the input element, and redirects the browser to a search results page with the query as a parameter.</span></p>\n<p dir=\"ltr\"><span>Now, you’ll import this component to your </span><code><span>./src/components/LandingHero.astro</span></code><span> component:</span></p>\n<pre><code class=\"hljs language-perl\">---\n<span class=\"hljs-regexp\">//</span> ./src/components/LandingHero.astro\nimport type { kb_landing_hero } from <span class=\"hljs-string\">\"../lib/butter\"</span>;\n\n<span class=\"hljs-regexp\">//</span> ...\n---\n&<span class=\"hljs-keyword\">lt</span>;header <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">hero</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;!-- ... --&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;Search placeholder={fields.search_field_placeholder} /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/div&gt;\n&lt;/</span>header&<span class=\"hljs-keyword\">gt</span>;</code></pre>\n<p dir=\"ltr\"><span>Next, you’ll build out the pages for your knowledge base.</span></p>\n<h2 dir=\"ltr\" id=\"creating-pages\"><span style=\"font-weight: 400;\">Creating pages</span></h2>\n<p dir=\"ltr\"><span>Put the components you just created together to create your dynamic landing page.</span></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the landing page</span></h3>\n<p dir=\"ltr\"><span>Back in your </span><code><span>./src/pages/index.astro</span></code><span> file, import your components, map through the </span><span>sections</span><span>,</span><span> and use a </span><span>switch</span><span> statement to conditionally render your section components:</span></p>\n<pre><code class=\"hljs language-javascript\">---\n<span class=\"hljs-comment\">// .src/pages/index.astro</span>\n<span class=\"hljs-keyword\">import</span> <span class=\"hljs-title class_\">LandingHero</span> <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">\"../components/LandingHero.astro\"</span>;\n<span class=\"hljs-keyword\">import</span> <span class=\"hljs-title class_\">FeaturedArticles</span> <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">\"../components/Sections/FeaturedArticles.astro\"</span>;\n<span class=\"hljs-keyword\">import</span> <span class=\"hljs-title class_\">FeaturedCategories</span> <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">\"../components/Sections/FeaturedCategories.astro\"</span>;\n<span class=\"hljs-keyword\">import</span> <span class=\"hljs-title class_\">KBFAQs</span> <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">\"../components/Sections/KBFAQs.astro\"</span>;\n<span class=\"hljs-keyword\">import</span> <span class=\"hljs-title class_\">Layout</span> <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">\"../layouts/Layout.astro\"</span>;\n<span class=\"hljs-keyword\">import</span> { butterClient, kb_landing_page_fields } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">\"../lib/butter\"</span>;\n<span class=\"hljs-comment\">// retrieve the page data from ButterCMS by type and slug</span>\n<span class=\"hljs-keyword\">const</span> response = <span class=\"hljs-keyword\">await</span> butterClient.<span class=\"hljs-property\">page</span>.<span class=\"hljs-title function_\">retrieve</span>(\n <span class=\"hljs-string\">\"kb_landing_page\"</span>,\n <span class=\"hljs-string\">\"kb-home-page\"</span>\n);\n<span class=\"hljs-comment\">// get the page data</span>\n<span class=\"hljs-keyword\">const</span> pageData = response.<span class=\"hljs-property\">data</span>.<span class=\"hljs-property\">data</span>;\n<span class=\"hljs-comment\">// cast the fields to the correct type</span>\n<span class=\"hljs-keyword\">const</span> fields = pageData.<span class=\"hljs-property\">fields</span> <span class=\"hljs-keyword\">as</span> kb_landing_page_fields;\n---\n&lt;<span class=\"hljs-title class_\">Layout</span> title={fields.<span class=\"hljs-property\">seo</span>.<span class=\"hljs-property\">title</span>} description={fields.<span class=\"hljs-property\">seo</span>.<span class=\"hljs-property\">description</span>}&gt;\n &lt;main <span class=\"hljs-keyword\">class</span>=<span class=\"hljs-string\">\"site-main\"</span>&gt;\n &lt;<span class=\"hljs-title class_\">LandingHero</span> fields={fields.<span class=\"hljs-property\">kb_landing_hero</span>} /&gt;\n {\n fields.<span class=\"hljs-property\">sections</span>.<span class=\"hljs-title function_\">map</span>((section) =&gt; {\n <span class=\"hljs-keyword\">switch</span> (section.<span class=\"hljs-property\">type</span>) {\n <span class=\"hljs-keyword\">case</span> <span class=\"hljs-string\">\"kb_featured_categories\"</span>:\n <span class=\"hljs-keyword\">return</span> &lt;<span class=\"hljs-title class_\">FeaturedCategories</span> section={section} /&gt;;\n <span class=\"hljs-keyword\">case</span> <span class=\"hljs-string\">\"kb_featured_articles\"</span>:\n <span class=\"hljs-keyword\">return</span> &lt;<span class=\"hljs-title class_\">FeaturedArticles</span> section={section} /&gt;;\n <span class=\"hljs-keyword\">case</span> <span class=\"hljs-string\">\"kb_faqs\"</span>:\n <span class=\"hljs-keyword\">return</span> &lt;<span class=\"hljs-title class_\">KBFAQs</span> section={section} /&gt;;\n <span class=\"hljs-attr\">default</span>:\n <span class=\"hljs-keyword\">return</span> <span class=\"hljs-literal\">null</span>;\n }\n })\n }\n &lt;/main&gt;\n&lt;/<span class=\"hljs-title class_\">Layout</span>&gt;\n</code></pre>\n<p dir=\"ltr\"><span>With that, you should have something like this:</span></p>\n<p dir=\"ltr\"><span>Here, you can see the landing hero and featured categories.</span></p>\n<p><img src=\"https://cdn.buttercms.com/90B5UNdER9SRT1DBbcsQ\" alt=\"Astro tutorial: rendered hero section\" width=\"836\" height=\"431\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p><span>Here, you can see the featured articles.</span></p>\n<p dir=\"ltr\"><img src=\"https://cdn.buttercms.com/an83Gq8dTKqNvXfL9vPq\" alt=\"Astro Tutorial: Rendered Featured Articles section\" width=\"847\" height=\"437\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p>And finally, here you see the FAQs.</p>\n<p><img src=\"https://cdn.buttercms.com/uXJPF5NiRQmEvDQXJXZq\" alt=\"Astro tutorial: Rendered FAQs\" width=\"834\" height=\"430\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the dynamic categories page</span></h3>\n<p dir=\"ltr\"><span>First, </span><a href=\"https://docs.astro.build/en/guides/server-side-rendering/#enabling-ssr-in-your-project\" rel=\"nofollow noopener\" target=\"_blank\"><span>enable SSR mode</span></a><span> by adding </span><span>output: \"server\"</span><span> to your </span><code><span>./astro.config.cjs</span></code><span> file:</span></p>\n<p><b id=\"docs-internal-guid-b661b91a-7fff-cdb4-a82f-46bbd9a7349f\"></b></p>\n<pre><code class=\"hljs language-perl\">// ./astro.config.mjs\nimport { defineConfig } from <span class=\"hljs-string\">'astro/config'</span>;\nimport tailwind from <span class=\"hljs-string\">\"<span class=\"hljs-variable\">@astrojs</span>/tailwind\"</span>;\n<span class=\"hljs-regexp\">//</span> https:<span class=\"hljs-regexp\">//as</span>tro.build/config\nexport default defineConfig({\n integrations: [tailwind()],\n output: <span class=\"hljs-string\">\"server\"</span>\n});\nNow create a new - ./src/pages/category/[slug].astro file:\n---\n<span class=\"hljs-regexp\">//</span> ./src/pages/category/[slug].astro\nimport ArticleItem from <span class=\"hljs-string\">\"../../components/ArticleItem.astro\"</span>;\nimport LandingHero from <span class=\"hljs-string\">\"../../components/LandingHero.astro\"</span>;\nimport Layout from <span class=\"hljs-string\">\"../../layouts/Layout.astro\"</span>;\nimport { butterClient, kb_article, kb_category } from <span class=\"hljs-string\">\"../../lib/butter\"</span>;\nconst { slug } = Astro.params;\nconst params = {\n <span class=\"hljs-string\">\"fields.category.slug\"</span>: slug,\n};\nconst collectionResponse = await butterClient.content.retrieve(\n [<span class=\"hljs-string\">\"kb_category\"</span>],\n {\n <span class=\"hljs-string\">\"fields.slug\"</span>: slug,\n }\n);\nconst pagesResponse = await butterClient.page.list(<span class=\"hljs-string\">\"kb_article\"</span>, params);\nconst categoryData = collectionResponse.data.data;\nconst pagesData = pagesResponse.data.data;\nconst category = categoryData.kb_category as kb_category[];\nconst articles = pagesData as unknown as kb_article[];\n---\n&<span class=\"hljs-keyword\">lt</span>;Layout title={category[<span class=\"hljs-number\">0</span>].name} description={category[<span class=\"hljs-number\">0</span>].description}&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;main <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">main</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;LandingHero\n fields={{\n caption: category[<span class=\"hljs-number\">0</span>].name,\n text: category[<span class=\"hljs-number\">0</span>].description,\n search_field_placeholder: <span class=\"hljs-string\">\"Search for articles\"</span>,\n }}\n /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;section <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">section</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;ul <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">articles</span>-<span class=\"hljs-title\">list</span>\"&<span class=\"hljs-title\">gt</span></span>;\n {\n articles.map((article) =&<span class=\"hljs-keyword\">gt</span>; (\n &<span class=\"hljs-keyword\">lt</span>;li&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;ArticleItem article=<span class=\"hljs-string\">{article}</span> /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/li&gt;\n ))\n }\n &lt;/ul</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/div&gt;\n &lt;/s</span>ection&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/main&gt;\n&lt;/</span>Layout&<span class=\"hljs-keyword\">gt</span>;</code></pre>\n<p dir=\"ltr\"><span>Here, the category data and article data are retrieved separately using the </span><code><span>content.retrieve()</span></code><span> and </span><code><span>page.list()</span></code><span> functions from the </span><code><span>butterClient</span></code><span> library. Gt the slug </span><code><span>Astro.params</span></code><span> and assign it to the </span><code><span>fields.category.slug</span></code><span> key in the </span><span>params</span><span> object which will be used to fetch the pages.</span></p>\n<p dir=\"ltr\"><span>Assign the slug to the </span><code><span>fields.slug</span></code><span> key in the </span><code><span>butterClient.content.retrieve()</span></code><span> function which will be used to fetch the category.</span></p>\n<p dir=\"ltr\"><span>The retrieved data is then used to display the category name, description, and a list of articles on the page.</span></p>\n<p dir=\"ltr\"><span>Now, when you navigate to </span><span><code>category/getting-started</code>,</span><span> you should have something like this:</span></p>\n<p><img src=\"https://cdn.buttercms.com/Y2u6TFysSMikAwkLV9nC\" alt=\"Getting started categories page\" width=\"834\" height=\"430\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the Article page</span></h3>\n<p dir=\"ltr\"><span>Create a new file called </span><code><span>./src/pages/article/[slug].astro</span></code><span>:</span></p>\n<pre><code class=\"hljs language-perl\">---\n<span class=\"hljs-regexp\">//</span> ./src/pages/article/[slug].astro\n\nimport Layout from <span class=\"hljs-string\">\"../../layouts/Layout.astro\"</span>;\nimport { butterClient, kb_article } from <span class=\"hljs-string\">\"../../lib/butter\"</span>;\nconst { slug } = Astro.params;\nconst response = await butterClient.page.retrieve(<span class=\"hljs-string\">\"kb_article\"</span>, slug);\nconst article = response.data.data as kb_article;\n---\n&<span class=\"hljs-keyword\">lt</span>;main <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">main</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;Layout title={article.fields.title} description={article.fields.description}&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;article <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">article</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;header <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">article</span>-<span class=\"hljs-title\">header</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;h1 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">article</span>-<span class=\"hljs-title\">header</span>-<span class=\"hljs-title\">caption</span>\"&<span class=\"hljs-title\">gt</span></span>;\n {article.fields.title}\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/h1&gt;\n &lt;p class=\"site-hero-text\"&gt;\n {article.fields.description}\n &lt;/p</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/div&gt;\n &lt;/</span>header&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;section <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">section</span> <span class=\"hljs-title\">content</span>-<span class=\"hljs-title\">section</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;div\n <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span> <span class=\"hljs-title\">prose</span> <span class=\"hljs-title\">dark</span>:<span class=\"hljs-title\">prose</span>-<span class=\"hljs-title\">invert</span> !<span class=\"hljs-title\">max</span>-<span class=\"hljs-title\">w</span>-4<span class=\"hljs-title\">xl</span>\"\n <span class=\"hljs-title\">set</span>:<span class=\"hljs-title\">html</span>=</span>{article.fields.content}\n /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/section&gt;\n &lt;/di</span>v&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/article&gt;\n &lt;/</span>Layout&<span class=\"hljs-keyword\">gt</span>;\n&<span class=\"hljs-keyword\">lt</span>;/main&<span class=\"hljs-keyword\">gt</span>;\n</code></pre>\n<p dir=\"ltr\"><span>Here, extract the </span><span>slug</span><span> value from the </span><code><span>Astro.params</span></code><span> object, which is the identifier of the article being displayed.</span></p>\n<p dir=\"ltr\"><span>Next, call the </span><code><span>butterClient.page.retrieve()</span></code><span> function to retrieve the article data. The function takes two arguments: the content type (in this case, </span><code><span>kb_article</span></code><span>) and the article slug. The response from the function call is stored in </span><span>response</span><span>, and the actual article data is extracted from it and stored in </span><code><span>article</span></code><span>.</span></p>\n<p dir=\"ltr\"><span>Finally, the article data is used to populate the HTML of the web page. The article title and description are passed to the </span><span>Layout</span><span> component as props which set the page metadata. The article content is displayed within an </span><code><span>article</span></code><span> tag, with the title and description displayed in a header section, and the article content displayed in a </span><span>section</span><span> tag using the </span><code><span>set:html</span></code><span> attribute to populate it with the HTML content from the </span><code><span>article.fields.content</span></code><span> property.</span></p>\n<p dir=\"ltr\"><span>With that, your article page should look something like this:</span></p>\n<p><img src=\"https://cdn.buttercms.com/JzuJjqxCQZyo3C869u7H\" alt=\"Astro tutorial: rendered kb article page\" width=\"836\" height=\"431\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Create the Search page</span></h3>\n<p dir=\"ltr\"><span>Create a new file called </span><code><span>./src/pages/search.astro</span></code><span>:</span></p>\n<p><b id=\"docs-internal-guid-639b7631-7fff-80d3-8fb9-ceba24c26375\"></b></p>\n<pre><code class=\"hljs language-perl\">---\n<span class=\"hljs-regexp\">//</span> ./src/pages/search.astro\nimport ArticleItem from <span class=\"hljs-string\">\"../components/ArticleItem.astro\"</span>;\nimport LandingHero from <span class=\"hljs-string\">\"../components/LandingHero.astro\"</span>;\nimport Layout from <span class=\"hljs-string\">\"../layouts/Layout.astro\"</span>;\nimport { butterClient, kb_article } from <span class=\"hljs-string\">\"../lib/butter\"</span>;\nconst query = Astro.url.searchParams.get(<span class=\"hljs-string\">\"query\"</span>);\nconst response = await butterClient.page.search(query, {\n page_type: <span class=\"hljs-string\">\"kb_article\"</span>,\n});\nconst pagesData = response.data.data;\nconst articles = pagesData as kb_article[];\n---\n&<span class=\"hljs-keyword\">lt</span>;Layout\n title={<span class=\"hljs-string\">`Results matching ${query}`</span>}\n description={<span class=\"hljs-string\">`Results matching ${query}`</span>}\n&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;main <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">main</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;LandingHero\n fields={{\n caption: <span class=\"hljs-string\">`Results matching \"${query}\"`</span>,\n text: <span class=\"hljs-string\">`Here's what we could find matching \"${query}\"`</span>,\n search_field_placeholder: <span class=\"hljs-string\">\"Search for something else\"</span>,\n }}\n /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;section <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">site</span>-<span class=\"hljs-title\">section</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">wrapper</span>\"&<span class=\"hljs-title\">gt</span></span>;\n &<span class=\"hljs-keyword\">lt</span>;ul <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span>=\"<span class=\"hljs-title\">articles</span>-<span class=\"hljs-title\">list</span>\"&<span class=\"hljs-title\">gt</span></span>;\n {\n articles.map((article) =&<span class=\"hljs-keyword\">gt</span>; (\n &<span class=\"hljs-keyword\">lt</span>;li&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;ArticleItem article=<span class=\"hljs-string\">{article}</span> /&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/li&gt;\n ))\n }\n &lt;/ul</span>&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/div&gt;\n &lt;/s</span>ection&<span class=\"hljs-keyword\">gt</span>;\n &<span class=\"hljs-keyword\">lt</span>;<span class=\"hljs-regexp\">/main&gt;\n&lt;/</span>Layout&<span class=\"hljs-keyword\">gt</span>;\n</code></pre>\n<p dir=\"ltr\"><span>Here, retrieve the search query from the URL using </span><code><span>Astro.url.searchParams.get(\"query\")</span></code><span>. Then, call the </span><code><span>butterClient.page.search</span></code><span> method to retrieve a list of articles matching the query, passing in an object with </span><code><span>page_type: \"kb_article\"</span></code><span> to restrict the search to knowledge base articles only.</span></p>\n<p dir=\"ltr\"><span>The search response is then used to extract the article data as an array of </span><code><span>kb_article</span></code><span> objects. Finally, the article data is used to render the search results on the page by mapping over the </span><code><span>articles</span></code><span> array and rendering an </span><code><span>ArticleItem</span></code><span> component for each one.</span></p>\n<p dir=\"ltr\"><span>With that, your search page should look something like this:</span></p>\n<p><img src=\"https://cdn.buttercms.com/PBUOLT9BQdaTl1zvSNwg\" alt=\"Astro tutorial: Rendered search functionality\" width=\"836\" height=\"431\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<h2 dir=\"ltr\" id=\"deploying-to-netlify\"><span style=\"font-weight: 400;\">Deploying to Netlify</span></h2>\n<p dir=\"ltr\"><span>Deploying a project is pretty straightforward. For this tutorial, you'll deploy using GitHub. First, add a </span><a href=\"https://docs.astro.build/en/guides/integrations-guide/netlify/\" rel=\"nofollow noopener\" target=\"_blank\"><span>Netlify adapter to your application because you’re using SSR</span></a><span> in your Astro project.</span></p>\n<p dir=\"ltr\"><span>Run the command to add the Netlify adapter:</span></p>\n<pre><code class=\"hljs language-csharp\">npx astro <span class=\"hljs-keyword\">add</span> netlify</code></pre>\n<p dir=\"ltr\"><span>Create a repo on GitHub for your project and upload your code. After that’s done, navigate to the Netlify dashboard and create a new project:</span></p>\n<p><img src=\"https://cdn.buttercms.com/vCQePuOXRuyComd4DLhT\" alt=\"Netlify dashboard\" width=\"822\" height=\"440\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>In the next step, select </span><strong>GitHub</strong><span> as the Git provider. Next, select the repo for the project:</span></p>\n<p><img src=\"https://cdn.buttercms.com/GFlDOzETTD2wfayiMhdr\" alt=\"Select repo for project\" width=\"830\" height=\"444\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Under the site settings, click on <strong>A</strong></span><strong>dvanced Options</strong><span> and enter the Butter API token env:</span></p>\n<p><img src=\"https://cdn.buttercms.com/swErAJeQkmXRfP9jn9jL\" alt=\"Add ButterCMS token\" width=\"838\" height=\"431\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<p dir=\"ltr\"><span>Now, click on </span><span><strong>Deploy Site</strong>.</span></p>\n<h2 dir=\"ltr\" id=\"final-results\"><span style=\"font-weight: 400;\">Final results</span></h2>\n<p dir=\"ltr\"><span>Congratulations on making it this far into this tutorial. Hopefully, you’ve been able to set up your ButterCMS, create a few pages, and build out your Astro front-end by following this tutorial.</span></p>\n<p dir=\"ltr\"><span>Once your site is deployed, you can always change the default randomly generated site name to something else.</span></p>\n<p dir=\"ltr\"><span>Here’s a GIF of what the final result should look like:</span></p>\n<p><img src=\"https://cdn.buttercms.com/EZs3W0bTp7dDZjMWfcrQ\" alt=\"Astro tutorial: Application walkthrough\" style=\"display: block; margin-left: auto; margin-right: auto;\"></p>\n<h2 dir=\"ltr\" id=\"closing-thoughts\"><span style=\"font-weight: 400;\">Closing thoughts</span></h2>\n<p dir=\"ltr\"><span>In conclusion, building a knowledge base with Astro and ButterCMS is an effective and efficient way to create a robust, organized, and user-friendly resource for your audience. By following the steps outlined in this article, you can easily set up ButterCMS to power your knowledge base, create dynamic landing pages and components, and add content to ButterCMS. </span></p>\n<p dir=\"ltr\"><span>With Astro, you can integrate ButterCMS and fetch data to build out your knowledge base using various components, including the landing hero, article card, category item, featured categories, featured articles, KB FAQs, and search. Deploying your knowledge base to Netlify is straightforward, and the final result will be a professional and comprehensive resource that is easy to navigate and use.</span></p>\n<p dir=\"ltr\"><span>By investing the time and effort to create a knowledge base using Astro and ButterCMS, you can provide value to your audience and establish your brand as a knowledgeable and reliable source of information.</span></p>\n<p dir=\"ltr\"><span>If you'd like to learn more about how you can use ButterCMS with Astro, check out the following tutorial:</span></p>\n<ul>\n<li dir=\"ltr\"><span style=\"color: #3d2dd3;\"><a href=\"/blog/astro-tutorial-building-flexible-landing-pages/\" rel=\"follow\">Astro Tutorial: Building Flexible Landing Pages for Your App with ButterCMS</a></span><span style=\"color: #3d2dd3;\"><br></span></li>\n</ul>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Further reading and resources</span></h3>\n<p dir=\"ltr\"><span>Here are a few links I think you might find useful:</span></p>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><a href=\"https://docs.astro.build/\" rel=\"nofollow noopener\" target=\"_blank\"><span style=\"color: #3d2dd3;\">Astro Docs</span></a></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><a href=\"/docs/api/#introduction\" rel=\"nofollow\"><span style=\"color: #3d2dd3;\">ButterCMS API Docs</span></a></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><a href=\"/kb/use-cases-for-buttercms-collections/#pagefacets\" rel=\"nofollow\"><span style=\"color: #3d2dd3;\">Use Cases for ButterCMS Collections</span></a></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><a href=\"/kb/create-dynamic-landing-pages-using-components/\" rel=\"nofollow\"><span style=\"color: #3d2dd3;\">Create Dynamic Landing Pages Using Components</span></a></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><a href=\"https://docs.astro.build/en/guides/cms/buttercms/\" rel=\"nofollow noopener\" target=\"_blank\"><span style=\"color: #3d2dd3;\">ButterCMS and Astro</span></a></p>\n</li>\n</ul>\n<h3 dir=\"ltr\"><span style=\"font-weight: 400;\">Resources</span></h3>\n<ul>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><a href=\"https://github.com/ButterCMS/astro-knowledge-base-tutorial\" rel=\"nofollow noopener\" target=\"_blank\"><span style=\"color: #3d2dd3;\">Project source code on GitHub</span></a></p>\n</li>\n<li dir=\"ltr\" aria-level=\"1\">\n<p dir=\"ltr\" role=\"presentation\"><a href=\"https://astro-butter-kb.netlify.app/\" rel=\"nofollow noopener\" target=\"_blank\"><span style=\"color: #3d2dd3;\">Deployed example project on Netlify</span></a></p>\n</li>\n</ul>\n"],"summary":[0,"Learn how to create an amazing knowledge base using ButterCMS. Our Astro tutorial will guide you through the process step-by-step."],"seoTitle":[0,"Astro Tutorial: Create a Knowledge Base with ButterCMS"],"metaDescription":[0,"Learn how to create an amazing knowledge base using ButterCMS. Our Astro tutorial will guide you through the process step-by-step."],"readTimeMinutes":[0,28],"href":[0,"/blog/astro-tutorial-knowledge-base/"]}],[0,{"slug":[0,"angular-seo-how-to-make-search-friendly-pages"],"author":[0,{"name":[0,"Maab Saleem"],"title":[0,""],"avatarSrc":[0,"https://cdn.buttercms.com/kv1shOhhRjv1voK45csv"],"bio":[0,"Maab is an experienced software engineer who specializes in explaining technical topics to a wider audience."],"slug":[0,"maab-saleem"],"linkedinUrl":[0,""],"twitterHandle":[0,""],"facebookUrl":[0,""],"href":[0,"/blog/author/maab-saleem/"]}],"categories":[1,[[0,{"name":[0,"GSD"],"slug":[0,"gsd"],"href":[0,"/blog/category/gsd/"]}]]],"tags":[1,[[0,{"name":[0,"angular"],"slug":[0,"angular"]}],[0,{"name":[0,"seo"],"slug":[0,"seo"]}]]],"published":[3,"2025-08-20T22:51:00.000Z"],"updated":[3,"2025-08-20T21:12:37.805Z"],"featuredImageSrc":[0,"https://cdn.buttercms.com/AxRIQyAYQkykyOSTP11M"],"featuredImageAlt":[0,""],"title":[0,"Angular SEO: How to Make Search-Friendly Pages"],"body":[0,"<p>Search engines need content they can crawl and understand. That’s usually not a problem for traditional server-rendered websites. But with Angular apps, things can get tricky. </p>\n<p>As you already know, Angular is a single-page application (SPA) framework, and that means most of the content is loaded and rendered on the client side. Search bots don’t always wait around for your <span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\"><a target=\"_blank\" rel=\"noopener\">JavaScript </a>to</span> finish running.</p>\n<p>So even though you’ve got a beautiful Angular app, your content might as well be invisible to search engines <em>if you’re not careful.</em></p>\n<p>This is why Angular SEO isn’t just about meta tags or good URLs. It’s about structuring your app so that crawlers can access meaningful content as soon as possible. </p>\n<p>In this post, we’ll look at practical strategies to make that happen. Let’s get started.</p>\n<div class=\"section__content-aside\">\n<h4>Table of contents</h4>\n</div>\n<div class=\"section__content-main\">\n<ul>\n<li><a href=\"#why\" rel=\"follow\">Why is Angular SEO challenging?</a></li>\n<li><a href=\"#How\" rel=\"follow\">How to optimize Angular for SEO</a>\n<ul>\n<li><a href=\"#optimize\" rel=\"follow\">Optimize meta tags dynamically</a></li>\n<li><a href=\"#implement\" rel=\"follow\">Implement structured data (schema markup)</a></li>\n<li><a href=\"#create\" rel=\"follow\">Create SEO-friendly URLs</a></li>\n<li><a href=\"#improve\" rel=\"follow\">Improve page load speed</a></li>\n<li><a href=\"#generate\" rel=\"follow\">Generate and submit a sitemap</a></li>\n<li><a href=\"#configure\" rel=\"follow\">Configure robotx.txt and meta robots tags</a></li>\n</ul>\n</li>\n<li><a href=\"#best-seo-tools\" rel=\"follow\">Best SEO tools for Angular developers</a></li>\n<li><a href=\"#conclusion\" rel=\"follow\">Conclusion</a></li>\n</ul>\n</div>\n<h2 id=\"why\">Why is Angular SEO challenging?</h2>\n<p>Let’s start by looking at some common Angular SEO issues that developers run into:</p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>Empty or minimal HTML on first load:</strong> When a crawler hits an Angular page, it often gets a blank HTML shell with a few script tags. This makes it hard for search engines to index the actual content of the page.</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>Dynamic metadata</strong>: Titles, descriptions, Open Graph tags, and other metadata are often updated dynamically in Angular. If not handled correctly, crawlers don’t see the right metadata.</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>Routing issues</strong>: Angular uses its own client-side router. When not configured properly, crawlers may not be able to follow internal links or may run into broken routes.</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>JavaScript errors or timeouts:</strong> If a page takes too long to render or if there’s a JS error, bots may skip the content entirely.</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>Lack of server-rendered previews for social sharing:</strong> Without proper SSR or prerendering, social media shares of Angular URLs won’t show previews, since those platforms rely on static meta tags.</span></p>\n</li>\n</ul>\n<h2 id=\"how\">How to optimize Angular for SEO</h2>\n<p>Server-side rendering (SSR) is one of the most effective ways to make Angular SEO friendly. Here’s why:</p>\n<p>With SSR enabled, your app sends fully rendered HTML from the server instead of waiting for the browser to build the page using JavaScript. This means that crawlers get immediate access to all your content and metadata, without having to wait or execute any scripts.</p>\n<p>Before Angular 17, you’d have to add and configure Angular Universal as a separate package to implement server-side rendering. But since Angular 17, SSR and prerendering are built right into the Angular CLI through the <a href=\"https://angular.dev/guide/ssr\" rel=\"nofollow noopener\" target=\"_blank\">@angular/ssr</a> package. </p>\n<p>You no longer need a separate Universal setup, and the CLI handles the integration out of the box. <em>Please note that the </em><a href=\"https://github.com/angular/universal\" rel=\"nofollow noopener\" target=\"_blank\"><em>old Angular Universal repo</em></a><em> is now in maintenance mode, and all future improvements to SSR and prerendering are handled directly in the CLI.</em></p>\n<p>That said, let’s go over the steps needed to enable SSR in an Angular app.</p>\n<ol>\n<li>\n<p><span style=\"font-weight: 400;\">If you’re starting from scratch, you can scaffold a project with SSR already configured:</span></p>\n</li>\n</ol>\n<pre><code class=\"hljs language-sql\">ng <span class=\"hljs-keyword\">new</span> my<span class=\"hljs-operator\">-</span>angular<span class=\"hljs-operator\">-</span>app <span class=\"hljs-comment\">--ssr</span></code></pre>\n<p>          This sets up everything you need for hybrid rendering out of the box.</p>\n<ol start=\"2\">\n<li>\n<p><span style=\"font-weight: 400;\">If you already have an Angular app, you can add SSR support using:</span></p>\n</li>\n</ol>\n<pre><code class=\"hljs language-sql\">ng <span class=\"hljs-keyword\">add</span> <span class=\"hljs-variable\">@angular</span><span class=\"hljs-operator\">/</span>ssr</code></pre>\n<p>          This will install the necessary packages and update your project files to support SSR.</p>\n<ol start=\"3\">\n<li>\n<p><span style=\"font-weight: 400;\">You can control how each route is rendered using the <strong>RenderMode</strong> options. These typically live in <code>app.routes.server.ts</code>:</span></p>\n</li>\n</ol>\n<pre><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import</span> { <span class=\"hljs-title class_\">RenderMode</span>, <span class=\"hljs-title class_\">ServerRoute</span> } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'@angular/ssr'</span>;\n\n<span class=\"hljs-keyword\">export</span> <span class=\"hljs-keyword\">const</span> <span class=\"hljs-attr\">serverRoutes</span>: <span class=\"hljs-title class_\">ServerRoute</span>[] = [\n { <span class=\"hljs-attr\">path</span>: <span class=\"hljs-string\">''</span>, <span class=\"hljs-attr\">renderMode</span>: <span class=\"hljs-title class_\">RenderMode</span>.<span class=\"hljs-property\">Client</span> }, <span class=\"hljs-comment\">// CSR</span>\n { <span class=\"hljs-attr\">path</span>: home, <span class=\"hljs-attr\">renderMode</span>: <span class=\"hljs-title class_\">RenderMode</span>.<span class=\"hljs-property\">Prerender</span> }, <span class=\"hljs-comment\">// SSG</span>\n { <span class=\"hljs-attr\">path</span>: account, <span class=\"hljs-attr\">renderMode</span>: <span class=\"hljs-title class_\">RenderMode</span>.<span class=\"hljs-property\">Server</span> }, <span class=\"hljs-comment\">// SSR</span>\n { <span class=\"hljs-attr\">path</span>: <span class=\"hljs-string\">'**'</span>, <span class=\"hljs-attr\">renderMode</span>: <span class=\"hljs-title class_\">RenderMode</span>.<span class=\"hljs-property\">Server</span> }, <span class=\"hljs-comment\">// Fallback to SSR</span>\n];\n</code></pre>\n<ol start=\"4\">\n<li>\n<p><span style=\"font-weight: 400;\">You’ll then pass the above routes to the <code>provideServerRendering()</code> method using <code>withRoutes()</code> in your server config:</span></p>\n</li>\n</ol>\n<pre><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import</span> { provideServerRendering, withRoutes } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'@angular/ssr'</span>;\n<span class=\"hljs-keyword\">import</span> { serverRoutes } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'./app.routes.server'</span>;\n\n<span class=\"hljs-keyword\">const</span> <span class=\"hljs-attr\">serverConfig</span>: <span class=\"hljs-title class_\">ApplicationConfig</span> = {\n <span class=\"hljs-attr\">providers</span>: [\n <span class=\"hljs-title function_\">provideServerRendering</span>(<span class=\"hljs-title function_\">withRoutes</span>(serverRoutes)),\n <span class=\"hljs-comment\">// other providers</span>\n ]\n};\n</code></pre>\n<ol start=\"5\">\n<li>\n<p><span style=\"font-weight: 400;\">If you’re using the <a href=\"https://angular.dev/ecosystem/service-workers/app-shell\" rel=\"nofollow noopener\" target=\"_blank\"><strong>App Shell</strong></a> pattern, define a shell component for routes rendered on the client:</span></p>\n</li>\n</ol>\n<pre><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import</span> { provideServerRendering } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'@angular/ssr'</span>;\n<span class=\"hljs-keyword\">import</span> { withRoutes } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'@angular/ssr'</span>;\n<span class=\"hljs-keyword\">import</span> { withAppShell } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'@angular/ssr'</span>;\n<span class=\"hljs-keyword\">import</span> { <span class=\"hljs-title class_\">AppShellComponent</span> } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'./app-shell/app-shell.component'</span>;\n<span class=\"hljs-keyword\">import</span> { serverRoutes } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'./app.routes.server'</span>;\n\n<span class=\"hljs-keyword\">const</span> <span class=\"hljs-attr\">serverConfig</span>: <span class=\"hljs-title class_\">ApplicationConfig</span> = {\n <span class=\"hljs-attr\">providers</span>: [\n <span class=\"hljs-title function_\">provideServerRendering</span>(\n <span class=\"hljs-title function_\">withRoutes</span>(serverRoutes),\n <span class=\"hljs-title function_\">withAppShell</span>(<span class=\"hljs-title class_\">AppShellComponent</span>)\n ),\n <span class=\"hljs-comment\">// any additional providers</span>\n ],\n};\n</code></pre>\n<ol start=\"6\">\n<li>\n<p><span style=\"font-weight: 400;\">You can customize headers or status codes for specific routes:</span></p>\n</li>\n</ol>\n<pre><code class=\"hljs language-lua\">{\n <span class=\"hljs-built_in\">path</span>: <span class=\"hljs-string\">'profile'</span>,\n renderMode: RenderMode.Server,\n headers: {\n <span class=\"hljs-string\">'X-Custom-Header'</span>: <span class=\"hljs-string\">'some-value'</span>\n },\n <span class=\"hljs-built_in\">status</span>: <span class=\"hljs-number\">201</span>,\n}\n</code></pre>\n<p>That should get you started! With SSR in place, your app will deliver rendered HTML for the routes you’ve configured, helping resolve many of the Angular SEO issues you may have been facing.</p>\n<h3 id=\"optimize\">Optimize meta tags dynamically</h3>\n<p>Meta tags like the page title and <a href=\"https://ogp.me/\" rel=\"nofollow noopener\" target=\"_blank\">Open Graph</a> tags play a big role in SEO. In Angular SPAs, you must: </p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\">Update these tags dynamically as users navigate across different routes </span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Make sure they’re ready when crawlers arrive </span></p>\n</li>\n</ul>\n<p>Angular provides the <a href=\"https://angular.dev/api/platform-browser/Title\" rel=\"nofollow noopener\" target=\"_blank\"><strong>Title</strong></a> and <a href=\"https://angular.dev/api/platform-browser/Meta\" rel=\"nofollow noopener\" target=\"_blank\"><strong>Meta</strong></a> services to handle this. You can inject these services into any component that manages route-level metadata. Here’s an example:</p>\n<pre><code class=\"hljs language-kotlin\"><span class=\"hljs-keyword\">import</span> { Component, OnInit } from <span class=\"hljs-string\">'@angular/core'</span>;\n<span class=\"hljs-keyword\">import</span> { Title, Meta } from <span class=\"hljs-string\">'@angular/platform-browser'</span>;\n\n<span class=\"hljs-meta\">@Component({\n selector: <span class=\"hljs-string\">'app-about'</span>,\n templateUrl: <span class=\"hljs-string\">'./about.component.html'</span>,\n})</span>\nexport <span class=\"hljs-keyword\">class</span> <span class=\"hljs-title class_\">AboutComponent</span> <span class=\"hljs-title\">implements</span> <span class=\"hljs-title\">OnInit</span> {\n <span class=\"hljs-keyword\">constructor</span>(<span class=\"hljs-keyword\">private</span> title: Title, <span class=\"hljs-keyword\">private</span> meta: Meta) {}\n\n ngOnInit(): void {\n <span class=\"hljs-keyword\">this</span>.title.setTitle(<span class=\"hljs-string\">'About Us - My Angular App'</span>);\n <span class=\"hljs-keyword\">this</span>.meta.updateTag({ name: <span class=\"hljs-string\">'description'</span>, content: <span class=\"hljs-string\">'Learn more about our company, team, and mission.'</span> });\n <span class=\"hljs-keyword\">this</span>.meta.updateTag({ property: <span class=\"hljs-string\">'og:title'</span>, content: <span class=\"hljs-string\">'About Us - My Angular App'</span> });\n <span class=\"hljs-keyword\">this</span>.meta.updateTag({ property: <span class=\"hljs-string\">'og:description'</span>, content: <span class=\"hljs-string\">'Learn more about our company, team, and mission.'</span> });\n }\n}\n</code></pre>\n<p>In this example, we set the page title and meta description using Angular’s Title and Meta services. We also update Open Graph tags like <code>og:title</code> and<strong> </strong><code>og:description</code><strong> </strong>for better link previews. All this is run in <code>ngOnInit</code>, so tags update whenever the component loads.<span style=\"font-weight: 400;\"></span></p>\n<h3 id=\"implement\">Implement structured data (schema markup)</h3>\n<p>Structured data helps search engines understand the content of your pages better. In Angular, you can add schema markup using <a href=\"https://json-ld.org/\" rel=\"nofollow noopener\" target=\"_blank\">JSON-LD</a> by inserting it directly into the page with<a href=\"https://angular.dev/api/core/Renderer2\" rel=\"nofollow noopener\" target=\"_blank\"> Renderer2</a>. This lets the markup exist in the HTML sent from the server, which is important for SEO.</p>\n<p>Here’s a basic example:</p>\n<pre><code class=\"hljs language-typescript\"><span class=\"hljs-title function_\">constructor</span>(<span class=\"hljs-params\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-attr\">renderer</span>: <span class=\"hljs-title class_\">Renderer2</span>, <span class=\"hljs-keyword\">private</span> <span class=\"hljs-attr\">el</span>: <span class=\"hljs-title class_\">ElementRef</span></span>) {}\n\n<span class=\"hljs-title function_\">ngOnInit</span>(<span class=\"hljs-params\"></span>) {\n <span class=\"hljs-keyword\">const</span> schema = {\n <span class=\"hljs-string\">\"@context\"</span>: <span class=\"hljs-string\">\"https://schema.org\"</span>,\n <span class=\"hljs-string\">\"@type\"</span>: <span class=\"hljs-string\">\"Article\"</span>,\n <span class=\"hljs-string\">\"headline\"</span>: <span class=\"hljs-string\">\"Angular SEO Guide\"</span>,\n <span class=\"hljs-string\">\"author\"</span>: {\n <span class=\"hljs-string\">\"@type\"</span>: <span class=\"hljs-string\">\"Person\"</span>,\n <span class=\"hljs-string\">\"name\"</span>: <span class=\"hljs-string\">\"Your Name\"</span>\n },\n <span class=\"hljs-string\">\"datePublished\"</span>: <span class=\"hljs-string\">\"2025-08-06\"</span>\n };\n\n <span class=\"hljs-keyword\">const</span> script = <span class=\"hljs-variable language_\">this</span>.<span class=\"hljs-property\">renderer</span>.<span class=\"hljs-title function_\">createElement</span>(<span class=\"hljs-string\">'script'</span>);\n script.<span class=\"hljs-property\">type</span> = <span class=\"hljs-string\">'application/ld+json'</span>;\n script.<span class=\"hljs-property\">text</span> = <span class=\"hljs-title class_\">JSON</span>.<span class=\"hljs-title function_\">stringify</span>(schema);\n <span class=\"hljs-variable language_\">this</span>.<span class=\"hljs-property\">renderer</span>.<span class=\"hljs-title function_\">appendChild</span>(<span class=\"hljs-variable language_\">this</span>.<span class=\"hljs-property\">el</span>.<span class=\"hljs-property\">nativeElement</span>, script);\n}\n</code></pre>\n<p>This code adds schema.org structured data using JSON-LD inside a <code><script></code> tag. Search engines read this to understand the page content better.</p>\n<h3 id=\"create\">Create SEO-friendly URLs</h3>\n<p>Clean and readable URLs help both users and search engines understand what a page is about. Here are some tips on how to create them:</p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\">Use lowercase letters</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Separate words with hyphens (-)</span></p>\n</li>\n<li>\n<p>Avoid special characters and long query strings</p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Keep the URL short and descriptive</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Use meaningful paths like <code>/blog/angular-seo-tips</code></span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Define clear routes using Angular’s <strong>Router</strong> module</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Avoid changing URLs frequently to maintain link equity</span></p>\n</li>\n</ul>\n<h3 id=\"improve\">Improve page load speed</h3>\n<p>Page load speed affects both user experience and search rankings. Follow these tips to keep your app fast and responsive:</p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\">Use Angular’s built-in lazy loading to load modules only when needed</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Minify JavaScript, CSS, and HTML files using Angular CLI</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Enable HTTP compression on your server</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Optimize images using tools like <strong>ImageMagick</strong>, <strong>TinyPNG</strong>, or <strong>ImageKit</strong></span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Use a Content Delivery Network (CDN) to serve static assets faster</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Preload important resources using <code><link rel=\"preload\"></code></span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Audit and monitor performance with tools like <a href=\"https://developer.chrome.com/docs/lighthouse/overview\" rel=\"nofollow noopener\" target=\"_blank\">Lighthouse</a> or <a href=\"https://www.webpagetest.org/\" rel=\"nofollow noopener\" target=\"_blank\">WebPageTest</a></span></p>\n</li>\n</ul>\n<h3 id=\"generate\">Generate and submit a sitemap</h3>\n<p>As we already know, search engines can find it hard to crawl Angular apps because much of the content is loaded dynamically with JavaScript. This can lead to some pages not getting indexed. The solution? Sitemaps. </p>\n<p>A sitemap gives search engines a full list of available URLs, which ensures that your content is found and ranked properly.</p>\n<p>Here are some ways you can generate a sitemap to boost Angular SEO:</p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>If you’re using SSR:</strong> Set up a server route that returns a sitemap XML. You can pull URLs from your routes configuration or a database if your pages are dynamic.</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>If your app is fully static:</strong> Use plugins or scripts to crawl and collect routes, then generate the sitemap during your build process.</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>For dynamic content:</strong> Loop through your content and add their URLs to the sitemap output.</span></p>\n</li>\n</ul>\n<p>Then:</p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>Make sure the sitemap is accessible:</strong> Upload it to your domain (e.g., mydomain.com/sitemap.xml) and reference it in <code>robots.txt</code><strong>.</strong></span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\"><strong>Submit to search engines</strong>: Submit your sitemap to <a href=\"https://search.google.com/search-console/about\" rel=\"nofollow noopener\" target=\"_blank\">Google Search Console</a>, <a href=\"https://www.bing.com/webmaster\" rel=\"nofollow noopener\" target=\"_blank\">Bing Webmaster Tools</a><strong>, </strong>and<strong> </strong>any other relevant platforms.</span></p>\n</li>\n</ul>\n<h3 id=\"configure\">Configure robots.txt and meta robots tags </h3>\n<p>Search engines follow rules defined in your <code>robots.txt</code> file and meta robots tags to decide which parts of your site to crawl and index. Here are some tips on how to set them properly:</p>\n<h4><em>Robots.txt:</em></h4>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\">Place this file in the root of your domain (e.g., yourdomain.com/robots.txt)</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Use it to allow or disallow access to paths or folders</span></p>\n</li>\n</ul>\n<h4><em>Meta robots tags:</em></h4>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\">Add these tags in the <head> of your HTML to control indexing at the page level</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Useful for marking pages as <strong>noindex</strong>, <strong>nofollow</strong>, etc.</span></p>\n</li>\n</ul>\n<h2 id=\"best-seo-tools\">Best SEO tools for Angular developers</h2>\n<p>Finally, here are some tools that can help you test, audit, monitor, and improve your Angular SEO setup:</p>\n<h3>Google Search Console</h3>\n<p>A must-have for anyone running a website. Use it to:</p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\">Submit your sitemap for indexing</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Track keyword performance and search visibility</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Check for crawl errors, indexing issues, or mobile usability problems</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">See which pages are getting impressions and clicks</span></p>\n</li>\n</ul>\n<h3>Screaming Frog SEO Spider</h3>\n<p>A <a href=\"https://www.screamingfrog.co.uk/seo-spider/\" rel=\"nofollow noopener\" target=\"_blank\">desktop crawler</a> that lets you analyze your site like a search engine and catch any Angular SEO issues. Use it to:</p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\">Spot broken links or missing metadata</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Identify pages blocked by robots.txt</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">See how your Angular routes are being rendered</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Export reports for technical SEO fixes</span></p>\n</li>\n</ul>\n<h3>Lighthouse</h3>\n<p>An open-source tool that runs audits on performance and SEO. Use it to:</p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\">Measure page speed and SEO scores</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Get suggestions to fix slow-loading elements</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Check for best practices like proper heading structure and image alt text</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Identify issues with mobile usability</span></p>\n</li>\n</ul>\n<h3>Ahrefs/SEMrush </h3>\n<p><a href=\"https://ahrefs.com/\" rel=\"nofollow noopener\" target=\"_blank\">Ahrefs</a> and <a href=\"https://www.semrush.com/\" rel=\"nofollow noopener\" target=\"_blank\">SEMrush</a> are popular SEO platforms that offer deep keyword and traffic insights. Use them to:</p>\n<ul>\n<li>\n<p><span style=\"font-weight: 400;\">Track your keyword rankings over time</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Find content gaps and competitor opportunities</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">See what backlinks your site has (and which ones you’ve lost)</span></p>\n</li>\n<li>\n<p><span style=\"font-weight: 400;\">Monitor changes in traffic based on search algorithm updates</span></p>\n</li>\n</ul>\n<h2 id=\"conclusion\">Conclusion</h2>\n<p>Now that you know how to make Angular SEO friendly, you can start building responsive SPAs that rank just as well as traditional server-rendered websites. With ButterCMS, you can take things further by pulling in SEO tags, meta descriptions, and even full-page content through the API. Check out our <a href=\"/docs/api-client/angular\" rel=\"follow noopener\" target=\"_blank\">Angular API docs</a> to see how it works.</p>\n"],"summary":[0,"Building in Angular? Your site might look great to users but invisible to Google. Learn how to fix SEO blind spots with SSR, prerendering, and sitemaps so your site stays crawlable and rank-worthy."],"seoTitle":[0,"Angular SEO Guide: How to Make Search-Friendly Pages"],"metaDescription":[0,"Discover the Angular SEO guide to making your pages easily indexable and search-friendly for crawlers and end-users alike."],"readTimeMinutes":[0,10],"href":[0,"/blog/angular-seo-how-to-make-search-friendly-pages/"]}]]],"class":[0,"blog-post-page__footer"],"data-astro-cid-g5xfp254":[0,true]}" ssr client="visible" opts="{"name":"BlogPostFooter","value":{"rootMargin":"100px"}}" await-children><div class="palette-bg-main-tl-primary-dark blog-post-page__footer" data-astro-cid-g5xfp254="true" data-v-c2770f19><div class="blog-post-footer__header" data-v-c2770f19><div class="card-container card-container--left card" fullwidth="false" data-v-c2770f19 data-v-ca2e0e70 data-v-c06dbce0><!--[--><!----><div class="card__inner" data-v-ca2e0e70><!----><div class="card__content" data-v-ca2e0e70><div class="card__heading" data-v-ca2e0e70><!--[--><div class="heading--margin-bottom" style="--heading-font-size:var(--heading-2-font-size);" data-v-ca2e0e70 data-v-6b9b1130><!--[--><!----><!--]--><div class="title title--2" data-v-6b9b1130><!--[--><h2 class="title--internal" data-v-6b9b1130>Related Posts & Resources</h2><!--]--><!----></div><!--[--><!----><!--]--></div><!--]--></div><div class="" data-v-ca2e0e70><!--[--><div class="rich-text rich-text--no-margin" data-v-ca2e0e70>Explore more helpful content to build your best site!</div><!--]--></div><!--[--><!--]--><!----><!--[--><!----><!--]--></div></div><!--]--></div><a class="button palette-tertiary-filled palette-tertiary-filled-interactive blog-post-footer__actions-button--lg" style="" href="/blog/" data-v-c2770f19 data-v-ca7504de><div class="button__content button__content--sm" data-v-ca7504de><!----><!--[--><div class="button__text" data-v-ca7504de>View all resources</div><!--]--><!----></div><!----></a></div><div class="blog-post-footer__card-list" data-v-c2770f19><!--[--><div class="card-container card-container--left card blog-tile blog-post-footer__card-list-item" fullwidth="false" data-v-c2770f19 data-v-69ff57c4 data-v-ca2e0e70 data-v-c06dbce0><!--[--><div class="card__header-image" data-v-ca2e0e70><!--[--><!----><div class="blog-image blog-tile__image" data-v-69ff57c4 data-v-1ef83722><div class="t-image__container blog-image__image" style="--width-base:100%;--width-md:100%;--width-lg:100%;--width-xl:100%;--height-base:100%;--height-md:100%;--height-lg:100%;--height-xl:100%;" data-v-1ef83722><img src="https://cdn.buttercms.com/qhP0NLPoT2iwAzTlpq76" alt="Featured Image: Astro Tutorial - How to Build a Knowledge Base" loading="lazy" class="t-image t-image--fit-cover"></div><a class="blog-category blog-image__category" href="/blog/category/gsd/" data-v-1ef83722 data-v-25feb97e>GSD</a></div><!--]--></div><div class="card__inner" data-v-ca2e0e70><!----><div class="card__content" data-v-ca2e0e70><div class="card__heading card__heading--has-header" data-v-ca2e0e70><!--[--><div class="heading--margin-bottom blog-tile__heading" style="--heading-font-size:var(--heading-4-font-size);" data-v-ca2e0e70 data-v-6b9b1130><!--[--><!----><!--]--><div class="title title--4" data-v-6b9b1130><!--[--><a href="/blog/astro-tutorial-knowledge-base/" class="heading__title-link" data-v-6b9b1130><h4 class="title--internal" data-v-6b9b1130>How to build a knowledge base with Astro and ButterCMS</h4></a><!--]--><!----></div><!--[--><!----><!--]--></div><!--]--></div><div class="" data-v-ca2e0e70><!--[--><div style="--max-name-title-width:14em;" data-v-69ff57c4 data-v-ab490e41><a href="/blog/author/miracle-onyenma/" class="author__profile" data-v-ab490e41><div data-v-ab490e41><div class="avatar avatar--size-sm avatar--shape-circle" data-v-ab490e41 data-v-ce2c847c><img src="https://cdn.buttercms.com/FntrvXTJ6DoL0L6Dm0Lg" alt="Miracle Onyenma" class="avatar-image" data-v-ce2c847c></div></div><div data-v-ab490e41><div class="text text--size-md text--weight-medium text--no-margin author__name" data-v-ab490e41 data-v-b47df0cd><!--[-->Miracle Onyenma<!--]--></div><!----></div></a><a class="button button--link palette-primary-link palette-primary-link-interactive author__button" style="" href="/blog/astro-tutorial-knowledge-base/" rel="nofollow" link data-v-ab490e41 data-v-ca7504de><div class="button__content button__content--md" data-v-ca7504de><!----><!--[-->Read more<!--]--><!----></div><!----></a></div><!--]--></div><!--[--><!--]--><!----><!--[--><!----><!--]--></div></div><!--]--></div><div class="card-container card-container--left card blog-tile blog-post-footer__card-list-item" fullwidth="false" data-v-c2770f19 data-v-69ff57c4 data-v-ca2e0e70 data-v-c06dbce0><!--[--><div class="card__header-image" data-v-ca2e0e70><!--[--><!----><div class="blog-image blog-tile__image" data-v-69ff57c4 data-v-1ef83722><div class="t-image__container blog-image__image" style="--width-base:100%;--width-md:100%;--width-lg:100%;--width-xl:100%;--height-base:100%;--height-md:100%;--height-lg:100%;--height-xl:100%;" data-v-1ef83722><img src="https://cdn.buttercms.com/AxRIQyAYQkykyOSTP11M" alt="" loading="lazy" class="t-image t-image--fit-cover"></div><a class="blog-category blog-image__category" href="/blog/category/gsd/" data-v-1ef83722 data-v-25feb97e>GSD</a></div><!--]--></div><div class="card__inner" data-v-ca2e0e70><!----><div class="card__content" data-v-ca2e0e70><div class="card__heading card__heading--has-header" data-v-ca2e0e70><!--[--><div class="heading--margin-bottom blog-tile__heading" style="--heading-font-size:var(--heading-4-font-size);" data-v-ca2e0e70 data-v-6b9b1130><!--[--><!----><!--]--><div class="title title--4" data-v-6b9b1130><!--[--><a href="/blog/angular-seo-how-to-make-search-friendly-pages/" class="heading__title-link" data-v-6b9b1130><h4 class="title--internal" data-v-6b9b1130>Angular SEO: How to Make Search-Friendly Pages</h4></a><!--]--><!----></div><!--[--><!----><!--]--></div><!--]--></div><div class="" data-v-ca2e0e70><!--[--><div style="--max-name-title-width:14em;" data-v-69ff57c4 data-v-ab490e41><a href="/blog/author/maab-saleem/" class="author__profile" data-v-ab490e41><div data-v-ab490e41><div class="avatar avatar--size-sm avatar--shape-circle" data-v-ab490e41 data-v-ce2c847c><img src="https://cdn.buttercms.com/kv1shOhhRjv1voK45csv" alt="Maab Saleem" class="avatar-image" data-v-ce2c847c></div></div><div data-v-ab490e41><div class="text text--size-md text--weight-medium text--no-margin author__name" data-v-ab490e41 data-v-b47df0cd><!--[-->Maab Saleem<!--]--></div><!----></div></a><a class="button button--link palette-primary-link palette-primary-link-interactive author__button" style="" href="/blog/angular-seo-how-to-make-search-friendly-pages/" rel="nofollow" link data-v-ab490e41 data-v-ca7504de><div class="button__content button__content--md" data-v-ca7504de><!----><!--[-->Read more<!--]--><!----></div><!----></a></div><!--]--></div><!--[--><!--]--><!----><!--[--><!----><!--]--></div></div><!--]--></div><!--]--><form id="cms-guide-signup" class="simple-form palette-bg-main-tl-primary-dark blog-post-footer__cms-guide-signup--lg blog-post-footer__card-list-item" data-v-c2770f19 data-v-8e294739 data-v-2d271486><div class="card-container card-container--left card-container--rounded card-container--border card" fullwidth="false" data-v-2d271486 data-v-ca2e0e70 data-v-c06dbce0><!--[--><!----><div class="card__inner cms-guide-signup card__inner--with-padding" data-v-ca2e0e70><!----><div class="card__content" data-v-ca2e0e70><div class="card__heading" data-v-ca2e0e70><!--[--><div class="heading--margin-bottom" style="--heading-font-size:var(--heading-4-font-size);" data-v-ca2e0e70 data-v-6b9b1130><!--[--><!----><!--]--><div class="title title--4" data-v-6b9b1130><!--[--><h4 class="title--internal" data-v-6b9b1130>New to Headless CMS? Check out our Guide!</h4><!--]--><!----></div><!--[--><!----><!--]--></div><!--]--></div><div class="" data-v-ca2e0e70><!--[--><div class="rich-text" data-v-2d271486>Get a step-by-step walkthrough of how to determine whether Headless CMS is a good option for your team and what to do next to enjoy the benefits!</div><div class="input-comment" data-v-2d271486 data-v-0937b6fd><label for="text-input--cms-guide-signup-comment" data-v-0937b6fd>Comment</label><input id="text-input--cms-guide-signup-comment" name="comment" placeholder="Enter your comments" value="" data-v-0937b6fd></div><div class="input-text" data-v-2d271486 data-v-bfa6ee83><label for="text-input--cms-guide-signup-email" class="input-text__label" data-v-bfa6ee83><span class="input-text__asterisk" aria-hidden="true" data-v-bfa6ee83>*</span> Email</label><input id="text-input--cms-guide-signup-email" name="email" type="email" aria-describedby="text-input-hint--cms-guide-signup-email" aria-invalid="false" class="input-text__input cms-guide-signup__input" autocomplete="on" data-v-bfa6ee83><div class="input-text__message" data-v-bfa6ee83><!----></div></div><!--]--></div><!--[--><!--]--><div class="card__footer--has-content single-input-form__actions" data-v-ca2e0e70><!--[--><div class="card__actions card__actions--left" data-v-ca2e0e70><!--[--><button class="button palette-primary-filled palette-primary-filled-interactive" style="" type="submit" data-v-ca2e0e70 data-v-ca7504de><div class="button__content button__content--md" data-v-ca7504de><!----><!--[--><div class="button__text" data-v-ca7504de>Get My Guide</div><!--]--><!----></div><!----></button><!--]--></div><!--]--></div><!--[--><div class="rich-text" data-v-2d271486><p><small>By subscribing you agree to our <a href="/privacy">Privacy Policy</a>.</small></p></div><!--]--></div></div><!--]--></div></form></div><div class="blog-post-footer__actions" data-v-c2770f19><a class="button palette-tertiary-filled palette-tertiary-filled-interactive" style="" href="/blog/" data-v-c2770f19 data-v-ca7504de><div class="button__content button__content--sm" data-v-ca7504de><!----><!--[--><div class="button__text" data-v-ca7504de>View all resources</div><!--]--><!----></div><!----></a><form id="cms-guide-signup" class="simple-form palette-bg-main-tl-primary-dark" data-v-c2770f19 data-v-8e294739 data-v-2d271486><div class="card-container card-container--left card-container--rounded card-container--border card" fullwidth="false" data-v-2d271486 data-v-ca2e0e70 data-v-c06dbce0><!--[--><!----><div class="card__inner cms-guide-signup card__inner--with-padding" data-v-ca2e0e70><!----><div class="card__content" data-v-ca2e0e70><div class="card__heading" data-v-ca2e0e70><!--[--><div class="heading--margin-bottom" style="--heading-font-size:var(--heading-4-font-size);" data-v-ca2e0e70 data-v-6b9b1130><!--[--><!----><!--]--><div class="title title--4" data-v-6b9b1130><!--[--><h4 class="title--internal" data-v-6b9b1130>New to Headless CMS? Check out our Guide!</h4><!--]--><!----></div><!--[--><!----><!--]--></div><!--]--></div><div class="" data-v-ca2e0e70><!--[--><div class="rich-text" data-v-2d271486>Get a step-by-step walkthrough of how to determine whether Headless CMS is a good option for your team and what to do next to enjoy the benefits!</div><div class="input-comment" data-v-2d271486 data-v-0937b6fd><label for="text-input--cms-guide-signup-comment" data-v-0937b6fd>Comment</label><input id="text-input--cms-guide-signup-comment" name="comment" placeholder="Enter your comments" value="" data-v-0937b6fd></div><div class="input-text" data-v-2d271486 data-v-bfa6ee83><label for="text-input--cms-guide-signup-email" class="input-text__label" data-v-bfa6ee83><span class="input-text__asterisk" aria-hidden="true" data-v-bfa6ee83>*</span> Email</label><input id="text-input--cms-guide-signup-email" name="email" type="email" aria-describedby="text-input-hint--cms-guide-signup-email" aria-invalid="false" class="input-text__input cms-guide-signup__input" autocomplete="on" data-v-bfa6ee83><div class="input-text__message" data-v-bfa6ee83><!----></div></div><!--]--></div><!--[--><!--]--><div class="card__footer--has-content single-input-form__actions" data-v-ca2e0e70><!--[--><div class="card__actions card__actions--left" data-v-ca2e0e70><!--[--><button class="button palette-primary-filled palette-primary-filled-interactive" style="" type="submit" data-v-ca2e0e70 data-v-ca7504de><div class="button__content button__content--md" data-v-ca7504de><!----><!--[--><div class="button__text" data-v-ca7504de>Get My Guide</div><!--]--><!----></div><!----></button><!--]--></div><!--]--></div><!--[--><div class="rich-text" data-v-2d271486><p><small>By subscribing you agree to our <a href="/privacy">Privacy Policy</a>.</small></p></div><!--]--></div></div><!--]--></div></form></div></div><!--astro:end--></astro-island> </div> <!--]--></div> <astro-island uid="Znkmd2" prefix="s7" component-url="/_astro/TBFooter.XPDFj-cG.js" component-export="default" renderer-url="/_astro/client.BvFgaP12.js" props="{"logo":[0,{"src":[0,"https://cdn.buttercms.com/2a1lrRd1RBGOAMUoCaG1"],"alt":[0,"ButterCMS Logo"],"width":[0,{"base":[0,"140px"],"md":[0,"160px"]}],"height":[0,{"base":[0,"100%"]}]}],"socialLinks":[1,[[0,{"href":[0,"https://linkedin.com/company/buttercms/"],"icon":[0,"linkedin"]}],[0,{"href":[0,"https://x.com/buttercms"],"icon":[0,"twitter"]}],[0,{"href":[0,"https://www.youtube.com/@buttercmsfm"],"icon":[0,"youtube"]}],[0,{"href":[0,"https://github.com/buttercms"],"icon":[0,"github"]}],[0,{"href":[0,"https://www.g2.com/products/butter-cms/reviews"],"icon":[0,"g2"],"iconGroup":[0,"tt"]}]]],"columns":[1,[[0,{"label":[0,"Product"],"items":[1,[[0,{"label":[0,"Headless CMS"],"href":[0,"/headless-cms/"]}],[0,{"label":[0,"Capabilities"],"href":[0,"/capabilities/"]}],[0,{"label":[0,"Features"],"href":[0,"/features/"]}],[0,{"label":[0,"Integrations"],"href":[0,"/marketplace/"]}],[0,{"label":[0,"API"],"href":[0,"/docs/api/"]}]]]}],[0,{"label":[0,"Developers"],"items":[1,[[0,{"label":[0,"Knowledge Base"],"href":[0,"/knowledge-base/"]}],[0,{"label":[0,"Developer Docs"],"href":[0,"/docs/"]}],[0,{"label":[0,"API Documentation"],"href":[0,"/docs/api/"]}],[0,{"label":[0,"Developer Hub"],"href":[0,"/developers-hub/"]}],[0,{"label":[0,"Starter Projects"],"href":[0,"/starters/"]}],[0,{"label":[0,"API Status"],"href":[0,"https://status.buttercms.com/"]}]]]}],[0,{"label":[0,"Explore"],"items":[1,[[0,{"label":[0,"Pricing"],"href":[0,"/pricing/"]}],[0,{"label":[0,"Partners"],"href":[0,"/partners/"]}],[0,{"label":[0,"Security"],"href":[0,"/security/"]}],[0,{"label":[0,"Careers"],"href":[0,"https://apply.workable.com/buttercms/"]}],[0,{"label":[0,"Contact Us"],"href":[0,"/sales/"]}]]]}],[0,{"label":[0,"Resources"],"items":[1,[[0,{"label":[0,"Blog"],"href":[0,"/blog/"]}],[0,{"label":[0,"Podcast"],"href":[0,"/knowledge-base/category/podcasts/"]}],[0,{"label":[0,"WordPress Alternative"],"href":[0,"/wordpress-alternative/"]}],[0,{"label":[0,"Contentful Alternative"],"href":[0,"/contentful-alternatives/"]}],[0,{"label":[0,"Medium Alternative"],"href":[0,"/medium-alternative/"]}],[0,{"label":[0,"Strapi Alternative"],"href":[0,"/strapi-alternatives/"]}]]]}]]],"copyrightText":[0,"© 2025 ButterCMS. All rights reserved."],"legalLinks":[1,[[0,{"label":[0,"Privacy Policy"],"href":[0,"/privacy/"]}],[0,{"label":[0,"Terms of Use"],"href":[0,"/terms/"]}]]],"data-astro-cid-g5xfp254":[0,true]}" ssr client="visible" opts="{"name":"TBFooter","value":{"rootMargin":"100px"}}" await-children><footer class="page-content-container footer page-content-container--padding-top-block page-content-container--padding-bottom-block" data-astro-cid-g5xfp254="true" data-v-7b1d6696 data-v-7f4ce890><!--[--><div class="footer__desktop" data-v-7b1d6696><div class="footer__desktop-main-content" data-v-7b1d6696><div class="footer-brand footer__brand--desktop" data-v-7b1d6696 data-v-6b33ae86><a href="/" data-v-6b33ae86><div class="t-image__container" style="--width-base:140px;--width-md:160px;--width-lg:160px;--width-xl:160px;--height-base:100%;--height-md:100%;--height-lg:100%;--height-xl:100%;" data-v-6b33ae86><img src="https://cdn.buttercms.com/2a1lrRd1RBGOAMUoCaG1" alt="ButterCMS Logo" loading="lazy" class="t-image t-image--fit-contain"></div></a><div class="footer-brand__social" data-v-6b33ae86><!--[--><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://linkedin.com/company/buttercms/" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="pi pi-linkedin"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://x.com/buttercms" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="pi pi-twitter"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://www.youtube.com/@buttercmsfm" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="pi pi-youtube"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://github.com/buttercms" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="pi pi-github"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://www.g2.com/products/butter-cms/reviews" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="tt-icon tt-icon--g2"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><!--]--></div></div><div class="footer__desktop-columns-container" data-v-7b1d6696><div class="footer-columns-desktop footer__desktop-columns" data-v-7b1d6696 data-v-2258fe01><!--[--><div data-v-2258fe01><p class="text text--size-md text--weight-bold text--no-margin footer-columns-desktop__column-title" data-v-2258fe01 data-v-b47df0cd><!--[-->Product<!--]--></p><div class="footer-columns-desktop__column-items" data-v-2258fe01><!--[--><a href="/headless-cms/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Headless CMS</span><!--[--><!----><!--]--><!--]--></a><a href="/capabilities/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Capabilities</span><!--[--><!----><!--]--><!--]--></a><a href="/features/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Features</span><!--[--><!----><!--]--><!--]--></a><a href="/marketplace/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Integrations</span><!--[--><!----><!--]--><!--]--></a><a href="/docs/api/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>API</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div><div data-v-2258fe01><p class="text text--size-md text--weight-bold text--no-margin footer-columns-desktop__column-title" data-v-2258fe01 data-v-b47df0cd><!--[-->Developers<!--]--></p><div class="footer-columns-desktop__column-items" data-v-2258fe01><!--[--><a href="/knowledge-base/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Knowledge Base</span><!--[--><!----><!--]--><!--]--></a><a href="/docs/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Developer Docs</span><!--[--><!----><!--]--><!--]--></a><a href="/docs/api/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>API Documentation</span><!--[--><!----><!--]--><!--]--></a><a href="/developers-hub/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Developer Hub</span><!--[--><!----><!--]--><!--]--></a><a href="/starters/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Starter Projects</span><!--[--><!----><!--]--><!--]--></a><a href="https://status.buttercms.com/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>API Status</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div><div data-v-2258fe01><p class="text text--size-md text--weight-bold text--no-margin footer-columns-desktop__column-title" data-v-2258fe01 data-v-b47df0cd><!--[-->Explore<!--]--></p><div class="footer-columns-desktop__column-items" data-v-2258fe01><!--[--><a href="/pricing/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Pricing</span><!--[--><!----><!--]--><!--]--></a><a href="/partners/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Partners</span><!--[--><!----><!--]--><!--]--></a><a href="/security/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Security</span><!--[--><!----><!--]--><!--]--></a><a href="https://apply.workable.com/buttercms/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Careers</span><!--[--><!----><!--]--><!--]--></a><a href="/sales/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Contact Us</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div><div data-v-2258fe01><p class="text text--size-md text--weight-bold text--no-margin footer-columns-desktop__column-title" data-v-2258fe01 data-v-b47df0cd><!--[-->Resources<!--]--></p><div class="footer-columns-desktop__column-items" data-v-2258fe01><!--[--><a href="/blog/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Blog</span><!--[--><!----><!--]--><!--]--></a><a href="/knowledge-base/category/podcasts/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Podcast</span><!--[--><!----><!--]--><!--]--></a><a href="/wordpress-alternative/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>WordPress Alternative</span><!--[--><!----><!--]--><!--]--></a><a href="/contentful-alternatives/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Contentful Alternative</span><!--[--><!----><!--]--><!--]--></a><a href="/medium-alternative/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Medium Alternative</span><!--[--><!----><!--]--><!--]--></a><a href="/strapi-alternatives/" class="nav-button nav-button--padding palette-button palette-button-interactive nav-button--align-to-left footer-columns-desktop__column-item" data-v-2258fe01 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Strapi Alternative</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div><!--]--></div></div></div><div class="footer-legal footer__legal--desktop" data-v-7b1d6696 data-v-44e18308><p class="text text--size-sm text--weight-regular text--no-margin" color="muted" data-v-44e18308 data-v-b47df0cd><!--[-->© 2025 ButterCMS. All rights reserved.<!--]--></p><div class="footer-legal__links" data-v-44e18308><!--[--><a href="/privacy/" class="nav-button nav-button--padding palette-link palette-link-interactive" data-v-44e18308 data-v-d77ff174><!--[--><span class="" data-v-d77ff174>Privacy Policy</span><!--[--><!----><!--]--><!--]--></a><a href="/terms/" class="nav-button nav-button--padding palette-link palette-link-interactive" data-v-44e18308 data-v-d77ff174><!--[--><span class="" data-v-d77ff174>Terms of Use</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div></div><div class="footer__mobile" data-v-7b1d6696><div data-v-7b1d6696 data-v-d99ebee8><!--[--><div class="footer-columns-mobile__section" data-v-d99ebee8><button class="nav-button nav-button--padding palette-footer-mobile-menu-item palette-footer-mobile-menu-item-interactive footer-columns-mobile__menu-item" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Product</span><!--[--><i class="pi pi-chevron-down footer-columns-mobile__dropdown-icon" data-v-d99ebee8></i><!--]--><!--]--></button><div class="footer-columns-mobile__submenu" data-v-d99ebee8><div class="footer-columns-mobile__submenu-content" data-v-d99ebee8><div class="footer-columns-mobile__column" data-v-d99ebee8><!--[--><a href="/headless-cms/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Headless CMS</span><!--[--><!----><!--]--><!--]--></a><a href="/capabilities/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Capabilities</span><!--[--><!----><!--]--><!--]--></a><a href="/features/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Features</span><!--[--><!----><!--]--><!--]--></a><a href="/marketplace/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Integrations</span><!--[--><!----><!--]--><!--]--></a><a href="/docs/api/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>API</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div></div></div><div class="footer-columns-mobile__section" data-v-d99ebee8><button class="nav-button nav-button--padding palette-footer-mobile-menu-item palette-footer-mobile-menu-item-interactive footer-columns-mobile__menu-item" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Developers</span><!--[--><i class="pi pi-chevron-down footer-columns-mobile__dropdown-icon" data-v-d99ebee8></i><!--]--><!--]--></button><div class="footer-columns-mobile__submenu" data-v-d99ebee8><div class="footer-columns-mobile__submenu-content" data-v-d99ebee8><div class="footer-columns-mobile__column" data-v-d99ebee8><!--[--><a href="/knowledge-base/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Knowledge Base</span><!--[--><!----><!--]--><!--]--></a><a href="/docs/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Developer Docs</span><!--[--><!----><!--]--><!--]--></a><a href="/docs/api/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>API Documentation</span><!--[--><!----><!--]--><!--]--></a><a href="/developers-hub/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Developer Hub</span><!--[--><!----><!--]--><!--]--></a><a href="/starters/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Starter Projects</span><!--[--><!----><!--]--><!--]--></a><a href="https://status.buttercms.com/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>API Status</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div></div></div><div class="footer-columns-mobile__section" data-v-d99ebee8><button class="nav-button nav-button--padding palette-footer-mobile-menu-item palette-footer-mobile-menu-item-interactive footer-columns-mobile__menu-item" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Explore</span><!--[--><i class="pi pi-chevron-down footer-columns-mobile__dropdown-icon" data-v-d99ebee8></i><!--]--><!--]--></button><div class="footer-columns-mobile__submenu" data-v-d99ebee8><div class="footer-columns-mobile__submenu-content" data-v-d99ebee8><div class="footer-columns-mobile__column" data-v-d99ebee8><!--[--><a href="/pricing/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Pricing</span><!--[--><!----><!--]--><!--]--></a><a href="/partners/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Partners</span><!--[--><!----><!--]--><!--]--></a><a href="/security/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Security</span><!--[--><!----><!--]--><!--]--></a><a href="https://apply.workable.com/buttercms/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Careers</span><!--[--><!----><!--]--><!--]--></a><a href="/sales/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Contact Us</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div></div></div><div class="footer-columns-mobile__section" data-v-d99ebee8><button class="nav-button nav-button--padding palette-footer-mobile-menu-item palette-footer-mobile-menu-item-interactive footer-columns-mobile__menu-item" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Resources</span><!--[--><i class="pi pi-chevron-down footer-columns-mobile__dropdown-icon" data-v-d99ebee8></i><!--]--><!--]--></button><div class="footer-columns-mobile__submenu" data-v-d99ebee8><div class="footer-columns-mobile__submenu-content" data-v-d99ebee8><div class="footer-columns-mobile__column" data-v-d99ebee8><!--[--><a href="/blog/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Blog</span><!--[--><!----><!--]--><!--]--></a><a href="/knowledge-base/category/podcasts/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Podcast</span><!--[--><!----><!--]--><!--]--></a><a href="/wordpress-alternative/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>WordPress Alternative</span><!--[--><!----><!--]--><!--]--></a><a href="/contentful-alternatives/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Contentful Alternative</span><!--[--><!----><!--]--><!--]--></a><a href="/medium-alternative/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Medium Alternative</span><!--[--><!----><!--]--><!--]--></a><a href="/strapi-alternatives/" class="nav-button nav-button--padding palette-button palette-button-interactive" data-v-d99ebee8 data-v-d77ff174><!--[--><span class="nav-button__label--bold" data-v-d77ff174>Strapi Alternative</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div></div></div><!--]--></div><div class="footer-brand" data-v-7b1d6696 data-v-6b33ae86><a href="/" data-v-6b33ae86><div class="t-image__container" style="--width-base:140px;--width-md:160px;--width-lg:160px;--width-xl:160px;--height-base:100%;--height-md:100%;--height-lg:100%;--height-xl:100%;" data-v-6b33ae86><img src="https://cdn.buttercms.com/2a1lrRd1RBGOAMUoCaG1" alt="ButterCMS Logo" loading="lazy" class="t-image t-image--fit-contain"></div></a><div class="footer-brand__social" data-v-6b33ae86><!--[--><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://linkedin.com/company/buttercms/" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="pi pi-linkedin"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://x.com/buttercms" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="pi pi-twitter"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://www.youtube.com/@buttercmsfm" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="pi pi-youtube"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://github.com/buttercms" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="pi pi-github"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><a class="button button--link palette-neutral-link palette-neutral-link-interactive" style="" href="https://www.g2.com/products/butter-cms/reviews" data-v-6b33ae86 data-v-ca7504de><div class="button__content button__content--lg" data-v-ca7504de><span class="" data-v-ca7504de><!--[--><span data-v-ca7504de><i class="tt-icon tt-icon--g2"></i></span><!--]--></span><!--[--><div class="button__text" data-v-ca7504de></div><!--]--><!----></div><!----></a><!--]--></div></div><div class="footer-legal" data-v-7b1d6696 data-v-44e18308><p class="text text--size-sm text--weight-regular text--no-margin" color="muted" data-v-44e18308 data-v-b47df0cd><!--[-->© 2025 ButterCMS. All rights reserved.<!--]--></p><div class="footer-legal__links" data-v-44e18308><!--[--><a href="/privacy/" class="nav-button nav-button--padding palette-link palette-link-interactive" data-v-44e18308 data-v-d77ff174><!--[--><span class="" data-v-d77ff174>Privacy Policy</span><!--[--><!----><!--]--><!--]--></a><a href="/terms/" class="nav-button nav-button--padding palette-link palette-link-interactive" data-v-44e18308 data-v-d77ff174><!--[--><span class="" data-v-d77ff174>Terms of Use</span><!--[--><!----><!--]--><!--]--></a><!--]--></div></div></div><!--]--></footer><!--astro:end--></astro-island> </body></html>