Getting your content to your Next.js site is as simple as making an API request to the MarbleCMS API. This guide will walk you through setting up a blog with MarbleCMS and Next.js. If you want to get started quickly, you can clone our Next.js blog template.
1

Set up environment variables

First, you’ll need to add your MarbleCMS API URL and Workspace Key to your environment variables. Create a .env.local file in the root of your Next.js project and add the following:
.env.local
MARBLE_API_URL=https://api.marblecms.com/v1
MARBLE_WORKSPACE_KEY=your_workspace_key_here
Important: Never expose your MARBLE_WORKSPACE_KEY in client-side code. Your environment variables should only be accessed on the server during the build process. For client-side updates, use webhooks to trigger rebuilds.
You can find your Workspace Key in your MarbleCMS dashboard under the settings for your workspace.
2

Create API utility functions

To make it easier to fetch data from MarbleCMS, you can create a utility file to wrap your API calls. This keeps your data-fetching logic organized and reusable.Create a file at lib/query.js (or .ts if you’re using TypeScript).
lib/query.js
const url = process.env.MARBLE_API_URL;
const key = process.env.MARBLE_WORKSPACE_KEY;

export async function getPosts() {
  try {
    const raw = await fetch(`${url}/${key}/posts`);
    const data = await raw.json();
    return data;
  } catch (error) {
    console.log(error);
  }
}

export async function getSinglePost(slug) {
  try {
    const raw = await fetch(`${url}/${key}/posts/${slug}`);
    const data = await raw.json();
    return data;
  } catch (error) {
    console.log(error);
  }
}

export async function getTags() {
  try {
    const raw = await fetch(`${url}/${key}/tags`);
    const data = await raw.json();
    return data;
  } catch (error) {
    console.log(error);
  }
}

export async function getCategories() {
  try {
    const raw = await fetch(`${url}/${key}/categories`);
    const data = await raw.json();
    return data;
  } catch (error) {
    console.log(error);
  }
}

export async function getAuthors() {
  try {
    const raw = await fetch(`${url}/${key}/authors`);
    const data = await raw.json();
    return data;
  } catch (error)
    console.log(error);
  }
}
3

Display a list of posts

Now you can use the getPosts function in a Next.js page to fetch and display a list of your blog posts. For example, in app/blog/page.jsx:
app/blog/page.jsx
import { getPosts } from "@/lib/query";

// Revalidate this page every hour
export const revalidate = 3600;

export default async function BlogPage() {
  const { posts } = await getPosts();

  return (
    <section>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <a href={`/blog/${post.slug}`}>{post.title}</a>
          </li>
        ))}
      </ul>
    </section>
  );
}
4

Display a single post

To display a single post, you can create a dynamic route in Next.js, for example at app/blog/[slug]/page.jsx. This page will use the getSinglePost function to fetch the content for a specific post.
app/blog/[slug]/page.jsx
import { getSinglePost } from "@/lib/query";

export default async function PostPage({ params }) {
  const { post } = await getSinglePost(params.slug);

  return (
    <div>
      <h1>{post.title}</h1>
      <article dangerouslySetInnerHTML={{ __html: post.content }} />
    </div>
  );
}
A note on dangerouslySetInnerHTMLWe use dangerouslySetInnerHTML to render the HTML content of your post. MarbleCMS sanitizes all HTML content before it’s sent to you, so you can be confident that it’s safe to render in your application.