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-blogWhen 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? → NoNavigate into your project:
cd my-headless-blogProject 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.jsSetting Up Environment Variables
Create a .env.local file in your project root:
touch .env.localAdd your WordPress site URL:
# .env.local
NEXT_PUBLIC_WORDPRESS_URL=https://your-wordpress-site.comhttp://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-clientThis 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.tsAdd 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 devOpen http://localhost:3000 in your browser.
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:
- Restarted the development server after adding
.env.local - 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.