The WpImage Primitive & Responsive Media
How to use <WpImage> for responsive featured images, static assets with src, attachment resolution, and native wp_get_attachment_image compilation.
Images in a WordPress theme serve two fundamentally different roles:
- Content Images (Dynamic CMS Media): Blog post featured images, portfolio galleries, author avatars, and product photography uploaded by content editors via the WordPress Media Library.
- Design Assets (Static Theme Media): Brand logos, UI illustrations, background textures, and placeholder graphics bundled directly inside your theme codebase.
In traditional WordPress PHP themes, rendering an image responsibly requires nesting complex functions (wp_get_attachment_image, wp_get_attachment_image_src, wp_calculate_image_srcset, wp_calculate_image_sizes). In pure React applications, developers often use basic <img /> tags that fail to integrate with WordPress's media database.
ForgeWP solves this with <WpImage> from @forgewp/react. It provides an isomorphic, type-safe React component in development that compiles to high-performance WordPress wp_get_attachment_image() calls or get_theme_file_uri() in production.
1. How <WpImage> Works Across Environments
The following diagram illustrates how <WpImage> bridges local Vite development with native WordPress server rendering:
- In Local Development (
pnpm dev):<WpImage>reads image mock data fromcms/mock-data.tsthrough the ReactWpPostContext. If an image is missing or you use a random seed, it automatically displays a responsive placeholder so you can style your layout immediately. - At Compile & Export (
pnpm export): The compiler analyzes<WpImage>attributes and transforms the tag into native WordPress PHP (wp_get_attachment_imageorget_theme_file_uri). - On the Live WordPress Server: For a Media Library attachment, the generated PHP calls WordPress image functions, which use the site's registered image sizes and responsive attributes. Static theme assets follow a separate path.
2. Quick Decision Guide: Which Prop to Use?
Before diving into details, use this quick reference matrix to choose the right <WpImage> prop for your media:
I want to display... Prop to Use Code Example Current post's featured image field="featuredImage"<WpImage field="featuredImage" size="large" />ACF or custom field image field="meta_key"<WpImage field="author_photo" size="medium" />Specific WordPress Media ID id={attachmentId}<WpImage id={142} size="full" />Static image from src/assets/src={importedGraphic}<WpImage src={heroGraphic} alt="Hero" />Public static file from public/src="/assets/..."<WpImage src="/assets/logo.svg" width={180} height={40} />Remote CDN or Unsplash URL src="https://..."<WpImage src="https://images.unsplash.com/..." /> |
|---|
3. Rendering Post Featured Images (field="featuredImage")
The most common use case for <WpImage> is rendering the featured image (post thumbnail) of an article or custom post type.
Basic Implementation
1import { WpImage, useWpTitle, useWpExcerpt } from "@forgewp/react"
2
3export function PostCard() {
4 const title = useWpTitle()
5 const excerpt = useWpExcerpt()
6
7 return (
8 <article className="overflow-hidden rounded-2xl border border-border bg-card shadow-sm transition hover:shadow-md">
9 {/* Featured image with large thumbnail size */}
10 <WpImage
11 field="featuredImage"
12 size="large"
13 className="aspect-video w-full object-cover"
14 alt={title}
15 />
16
17 <div className="p-6">
18 <h3 className="mb-2 text-xl font-bold text-foreground">{title}</h3>
19 <p className="text-sm leading-relaxed text-muted-foreground">
20 {excerpt}
21 </p>
22 </div>
23 </article>
24 )
25}What the Compiler Outputs in PHP
When you export your theme, the JSX above compiles directly to:
1<?php echo wp_get_attachment_image(
2 get_post_thumbnail_id( get_the_ID() ),
3 'large',
4 false,
5 array(
6 'class' => 'w-full h-56 object-cover',
7 'alt' => esc_attr( get_the_title() ),
8 )
9); ?>Notice that:
- It calls WordPress's native
wp_get_attachment_image(). - WordPress automatically populates the
srcsetattribute with all image dimensions generated upon upload (thumbnail,medium,medium_large,large). - It preserves your Tailwind CSS classes in the output HTML.
3. Rendering Static & Local Assets with src
Beyond dynamic CMS uploads, you will frequently need to render static theme graphics: brand logos, decorative backgrounds, hero vectors, or bundled illustrations.
<WpImage> supports the src prop in three flexible ways:
Pattern A: Static Asset Import (Vite-Hashed)
When you import an image file directly from src/assets/, Vite treats it as a static asset module:
1import { WpImage } from "@forgewp/react"
2import heroPattern from "@/assets/hero-pattern.png"
3
4export function HeroSection() {
5 return (
6 <section className="relative overflow-hidden bg-background py-24">
7 {/* Vite automatically provides intrinsic width, height, and hashed src */}
8 <WpImage
9 src={heroPattern}
10 alt="Decorative background pattern"
11 className="absolute inset-0 h-full w-full object-cover opacity-15"
12 />
13 <div className="relative z-10 mx-auto max-w-4xl text-center">
14 <h1 className="text-5xl font-black text-foreground">
15 Next-Gen Architecture
16 </h1>
17 </div>
18 </section>
19 )
20}Pattern B: Absolute Path from public/
For static files stored in your project's public/ directory (e.g. public/assets/logo.svg):
1<WpImage
2 src="/assets/logo.svg"
3 width={180}
4 height={40}
5 alt="ForgeWP Company Logo"
6 className="h-10 w-auto"
7/>Pattern C: External CDN or Remote URLs
You can pass any fully qualified remote URL (https://...):
1<WpImage
2 src="https://images.unsplash.com/photo-example"
3 width={800}
4 height={600}
5 alt="Sample Unsplash Photo"
6 className="rounded-xl shadow"
7/>4. How the Compiler Identifies Images (Demystifying the "Magic")
Developers often ask: How does ForgeWP know whether to compile <WpImage> into a WordPress PHP function or a static HTML tag?
There is no black magic. The compilation pipeline operates in two clean, deterministic stages:
Stage 1: Server-Side Pre-Rendering (Intermediate Custom Element)
During pnpm export, ForgeWP evaluates your React components in a Node.js SSR context with _forgeWpCompileTime = true.
Instead of emitting a normal <img> tag, <WpImage> renders a temporary custom XML marker:
1<forgewp-image
2 data-field="featuredImage"
3 data-size="large"
4 data-class-name="w-full h-56 object-cover"
5 data-alt="Article Title"
6></forgewp-image>If you used src="/assets/logo.svg", it produces:
1<forgewp-image
2 data-src="/assets/logo.svg"
3 data-width="180"
4 data-height="40"
5 data-alt="ForgeWP Company Logo"
6></forgewp-image>Stage 2: The Markup Processor's 5-Branch Evaluation
Next, ForgeWP's compiler engine (markup-processor.js) parses all <forgewp-image> tags and executes a deterministic branching rule:
Tag Attributes Compiler Logic Emitted Output in PHP Template id={142} or src={142}Numeric Attachment ID <?php echo wp_get_attachment_image( 142, 'large', false, ... ); ?>field="featuredImage"Current Post Thumbnail <?php echo wp_get_attachment_image( get_post_thumbnail_id( get_the_ID() ), 'large', false, ... ); ?>field="author_photo"Custom Meta Key PHP conditional checking if meta is an attachment ID or image URL string src="/assets/logo.svg"Local Theme File <img src="<?php echo esc_url( get_theme_file_uri( 'assets/logo.svg' ) ); ?>" ... />src="https://..."External Web URL <img src="https://..." class="..." alt="..." ... /> |
|---|
5. WordPress Image Sub-Sizes (size)
WordPress automatically generates multiple cropped versions of every uploaded image. You can specify which registered image size <WpImage> should request using the size prop:
Prop Value Default WordPress Dimensions Typical Theme Use Case size="thumbnail"150 × 150 px (Square crop) Compact sidebar lists, user avatars, comment threads size="medium"Max 300 × 300 px Mobile card previews, grid thumbnails size="large"Max 1024 × 1024 px Post featured headers, standard card grids size="full"Original uploaded dimensions Full-width hero banners, lightbox zoom views size="custom-size"Custom registered size Any size declared via add_image_size() |
|---|
1<WpImage
2 field="featuredImage"
3 size="full"
4 priority={true}
5 className="max-h-150 w-full rounded-3xl object-cover"
6/>6. Custom Field & ACF Image Attachments
Themes frequently display secondary images attached to posts via custom fields or Advanced Custom Fields (ACF)—such as author portraits, product galleries, or sponsor logos.
Pass the meta key name to the field prop:
1import { WpImage, useWpAuthor, useWpCustomField } from "@forgewp/react"
2
3export function AuthorBio() {
4 const author = useWpAuthor()
5 const bio = useWpCustomField("author_bio", "")
6
7 return (
8 <aside className="flex items-center gap-4 rounded-xl border border-border bg-card p-6">
9 {/* Custom field containing attachment ID or image URL */}
10 <WpImage
11 field="author_profile_photo"
12 size="thumbnail"
13 className="h-16 w-16 rounded-full border-2 border-primary object-cover"
14 alt={author}
15 />
16
17 <div>
18 <h4 className="font-bold text-foreground">{author}</h4>
19 <p className="text-xs text-muted-foreground">{bio}</p>
20 </div>
21 </aside>
22 )
23}Smart Isomorphic Resolution for Custom Fields
In WordPress, custom fields can store images in two ways:
- As an Attachment ID (integer, e.g.
142): The compiler outputs a PHP check that callswp_get_attachment_image(142, 'thumbnail', ...)with full responsivesrcset. - As a Direct URL string (
https://...): If the field stores a raw URL string, the compiler falls back to<img src="..." />with escaped attributes.
7. Performance Optimization: priority & Core Web Vitals
Largest Contentful Paint (LCP) is one of Google's most critical Core Web Vitals metrics. Browsers penalize sites that lazy-load above-the-fold hero images.
ForgeWP provides the priority prop:
1<WpImage
2 field="featuredImage"
3 size="full"
4 priority={true}
5 alt="Featured Hero Story"
6 className="h-96 w-full object-cover"
7/>When priority={true} is set:
fetchpriority="high"is added to prioritize network downloading in Chromium browsers.loading="eager"is applied (disabling native lazy loading).- The compiler may also add a preload for a statically resolved image source. For dynamic WordPress attachment fields, inspect the generated output to check preload behavior.
For standard below-the-fold content images, omit priority (or specify loading="lazy"). The component defaults to lazy loading and asynchronous decoding.
8. Complete Props Reference for <WpImage>
Prop Type Default Description fieldstringundefinedPost meta key (e.g. "thumbnail", "featuredImage", "banner_img").idnumberundefinedSpecific WordPress Media Library attachment ID. srcstring | StaticImageData | numberundefinedLocal asset path, external URL, or static import object. sizestring"full"Registered WordPress image size ( "thumbnail", "medium", "large", "full").altstring""Image description for accessibility and SEO. widthnumber | stringundefinedIntrinsic image width in pixels. heightnumber | stringundefinedIntrinsic image height in pixels. prioritybooleanfalseWhen true, enables eager loading, fetchpriority="high", and head preloading.loading"lazy" | "eager""lazy" (or "eager" if priority)Browser loading strategy. decoding"async" | "sync" | "auto""async"Image decoding strategy. sizesstringundefinedCSS media condition sizes string (e.g. "(max-width: 768px) 100vw, 50vw").placeholder"empty" | "blur""empty"Blur-up placeholder mode while image loads. blurDataURLstringundefinedBase64-encoded micro-blur image string. |
|---|
Common Questions & FAQs
Can I use a standard HTML `<img />` tag instead of `<WpImage />`?
Yes, you can use regular <img src="..." /> tags for hardcoded static SVGs or third-party CDN images. However, for any content connected to WordPress (featured images, user uploads, custom fields), always use <WpImage>. Standard <img> tags cannot look up WordPress attachment IDs or generate responsive srcset variations.
How do I provide mock featured images during local development?
In cms/mock-data.ts, define featuredImage: "/assets/hero.jpg" on a mock post. Use <WpImage field="featuredImage" /> to read that property in local preview and compile it to WordPress's featured-image output.
Next Step: Local Assets, SVGs & Dynamic Icons
Proceed to Local Assets, SVGs & Dynamic Icons to learn how to bundle theme assets from public/ and src/assets/, render vector graphics, and use the tree-shaken <WpIcon> primitive!