Single Post Primitives & Core Hooks
How to consume WordPress post titles, content, excerpts, featured images, dates, and authors in React.
In traditional WordPress themes, fetching post data requires memorizing procedural PHP template tags such as the_title(), get_the_date(), or the_post_thumbnail_url(). In headless architectures, you often fetch complex GraphQL or REST payloads, creating network waterfalls and latency.
ForgeWP introduces an isomorphic data hook model via the @forgewp/react package. You write standard React hooks to access post properties:
1import {
2 useWpTitle,
3 useWpDate,
4 useWpAuthor,
5 useWpFeaturedImage,
6} from "@forgewp/react"
7
8export function PostHero() {
9 const title = useWpTitle()
10 const date = useWpDate()
11 const author = useWpAuthor()
12 const featuredImage = useWpFeaturedImage()
13
14 return (
15 <header className="relative overflow-hidden rounded-3xl border border-border bg-card p-8 md:p-12">
16 <img
17 src={featuredImage}
18 alt={title}
19 className="absolute inset-0 h-full w-full object-cover opacity-25"
20 />
21 <div className="relative z-10 max-w-3xl">
22 <div className="flex items-center gap-3 text-xs font-semibold tracking-wider text-muted-foreground uppercase">
23 <span>{date}</span>
24 <span>•</span>
25 <span>By {author}</span>
26 </div>
27 <h1 className="mt-4 font-heading text-3xl font-black tracking-tight text-foreground sm:text-5xl">
28 {title}
29 </h1>
30 </div>
31 </header>
32 )
33}How Data Hooks Work (The Isomorphic Bridge)
ForgeWP's hooks execute differently in development and production to deliver the best possible developer and visitor experience:
- In Local Development (
pnpm dev): Hooks read mock post data directly fromcms/mock-data.tsthrough the ReactWpPostContext. When you edit mock posts, Vite updates the UI instantly via Hot Module Replacement (HMR). - At Compile & Export (
pnpm export): The compiler performs an SSR pre-render pass, replacing hook invocations with high-performance PHP calls (the_title(),the_permalink(), etc.). - In Live WordPress: Generated PHP renders recognized post fields on the server. A static view with no interactive features does not need a React island bundle; other theme or plugin scripts may still load.
How does `useWpTitle()` know which post to read?
In WordPress, templates operate within a global "current post" context (The Loop). ForgeWP recreates this in React using WpPostContext. When you render a single post template (single.tsx) or iterate through a <WpQueryLoop>, the active post is automatically provided to every hook called inside that component tree—without needing to manually pass props!
Core Post Hooks Reference
All core data hooks are imported directly from @forgewp/react:
1import {
2 useWpTitle,
3 useWpContent,
4 useWpExcerpt,
5 useWpPermalink,
6 useWpDate,
7 useWpModifiedDate,
8 useWpAuthor,
9 useWpFeaturedImage,
10} from "@forgewp/react"Hook Return Type Dev Mock Source Compiled WordPress Output useWpTitle()stringpost.title<?php echo html_entity_decode( get_the_title(), ENT_QUOTES | ENT_HTML5, 'UTF-8' ); ?>useWpContent()stringpost.content<?php the_content(); ?>useWpExcerpt()stringpost.excerpt<?php the_excerpt(); ?>useWpPermalink()stringpost.permalink<?php the_permalink(); ?>useWpDate()stringpost.date<?php echo esc_html( get_the_date() ); ?>useWpModifiedDate()stringpost.modified<?php echo esc_html( get_the_modified_date() ); ?>useWpAuthor()stringpost.author<?php echo esc_html( get_the_author() ); ?>useWpFeaturedImage()stringpost.featuredImage<?php echo esc_url( get_the_post_thumbnail_url( null, 'large' ) ); ?> |
|---|
Deep Dive: Core Hook Behaviors
1. useWpTitle()
Returns the post or page title as a clean string. In compiled output, ForgeWP automatically applies HTML entity decoding so symbols like ampersands (&), quotes ("), and em-dashes (—) display cleanly without raw HTML character codes.
1const title = useWpTitle()
2return <h1 className="text-4xl font-bold">{title}</h1>2. useWpContent()
Returns the raw HTML body produced by WordPress and Gutenberg block editors. Because post content contains rich HTML (paragraphs, images, columns, embeds), inject it into your layout using React's dangerouslySetInnerHTML:
1const content = useWpContent()
2
3return (
4 <article
5 className="forgewp-content-width prose prose-slate dark:prose-invert max-w-none"
6 dangerouslySetInnerHTML={{ __html: content }}
7 />
8)3. useWpExcerpt()
Returns the post excerpt. If a post author authored an explicit excerpt in the WordPress post sidebar, it returns that excerpt. Otherwise, WordPress automatically generates a truncated snippet from the first 55 words of the post body:
1const excerpt = useWpExcerpt()
2return <p className="line-clamp-3 text-sm text-muted-foreground">{excerpt}</p>4. useWpPermalink()
Returns the canonical URL to the post.
- In local development, it generates a clean route based on post type and ID (e.g.
/post/1or/project/42). - In production, it compiles to WordPress
the_permalink(), respecting the permalink structure configured in Settings → Permalinks (e.g./%year%/%monthnum%/%postname%/).
1const permalink = useWpPermalink()
2return (
3 <a href={permalink} className="text-brand font-semibold hover:underline">
4 Read Post →
5 </a>
6)5. useWpDate() and useWpModifiedDate()
Returns the publication date formatted according to the date format configured in your WordPress admin settings (Settings → General → Date Format).
useWpDate()returns the original published date.useWpModifiedDate()returns the timestamp when the post was last updated, perfect for articles with "Last updated" timestamps:
1const published = useWpDate()
2const updated = useWpModifiedDate()
3
4return (
5 <div className="text-xs text-muted-foreground">
6 <span>Published: {published}</span>
7 {updated !== published && (
8 <span className="ml-2">(Updated: {updated})</span>
9 )}
10 </div>
11)6. useWpFeaturedImage()
Returns the URL of the post's featured thumbnail.
- If a featured image exists, it compiles to
get_the_post_thumbnail_url(null, 'large'). - If no featured image was uploaded for the post, it falls back to a graceful placeholder in development rather than crashing your component.
The Bounded Editorial Slot: <BlockArea />
In addition to useWpContent(), @forgewp/react exposes the <BlockArea /> component:
1import { BlockArea } from "@forgewp/react"
2
3export default function CustomLandingPage() {
4 return (
5 <main>
6 {/* 1. Custom Hero coded in React */}
7 <section className="bg-brand text-brand-foreground py-20 text-center">
8 <h1 className="text-5xl font-black">Welcome to Our Platform</h1>
9 </section>
10
11 {/* 2. Editorial Gutenberg Slot where editors can insert blocks */}
12 <section className="forgewp-content-width py-12">
13 <BlockArea name="editorial-content" />
14 </section>
15 </main>
16 )
17}Why Use <BlockArea />?
- In Local Development: Renders mock content if present, or displays an informative visual slot badge in your dev browser (
⚡ Gutenberg Block Area — editorial-content) so designers and frontend developers see exactly where CMS blocks will appear. - In Production: Compiles cleanly to
<?php the_content(); ?>. - Custom Block Compatibility: Any custom Gutenberg block you create in
src/blocks/(scaffolded viapnpm forgewp make:block) can be inserted into these designated areas by site editors in WordPress. To learn how to author custom blocks, see Create A Gutenberg Block. For styling Gutenberg block output with Tailwind typography, see Styling Gutenberg Content & Editor Parity.
When should I use `<BlockArea />` instead of `useWpContent()`?
Use <BlockArea /> whenever you are rendering full post or page body content—it provides visual placeholders in local dev and handles Gutenberg block rendering automatically. Use useWpContent() when you need programmatic access to the HTML string itself, such as calculating estimated reading time or stripping tags for custom excerpt generation.
Rules of Data Hooks
Because ForgeWP data hooks are standard React hooks, follow these two essential architectural rules:
- Context Boundary: Post hooks (
useWpTitle,useWpExcerpt, etc.) read fromWpPostContext. They must be called inside:- A single post template (
src/app/single.tsx,src/app/page.tsx). - Inside a query loop (
<WpQueryLoop>...</WpQueryLoop>). Calling them outside of post context will yield default fallback strings.
- A single post template (
- Standard React Rules of Hooks: Never call data hooks inside JavaScript conditionals (
if), loops (for), or nested callbacks. Always invoke them at the top level of your component function.
Next Step: Querying Lists & Custom Post Types
Now that you know how to extract data from a single post, discover how to query lists of posts, filter custom post types, and handle pagination in the next guide: