ForgeWP

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:

src/components/PostHero.tsx
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:

Flowchart
Rendering diagram...
  1. In Local Development (pnpm dev): Hooks read mock post data directly from cms/mock-data.ts through the React WpPostContext. When you edit mock posts, Vite updates the UI instantly via Hot Module Replacement (HMR).
  2. 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.).
  3. 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:

Importing Core Data Hooks
1import {
2  useWpTitle,
3  useWpContent,
4  useWpExcerpt,
5  useWpPermalink,
6  useWpDate,
7  useWpModifiedDate,
8  useWpAuthor,
9  useWpFeaturedImage,
10} from "@forgewp/react"
Table · 4 columns
Hook
Return Type
Dev Mock Source
Compiled WordPress Output
useWpTitle()
string
post.title
<?php echo html_entity_decode( get_the_title(), ENT_QUOTES | ENT_HTML5, 'UTF-8' ); ?>
useWpContent()
string
post.content
<?php the_content(); ?>
useWpExcerpt()
string
post.excerpt
<?php the_excerpt(); ?>
useWpPermalink()
string
post.permalink
<?php the_permalink(); ?>
useWpDate()
string
post.date
<?php echo esc_html( get_the_date() ); ?>
useWpModifiedDate()
string
post.modified
<?php echo esc_html( get_the_modified_date() ); ?>
useWpAuthor()
string
post.author
<?php echo esc_html( get_the_author() ); ?>
useWpFeaturedImage()
string
post.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.

src/app/single.tsx
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:

src/app/single.tsx
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:

src/components/PostCard.tsx
1const excerpt = useWpExcerpt()
2return <p className="line-clamp-3 text-sm text-muted-foreground">{excerpt}</p>

Returns the canonical URL to the post.

  • In local development, it generates a clean route based on post type and ID (e.g. /post/1 or /project/42).
  • In production, it compiles to WordPress the_permalink(), respecting the permalink structure configured in Settings → Permalinks (e.g. /%year%/%monthnum%/%postname%/).
src/components/PostCard.tsx
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:
src/components/ArticleMeta.tsx
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:

src/app/page.tsx
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 via pnpm 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:

  1. Context Boundary: Post hooks (useWpTitle, useWpExcerpt, etc.) read from WpPostContext. 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.
  2. 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:

Loops, Custom Post Types & Queries