Docs/Fetching Content

Fetching Content

Fetch posts, pages, and media from WordPress via the REST API.

Fetching WordPress Content

PhantomStudio connects to your WordPress site's REST API to fetch content. Your WordPress site serves as the CMS backend while Astro provides a fast, modern frontend.

How It Works

WordPress (Your CMS)Astro Site (PhantomStudio)
PostsREST APIStatic pages
Pages/wp-json/wp/v2/Blog
MediaSSR routes
Custom TypesFast delivery

WordPress remains your content management system: you write, edit, and manage content there. Your Astro site fetches this content through the WordPress REST API and renders it.

Connecting Your WordPress Site

Step 1: Enter Your Site URL

  1. Open Site ▾ › WordPress
  2. Enter your WordPress site URL (e.g., https://yourblog.com) in WordPress Site URL
  3. Click Fetch, then Save Settings

Step 2: Check What Was Found

When the site answers, the Content tab shows how many posts, pages, categories, tags, authors, media items and custom post type entries it found. See Connecting WordPress for the full dialog.

Step 3: Use Your Content

Once connected, you can:

  • See each post type and its fields in the Data tab, and add them to a page as cards (Data Sources)
  • Create ready-made blog, page and search templates with WordPress Tools (Code mode, the file tree's New menu)
  • Use the WordPress client library in your Astro components (below)

Using the WordPress Client

Saving the WordPress settings writes a src/lib/wordpress.ts client to your project:

// Fetch all posts
const posts = await getPosts();

// Fetch a single post by slug
const post = await getPost('my-post-slug');

// Fetch pages
const pages = await getPages();

// Fetch categories
const categories = await getCategories();

Example: Blog Index Page

---
import { getPosts } from '../lib/wordpress';
import Layout from '../layouts/Layout.astro';

const posts = await getPosts({ perPage: 10 });
---

<Layout title="Blog">
  <h1>Latest Posts</h1>
  <ul>
    {posts.map(post => (
      <li>
        <a href={`/blog/${post.slug}`}>
          {post.title.rendered}
        </a>
      </li>
    ))}
  </ul>
</Layout>

Example: Single Post Page

---
import { getPost, getAllPosts } from '../lib/wordpress';
import Layout from '../layouts/Layout.astro';

export async function getStaticPaths() {
  const posts = await getAllPosts();
  return posts.map(post => ({
    params: { slug: post.slug },
  }));
}

const { slug } = Astro.params;
const post = await getPost(slug);
---

<Layout title={post.title.rendered}>
  <article>
    <h1 set:html={post.title.rendered} />
    <div set:html={post.content.rendered} />
  </article>
</Layout>

What's Available via REST API

Posts

  • Title, content, excerpt
  • Publication date, modified date
  • Author information
  • Featured image (via _embed)
  • Categories and tags
  • Custom fields (meta)
  • SEO data (if using Yoast/Rank Math)

Pages

  • Title, content
  • Page template
  • Parent page
  • Menu order
  • Custom fields

Media

  • Images with all sizes
  • Alt text and captions
  • File metadata
  • Source URLs

Taxonomies

  • Categories
  • Tags
  • Custom taxonomies

Custom Post Types

  • Any registered post type with show_in_rest: true
  • WooCommerce products
  • Portfolio items
  • Events
  • etc.

The client requests posts and pages with _embed, so each one includes its featured image:

const posts = await getPosts({ perPage: 10 });
const post = posts[0];

// Access featured image
const featuredImage = post._embedded?.['wp:featuredmedia']?.[0];
if (featuredImage) {
  const imageUrl = featuredImage.source_url;
  const altText = featuredImage.alt_text;
}

The WordPress client includes helper functions:

import { getFeaturedImageUrl } from '../lib/wordpress';

const imageUrl = getFeaturedImageUrl(post, 'large');

SEO Data

If your WordPress site uses an SEO plugin, that data is available:

Yoast SEO

const seoData = post.yoast_head_json;
// { title, description, robots, og_title, og_image, schema, ... }

Rank Math

const title = post.rank_math_title;
const description = post.rank_math_description;

When Content Is Fetched

The same code runs at build time or on each request, depending on the page's rendering mode. Pick it per page; see Rendering.

ModeWhen WordPress is readNew WordPress content shows
StaticWhen the site is builtAfter the next deploy
DynamicOn every visitRight away
Dynamic, cachedOn demand, then reused for a whileWhen the cached copy expires

Dynamic modes need server-capable hosting (Vercel or Cloudflare Workers).

Rebuilding When Content Changes

Static pages show new WordPress content after the next deploy. To deploy when you publish in WordPress, add the snippet from Site ▾ › WordPress › Deployment to WordPress. It calls your project's deploy hook. To deploy by hand, use Publish ▾ › Deploy now. See Deploy Hooks.

Handling Private Content

For private posts or authenticated requests:

// In your WordPress client
const headers = {
  'Authorization': 'Basic ' + btoa('username:application_password')
};

Set up Application Passwords in WordPress (Users → Edit → Application Passwords).

Custom Post Types

Access any custom post type registered with REST API support:

// Fetch WooCommerce products
const products = await getCustomPostType('products');

// Fetch portfolio items
const portfolio = await getCustomPostType('portfolio');

Performance Tips

  1. Paginate large collections: don't fetch 1000 posts at once. getPosts takes page and perPage
  2. Fetch only what the page shows: a list page rarely needs every post's full content
  3. Use Static or Dynamic, cached for pages that don't need content to the second

Example paginated request:

const posts = await getPosts({
  page: 1,
  perPage: 10,
  orderBy: 'date',
  order: 'desc',
});

Troubleshooting

REST API Not Accessible

  1. Check your site URL is correct
  2. Ensure REST API isn't disabled by a plugin
  3. Verify permalinks are not set to "Plain"

CORS Errors

Builds and server code don't need CORS. You only see CORS errors when browser code calls WordPress directly. Fetch in the page's frontmatter or an API route instead, or allow your site's origin on WordPress, for example in .htaccess:

Header set Access-Control-Allow-Origin "https://www.example.com"

The client's getPosts, getPost, getPages and getPage already request _embed. If an image is still missing, check that the post has a featured image set in WordPress, and that the media item is public.

Next Steps