LESSON 2 ⏱️ 10 min read

Setting Up Your Next.js Project

Creating a New Next.js Project

Let's start by creating a fresh Next.js project with TypeScript support.

Open your terminal and run:

npx create-next-app@latest my-headless-blog

When prompted, select these options:

✔ Would you like to use TypeScript? → Yes
✔ Would you like to use ESLint? → Yes
✔ Would you like to use Tailwind CSS? → Yes
✔ Would you like to use `src/` directory? → Yes
✔ Would you like to use App Router? → Yes
✔ Would you like to customize the default import alias? → No

Navigate into your project:

cd my-headless-blog

Project Structure

Your new project should look like this:

my-headless-blog/
├── src/
│   ├── app/
│   │   ├── layout.tsx      # Root layout
│   │   ├── page.tsx        # Homepage
│   │   └── globals.css     # Global styles
│   └── ...
├── public/                  # Static assets
├── package.json
├── tailwind.config.ts
├── tsconfig.json
└── next.config.js

Setting Up Environment Variables

Create a .env.local file in your project root:

touch .env.local

Add your WordPress site URL:

# .env.local
NEXT_PUBLIC_WORDPRESS_URL=https://your-wordpress-site.com
💡 Tip: For local development, use your local WordPress URL like http://localhost:8080 or https://mysite.test

Installing the WP Headless Client

We'll use the wp-headless-client package for type-safe API calls:

npm install wp-headless-client

This lightweight package (only 7KB!) provides:

  • Full TypeScript support
  • Zero dependencies
  • All WordPress REST API endpoints
  • Pagination handling

Creating the WordPress Client

Create a new file for our API configuration:

mkdir -p src/lib
touch src/lib/wordpress.ts

Add the client setup:

// src/lib/wordpress.ts
import { createWordPressClient } from 'wp-headless-client';

const wordpressUrl = process.env.NEXT_PUBLIC_WORDPRESS_URL;

if (!wordpressUrl) {
  throw new Error('NEXT_PUBLIC_WORDPRESS_URL is not defined');
}

export const wp = createWordPressClient({
  baseUrl: wordpressUrl,
});

// Helper function to get API URL
export function getApiUrl(endpoint: string): string {
  return `${wordpressUrl}/wp-json/wp/v2${endpoint}`;
}

Testing the Connection

Let's verify our setup works. Update src/app/page.tsx:

// src/app/page.tsx
import { wp } from '@/lib/wordpress';

export default async function Home() {
  // Fetch recent posts
  const { data: posts } = await wp.posts().perPage(5).get();
  
  return (
    <main className="container mx-auto px-4 py-8">
      <h1 className="text-4xl font-bold mb-8">My Headless Blog</h1>
      
      <div className="space-y-4">
        {posts.map((post) => (
          <article key={post.id} className="border p-4 rounded">
            <h2 className="text-xl font-semibold">
              {post.title.rendered}
            </h2>
          </article>
        ))}
      </div>
    </main>
  );
}

Run the Development Server

Start your development server:

npm run dev

Open http://localhost:3000 in your browser.

✅ Success! If you see your WordPress post titles, the connection is working!

Troubleshooting

CORS Errors

If you see CORS errors, add this to your WordPress theme's functions.php:

// Enable CORS for your frontend
add_action('rest_api_init', function() {
    remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
    add_filter('rest_pre_serve_request', function($value) {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type, Authorization');
        return $value;
    });
}, 15);

Environment Variables Not Loading

Make sure you:

  1. Restarted the development server after adding .env.local
  2. Used NEXT_PUBLIC_ prefix for client-side variables

Summary

In this lesson, we:

  • ✅ Created a new Next.js project with TypeScript
  • ✅ Set up environment variables
  • ✅ Installed and configured the WordPress client
  • ✅ Verified the API connection

Next up: We'll create proper TypeScript types for our WordPress content and build a reusable data fetching layer.