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) | |
|---|---|---|
| Posts | REST API | Static pages |
| Pages | /wp-json/wp/v2/ | Blog |
| Media | SSR routes | |
| Custom Types | Fast 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
- Open Site ▾ › WordPress
- Enter your WordPress site URL (e.g.,
https://yourblog.com) in WordPress Site URL - 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.
Handling Featured Images
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.
| Mode | When WordPress is read | New WordPress content shows |
|---|---|---|
| Static | When the site is built | After the next deploy |
| Dynamic | On every visit | Right away |
| Dynamic, cached | On demand, then reused for a while | When 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
- Paginate large collections: don't fetch 1000 posts at once.
getPoststakespageandperPage - Fetch only what the page shows: a list page rarely needs every post's full content
- 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
- Check your site URL is correct
- Ensure REST API isn't disabled by a plugin
- 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"
Missing Featured Images
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
- Connecting WordPress: configure your connection
- Browsing Data: browse content and copy snippets
- Deploying to Vercel: deploy your headless site