This repository has been archived on 2025-08-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
hugo-mistergeek/docs/hugo-wordpress-integration.md
2025-08-18 15:31:30 +02:00

11 KiB

Hugo WordPress Integration Guide

This guide provides step-by-step instructions for setting up a Hugo static site that fetches and displays blog articles from a WordPress API.

Project Overview

Your Hugo project will act as a static site generator that pulls content from a WordPress REST API and generates a fast, SEO-friendly blog. This approach combines the performance benefits of static sites with the content management capabilities of WordPress.

Prerequisites

  • Hugo installed (v0.110.0 or later)
  • WordPress site with REST API enabled
  • Basic knowledge of Go templates and Hugo
  • Node.js (for build scripts)

Step 1: Configure Hugo for WordPress API Integration

Update hugo.toml Configuration

Add the following configuration to your hugo.toml file:

# Base configuration
baseURL = "https://yourblog.com"
languageCode = "en-us"
title = "Your Blog Title"
theme = "your-theme"

# WordPress API Configuration
[params.wordpress]
  apiUrl = "https://your-wordpress-site.com/wp-json/wp/v2"
  postsPerPage = 10
  featuredImageSize = "large"
  authorEndpoint = "users"
  categoryEndpoint = "categories"
  tagEndpoint = "tags"

# Build configuration
[build]
  writeStats = true
  [[build.cachebusters]]
    source = "assets/.*\\.(js|ts|jsx|ts)$"
    target = "js"
  [[build.cachebusters]]
    source = "assets/.*\\.(css|sass|scss)$"
    target = "css"

Step 2: Create Data Fetching Scripts

Create API Fetching Script

Create a new file scripts/fetch-wordpress.js:

const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');

const WORDPRESS_API = 'https://www.mistergeek.net//wp-json/wp/v2';
const OUTPUT_DIR = path.join(__dirname, '..', 'data', 'wordpress');

async function fetchPosts(page = 1, perPage = 100) {
  const response = await fetch(`${WORDPRESS_API}/posts?page=${page}&per_page=${perPage}&_embed`);
  const posts = await response.json();
  
  if (response.headers.get('x-wp-totalpages') > page) {
    const nextPosts = await fetchPosts(page + 1, perPage);
    return [...posts, ...nextPosts];
  }
  
  return posts;
}

async function fetchCategories() {
  const response = await fetch(`${WORDPRESS_API}/categories?per_page=100`);
  return response.json();
}

async function fetchTags() {
  const response = await fetch(`${WORDPRESS_API}/tags?per_page=100`);
  return response.json();
}

async function fetchAuthors() {
  const response = await fetch(`${WORDPRESS_API}/users?per_page=100`);
  return response.json();
}

async function generateData() {
  // Ensure output directory exists
  if (!fs.existsSync(OUTPUT_DIR)) {
    fs.mkdirSync(OUTPUT_DIR, { recursive: true });
  }

  console.log('Fetching WordPress data...');
  
  const [posts, categories, tags, authors] = await Promise.all([
    fetchPosts(),
    fetchCategories(),
    fetchTags(),
    fetchAuthors()
  ]);

  // Save data as JSON files
  fs.writeFileSync(path.join(OUTPUT_DIR, 'posts.json'), JSON.stringify(posts, null, 2));
  fs.writeFileSync(path.join(OUTPUT_DIR, 'categories.json'), JSON.stringify(categories, null, 2));
  fs.writeFileSync(path.join(OUTPUT_DIR, 'tags.json'), JSON.stringify(tags, null, 2));
  fs.writeFileSync(path.join(OUTPUT_DIR, 'authors.json'), JSON.stringify(authors, null, 2));

  console.log(`✅ Fetched ${posts.length} posts, ${categories.length} categories, ${tags.length} tags, ${authors.length} authors`);
}

generateData().catch(console.error);

Create Package.json for Build Scripts

Create package.json:

{
  "name": "hugo-wordpress-blog",
  "version": "1.0.0",
  "description": "Hugo static site with WordPress content",
  "scripts": {
    "fetch-data": "node scripts/fetch-wordpress.js",
    "build": "npm run fetch-data && hugo --minify",
    "dev": "npm run fetch-data && hugo server -D"
  },
  "dependencies": {
    "node-fetch": "^3.3.2"
  }
}

Step 3: Create Hugo Templates for WordPress Content

Create Content Layout

Create layouts/_default/single.html:

{{ define "main" }}
<article class="post">
  <header class="post-header">
    <h1 class="post-title">{{ .Title }}</h1>
    <div class="post-meta">
      <time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time>
      {{ if .Params.author }}
        <span class="post-author">{{ .Params.author.name }}</span>
      {{ end }}
      {{ if .Params.categories }}
        <div class="post-categories">
          {{ range .Params.categories }}
            <a href="/categories/{{ .slug }}" class="category">{{ .name }}</a>
          {{ end }}
        </div>
      {{ end }}
    </div>
    {{ if .Params.featured_image }}
      <img src="{{ .Params.featured_image }}" alt="{{ .Title }}" class="featured-image">
    {{ end }}
  </header>

  <div class="post-content">
    {{ .Content }}
  </div>

  {{ if .Params.tags }}
    <div class="post-tags">
      {{ range .Params.tags }}
        <a href="/tags/{{ .slug }}" class="tag">#{{ .name }}</a>
      {{ end }}
    </div>
  {{ end }}
</article>
{{ end }}

Create List Layout

