How to Integrate Google Blogger API with Next.js (Step-by-Step Guide)

If you’ve ever wanted to fetch and display your Blogger posts inside a modern Next.js application, this tutorial will walk you through the entire process — from setting up your environment to fetching and displaying live posts.


Step 1: Create a New Next.js Project

Open your terminal and navigate to the folder where you want your project to live. Then run:

npx create-next-app@latest blogger-integration

This will create a new folder named blogger-integration with a fresh Next.js setup.


Step 2: Setup Environment Variables

Inside your project, create a file named .env.local and add the following variables:

NEXT_PUBLIC_BLOGGER_BLOG_ID=1234567890123456789
BLOGGER_API_KEY=AIza...your_api_key_here

These variables will store your Blogger Blog ID and Google API key securely.


Step 3: Enable Blogger API and Generate API Key

  1. Go to Google Cloud Console.
  2. Create a new project (or select an existing one).
  3. From the left sidebar, navigate to APIs & Services → Library.
  4. Search for Blogger API v3 and click Enable.
  5. Next, go to APIs & Services → Credentials.
  6. Click Create Credentials → API key.
  7. Restrict the key to Blogger API and click Create.
  8. Copy your new API key and paste it inside .env.local as BLOGGER_API_KEY.

Step 4: Get Your Blogger Blog ID

  1. Open your Blogger dashboard.
  2. Choose your blog and check the URL — it will look like this:
https://www.blogger.com/blog/posts/{blog-id}

Copy the blog ID part and paste it into your .env.local file as NEXT_PUBLIC_BLOGGER_BLOG_ID.


Step 5: Create the API Route

Inside your project, create a file at:

app/api/blog/route.ts

Then paste this code:

import { NextResponse } from 'next/server';

const BLOG_ID = process.env.NEXT_PUBLIC_BLOGGER_BLOG_ID;
const API_KEY = process.env.BLOGGER_API_KEY;

export async function GET(request: Request) {
  if (!BLOG_ID || !API_KEY) {
    return NextResponse.json(
      { error: 'Missing BLOG_ID or API_KEY in environment' },
      { status: 500 }
    );
  }

  const url = new URL(`https://www.googleapis.com/blogger/v3/blogs/${BLOG_ID}/posts`);
  url.searchParams.set('key', API_KEY);

  const incoming = new URL(request.url);
  incoming.searchParams.forEach((value, name) => {
    if (name === 'key') return;
    url.searchParams.set(name, value);
  });

  try {
    const res = await fetch(url.toString());
    if (!res.ok) {
      const text = await res.text();
      return NextResponse.json({ error: 'Upstream error', details: text }, { status: res.status });
    }
    const data = await res.json();
    return NextResponse.json(data);
  } catch (err: any) {
    return NextResponse.json({ error: 'Fetch failed', message: err.message || String(err) }, { status: 500 });
  }
}

What this does:

  • Reads your Blogger Blog ID and API key from environment variables.
  • Calls the official Google Blogger API to fetch posts.
  • Returns clean JSON data to your frontend.

Step 6: Display Posts on the Home Page

Now open app/page.tsx and replace its content with:

import React from 'react';
import PostList from '../components/PostList';

type Post = {
  id: string;
  title: string;
  content?: string;
  published?: string;
  url?: string;
  author?: { displayName?: string } | null;
};

async function fetchPosts() {
  const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ||
    (process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : 'http://localhost:3000');

  const res = await fetch(`${baseUrl}/api/blog?maxResults=10`, { cache: 'no-store' });

  if (!res.ok) {
    const text = await res.text();
    throw new Error(`Failed to fetch posts: ${res.status} ${text}`);
  }

  const data = await res.json();
  return data.items ?? [];
}

export default async function Page() {
  let posts: Post[] = [];
  try {
    posts = await fetchPosts();
  } catch (e: any) {
    console.error('Error fetching posts', e);
  }

  return (
    <main className="p-6 max-w-3xl mx-auto">
      <h1 className="text-3xl font-bold mb-4">My Blogger Posts</h1>
      {posts.length === 0 ? (
        <p>No posts found or failed to load.</p>
      ) : (
        <PostList posts={posts} />
      )}
    </main>
  );
}

This dynamically fetches your posts from the API route and passes them to a PostList component for display.


Step 7: Create the PostList Component

Make a new file:

components/PostList.tsx

Paste this code:

'use client';

import React from 'react';

type Post = {
  id: string;
  title: string;
  content?: string;
  published?: string;
  url?: string;
  author?: { displayName?: string } | null;
};

export default function PostList({ posts }: { posts: Post[] }) {
  return (
    <div className="space-y-6">
      {posts.map((p) => (
        <article key={p.id} className="border rounded-lg p-4 shadow-sm">
          <a href={p.url} target="_blank" rel="noopener noreferrer">
            <h2 className="text-xl font-semibold">{p.title}</h2>
          </a>
          <p className="text-sm text-gray-500">
            {p.author?.displayName}{p.published ? new Date(p.published).toLocaleString() : ''}
          </p>
          <div className="mt-2 prose max-w-none" dangerouslySetInnerHTML={{ __html: p.content ?? '' }} />
        </article>
      ))}
    </div>
  );
}

This component:

  • Loops through posts and displays them nicely using Tailwind CSS.
  • Renders each post’s title, author, date, and content.
  • Uses dangerouslySetInnerHTML to safely show Blogger’s HTML-formatted content.

Step 8: Optional — Client-side Example Page

Create a file:

app/client-example/page.tsx

Add this:

'use client';

import React, { useEffect, useState } from 'react';

export default function ClientExample() {
  const [posts, setPosts] = useState<any[]>([]);
  const [loading, setLoading] = useState(false);
  const [err, setErr] = useState<string | null>(null);

  useEffect(() => {
    setLoading(true);
    fetch('/api/blog?maxResults=5')
      .then((r) => {
        if (!r.ok) throw new Error(`Status ${r.status}`);
        return r.json();
      })
      .then((data) => setPosts(data.items ?? []))
      .catch((e) => setErr(String(e)))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <p>Loading...</p>;
  if (err) return <p>Error: {err}</p>;

  return (
    <div className="p-4">
      <h2 className="text-2xl mb-3">Client-side posts</h2>
      {posts.map((p) => (
        <div key={p.id} className="mb-4">
          <a href={p.url} target="_blank" rel="noreferrer">
            <h3 className="font-medium">{p.title}</h3>
          </a>
        </div>
      ))}
    </div>
  );
}

This shows how to fetch and render posts client-side using React hooks.


Step 9: Run the Application

Now it’s time to test everything!
Run the development server:

npm run dev

Then open your browser and visit:

http://localhost:3000

You should now see all your Blogger posts fetched and displayed dynamically inside your Next.js app!


Final Thoughts

In this guide, you learned how to:

  • Create a Next.js project
  • Setup Blogger API access
  • Securely use environment variables
  • Build API routes to connect with Google Blogger
  • Fetch and render posts both server-side and client-side

With this setup, you can easily extend your app to:

  • Add pagination
  • Filter or search posts
  • Style your posts using Tailwind
  • Deploy to Vercel for instant live hosting