Create layouts/_default/list.html:

{{ define "main" }}
<div class="posts-list">
  <h1>{{ .Title }}</h1>
  
  {{ range .Pages }}
  <article class="post-preview">
    <h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
    <div class="post-meta">
      <time>{{ .Date.Format "Jan 2, 2006" }}</time>
      {{ if .Params.author }}
        <span>{{ .Params.author.name }}</span>
      {{ end }}
    </div>
    {{ if .Params.excerpt }}
      <p class="post-excerpt">{{ .Params.excerpt }}</p>
    {{ end }}
    <a href="{{ .RelPermalink }}" class="read-more">Read more →</a>
  </article>
  {{ end }}
</div>
{{ end }}

Step 4: Create Hugo Content from WordPress Data

Create Content Generator Script

Create scripts/generate-content.js:

const fs = require('fs');
const path = require('path');

const DATA_DIR = path.join(__dirname, '..', 'data', 'wordpress');
const CONTENT_DIR = path.join(__dirname, '..', 'content', 'posts');

function generateContent() {
  const posts = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'posts.json'), 'utf8'));
  
  // Ensure content directory exists
  if (!fs.existsSync(CONTENT_DIR)) {
    fs.mkdirSync(CONTENT_DIR, { recursive: true });
  }

  posts.forEach(post => {
    const slug = post.slug;
    const date = new Date(post.date);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    
    const contentDir = path.join(CONTENT_DIR, `${year}-${month}-${slug}`);
    
    if (!fs.existsSync(contentDir)) {
      fs.mkdirSync(contentDir, { recursive: true });
    }

    const frontmatter = {
      title: post.title.rendered,
      date: post.date,
      draft: post.status !== 'publish',
      slug: slug,
      wordpress_id: post.id,
      excerpt: post.excerpt.rendered.replace(/<[^>]*>/g, ''),
      featured_image: post._embedded?.['wp:featuredmedia']?.[0]?.source_url || '',
      author: {
        id: post.author,
        name: post._embedded?.author?.[0]?.name || 'Unknown'
      },
      categories: post._embedded?.['wp:term']?.[0]?.map(cat => ({
        id: cat.id,
        name: cat.name,
        slug: cat.slug
      })) || [],
      tags: post._embedded?.['wp:term']?.[1]?.map(tag => ({
        id: tag.id,
        name: tag.name,
        slug: tag.slug
      })) || []
    };

    const content = `---
${Object.entries(frontmatter)
  .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
  .join('\n')}
---

${post.content.rendered}`;

    fs.writeFileSync(path.join(contentDir, 'index.md'), content);
  });

  console.log(`✅ Generated ${posts.length} content files`);
}

generateContent();

Step 5: Update Build Process

Create Build Script

Update your package.json build scripts:

{
  "scripts": {
    "fetch-data": "node scripts/fetch-wordpress.js",
    "generate-content": "node scripts/generate-content.js",
    "prebuild": "npm run fetch-data && npm run generate-content",
    "build": "hugo --minify",
    "dev": "npm run fetch-data && npm run generate-content && hugo server -D",
    "clean": "rm -rf data/wordpress content/posts public"
  }
}

Step 6: Configure Netlify for Automated Builds

Create netlify.toml:

[build]
  command = "npm run build"
  publish = "public"

[build.environment]
  HUGO_VERSION = "0.110.0"
  NODE_VERSION = "18"

[[plugins]]
  package = "@netlify/plugin-sitemap"

[[plugins]]
  package = "netlify-plugin-hugo-cache-resources"

Step 7: Advanced Features

Add Search Functionality

Create layouts/partials/search.html:

<div class="search-container">
  <input type="search" id="search" placeholder="Search articles..." aria-label="Search">
  <div id="search-results" class="search-results"></div>
</div>

<script>
// Add client-side search using Fuse.js
</script>

Add RSS Feed

Create layouts/index.rss.xml:

<rss version="2.0">
  <channel>
    <title>{{ .Site.Title }}</title>
    <link>{{ .Site.BaseURL }}</link>
    <description>{{ .Site.Params.description }}</description>
    <language>{{ .Site.LanguageCode }}</language>
    {{ range first 15 .Pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <description>{{ .Params.excerpt }}</description>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</pubDate>
      <guid>{{ .Permalink }}</guid>
    </item>
    {{ end }}
  </channel>
</rss>

Deployment Options

Netlify

  1. Connect your GitHub repository
  2. Set build command: npm run build
  3. Set publish directory: public

Vercel

  1. Install Vercel CLI: npm i -g vercel
  2. Run: vercel --prod

GitHub Pages

  1. Create .github/workflows/deploy.yml
  2. Use GitHub Actions for automated deployment

Performance Optimization

  1. Image Optimization: Use Hugo's image processing
  2. CDN: Configure Cloudflare or similar
  3. Caching: Implement proper cache headers
  4. Minification: Enable HTML/CSS/JS minification
  5. Preloading: Add resource hints for critical assets

Next Steps

  1. Run npm install to install dependencies
  2. Run npm run dev to start development server
  3. Customize your theme and styling
  4. Set up automated deployment
  5. Configure SEO meta tags
  6. Add analytics (Google Analytics, Plausible, etc.)

Troubleshooting

  • API Rate Limits: Implement caching and rate limiting
  • Image Loading: Ensure WordPress images are accessible
  • CORS Issues: Configure WordPress CORS headers
  • Build Time: Consider incremental builds for large sites